1 /*-
2 * Copyright (c) 1997 by
3 * David L. Nugent <[email protected]>
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, is permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice immediately at the beginning of the file, without modification,
11 * this list of conditions, and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. This work was done expressly for inclusion into FreeBSD. Other use
16 * is permitted provided this notation is included.
17 * 4. Absolutely no warranty of function or purpose is made by the authors.
18 * 5. Modifications may be freely made to this file providing the above
19 * conditions are met.
20 *
21 * Display/change(+runprogram)/eval resource limits.
22 */
23
24 #include <sys/cdefs.h>
25 __FBSDID("$FreeBSD$");
26
27 #include <err.h>
28 #include <stdio.h>
29 #include <string.h>
30 #include <sys/types.h>
31 #include <sys/stat.h>
32 #include <sys/sysctl.h>
33 #include <sys/user.h>
34 #include <sys/param.h>
35 #include <stdlib.h>
36 #include <unistd.h>
37 #include <stdarg.h>
38 #include <stdint.h>
39 #include <ctype.h>
40 #include <errno.h>
41 #include <pwd.h>
42 #include <login_cap.h>
43 #include <sys/time.h>
44 #include <sys/resource.h>
45
46 enum
47 {
48 SH_NONE,
49 SH_SH, /* sh */
50 SH_CSH, /* csh */
51 SH_BASH, /* gnu bash */
52 SH_TCSH, /* tcsh */
53 SH_KSH, /* (pd)ksh */
54 SH_ZSH, /* zsh */
55 SH_RC, /* rc or es */
56 SH_NUMBER
57 };
58
59
60 /* eval emitter for popular shells.
61 * Why aren't there any standards here? Most shells support either
62 * the csh 'limit' or sh 'ulimit' command, but each varies just
63 * enough that they aren't very compatible from one to the other.
64 */
65 static struct {
66 const char * name; /* Name of shell */
67 const char * inf; /* Name used for 'unlimited' resource */
68 const char * cmd; /* Intro text */
69 const char * hard; /* Hard limit text */
70 const char * soft; /* Soft limit text */
71 const char * both; /* Hard+Soft limit text */
72 struct {
73 const char * pfx;
74 const char * sfx;
75 int divisor;
76 } lprm[RLIM_NLIMITS];
77 } shellparm[] =
78 {
79 { "", "infinity", "Resource limits%s%s:\n", "-max", "-cur", "",
80 {
81 { " cputime%-4s %8s", " secs\n", 1 },
82 { " filesize%-4s %8s", " kB\n", 1024 },
83 { " datasize%-4s %8s", " kB\n", 1024 },
84 { " stacksize%-4s %8s", " kB\n", 1024 },
85 { " coredumpsize%-4s %8s", " kB\n", 1024 },
86 { " memoryuse%-4s %8s", " kB\n", 1024 },
87 { " memorylocked%-4s %8s", " kB\n", 1024 },
88 { " maxprocesses%-4s %8s", "\n", 1 },
89 { " openfiles%-4s %8s", "\n", 1 },
90 { " sbsize%-4s %8s", " bytes\n", 1 },
91 { " vmemoryuse%-4s %8s", " kB\n", 1024 },
92 { " pseudo-terminals%-4s %8s", "\n", 1 },
93 { " swapuse%-4s %8s", " kB\n", 1024 },
94 { " kqueues%-4s %8s", "\n", 1 },
95 { " umtxp%-4s %8s", "\n", 1 },
96 }
97 },
98 { "sh", "unlimited", "", " -H", " -S", "",
99 {
100 { "ulimit%s -t %s", ";\n", 1 },
101 { "ulimit%s -f %s", ";\n", 512 },
102 { "ulimit%s -d %s", ";\n", 1024 },
103 { "ulimit%s -s %s", ";\n", 1024 },
104 { "ulimit%s -c %s", ";\n", 512 },
105 { "ulimit%s -m %s", ";\n", 1024 },
106 { "ulimit%s -l %s", ";\n", 1024 },
107 { "ulimit%s -u %s", ";\n", 1 },
108 { "ulimit%s -n %s", ";\n", 1 },
109 { "ulimit%s -b %s", ";\n", 1 },
110 { "ulimit%s -v %s", ";\n", 1024 },
111 { "ulimit%s -p %s", ";\n", 1 },
112 { "ulimit%s -w %s", ";\n", 1024 },
113 { "ulimit%s -k %s", ";\n", 1 },
114 { "ulimit%s -o %s", ";\n", 1 },
115 }
116 },
117 { "csh", "unlimited", "", " -h", "", NULL,
118 {
119 { "limit%s cputime %s", ";\n", 1 },
120 { "limit%s filesize %s", ";\n", 1024 },
121 { "limit%s datasize %s", ";\n", 1024 },
122 { "limit%s stacksize %s", ";\n", 1024 },
123 { "limit%s coredumpsize %s", ";\n", 1024 },
124 { "limit%s memoryuse %s", ";\n", 1024 },
125 { "limit%s memorylocked %s", ";\n", 1024 },
126 { "limit%s maxproc %s", ";\n", 1 },
127 { "limit%s openfiles %s", ";\n", 1 },
128 { "limit%s sbsize %s", ";\n", 1 },
129 { "limit%s vmemoryuse %s", ";\n", 1024 },
130 { "limit%s pseudoterminals %s", ";\n", 1 },
131 { "limit%s swapsize %s", ";\n", 1024 },
132 { "limit%s kqueues %s", ";\n", 1 },
133 { "limit%s umtxp %s", ";\n", 1 },
134 }
135 },
136 { "bash|bash2", "unlimited", "", " -H", " -S", "",
137 {
138 { "ulimit%s -t %s", ";\n", 1 },
139 { "ulimit%s -f %s", ";\n", 1024 },
140 { "ulimit%s -d %s", ";\n", 1024 },
141 { "ulimit%s -s %s", ";\n", 1024 },
142 { "ulimit%s -c %s", ";\n", 1024 },
143 { "ulimit%s -m %s", ";\n", 1024 },
144 { "ulimit%s -l %s", ";\n", 1024 },
145 { "ulimit%s -u %s", ";\n", 1 },
146 { "ulimit%s -n %s", ";\n", 1 },
147 { "ulimit%s -b %s", ";\n", 1 },
148 { "ulimit%s -v %s", ";\n", 1024 },
149 { "ulimit%s -p %s", ";\n", 1 },
150 { "ulimit%s -w %s", ";\n", 1024 }
151 }
152 },
153 { "tcsh", "unlimited", "", " -h", "", NULL,
154 {
155 { "limit%s cputime %s", ";\n", 1 },
156 { "limit%s filesize %s", ";\n", 1024 },
157 { "limit%s datasize %s", ";\n", 1024 },
158 { "limit%s stacksize %s", ";\n", 1024 },
159 { "limit%s coredumpsize %s", ";\n", 1024 },
160 { "limit%s memoryuse %s", ";\n", 1024 },
161 { "limit%s memorylocked %s", ";\n", 1024 },
162 { "limit%s maxproc %s", ";\n", 1 },
163 { "limit%s descriptors %s", ";\n", 1 },
164 { "limit%s sbsize %s", ";\n", 1 },
165 { "limit%s vmemoryuse %s", ";\n", 1024 },
166 { "limit%s pseudoterminals %s", ";\n", 1 },
167 { "limit%s swapsize %s", ";\n", 1024 },
168 { "limit%s kqueues %s", ";\n", 1 },
169 { "limit%s umtxp %s", ";\n", 1 },
170 }
171 },
172 { "ksh|pdksh", "unlimited", "", " -H", " -S", "",
173 {
174 { "ulimit%s -t %s", ";\n", 1 },
175 { "ulimit%s -f %s", ";\n", 512 },
176 { "ulimit%s -d %s", ";\n", 1024 },
177 { "ulimit%s -s %s", ";\n", 1024 },
178 { "ulimit%s -c %s", ";\n", 512 },
179 { "ulimit%s -m %s", ";\n", 1024 },
180 { "ulimit%s -l %s", ";\n", 1024 },
181 { "ulimit%s -p %s", ";\n", 1 },
182 { "ulimit%s -n %s", ";\n", 1 },
183 { "ulimit%s -b %s", ";\n", 1 },
184 { "ulimit%s -v %s", ";\n", 1024 },
185 { "ulimit%s -p %s", ";\n", 1 },
186 { "ulimit%s -w %s", ";\n", 1024 }
187 }
188 },
189 { "zsh", "unlimited", "", " -H", " -S", "",
190 {
191 { "ulimit%s -t %s", ";\n", 1 },
192 { "ulimit%s -f %s", ";\n", 512 },
193 { "ulimit%s -d %s", ";\n", 1024 },
194 { "ulimit%s -s %s", ";\n", 1024 },
195 { "ulimit%s -c %s", ";\n", 512 },
196 { "ulimit%s -m %s", ";\n", 1024 },
197 { "ulimit%s -l %s", ";\n", 1024 },
198 { "ulimit%s -u %s", ";\n", 1 },
199 { "ulimit%s -n %s", ";\n", 1 },
200 { "ulimit%s -b %s", ";\n", 1 },
201 { "ulimit%s -v %s", ";\n", 1024 },
202 { "ulimit%s -p %s", ";\n", 1 },
203 { "ulimit%s -w %s", ";\n", 1024 }
204 }
205 },
206 { "rc|es", "unlimited", "", " -h", "", NULL,
207 {
208 { "limit%s cputime %s", ";\n", 1 },
209 { "limit%s filesize %s", ";\n", 1024 },
210 { "limit%s datasize %s", ";\n", 1024 },
211 { "limit%s stacksize %s", ";\n", 1024 },
212 { "limit%s coredumpsize %s", ";\n", 1024 },
213 { "limit%s memoryuse %s", ";\n", 1024 },
214 { "limit%s lockedmemory %s", ";\n", 1024 },
215 { "limit%s processes %s", ";\n", 1 },
216 { "limit%s descriptors %s", ";\n", 1 },
217 { "limit%s sbsize %s", ";\n", 1 },
218 { "limit%s vmemoryuse %s", ";\n", 1024 },
219 { "limit%s pseudoterminals %s", ";\n", 1 },
220 { "limit%s swapuse %s", ";\n", 1024 }
221 }
222 },
223 { NULL, NULL, NULL, NULL, NULL, NULL,
224 { }
225 }
226 };
227
228 static struct {
229 const char * cap;
230 rlim_t (*func)(login_cap_t *, const char *, rlim_t, rlim_t);
231 } resources[RLIM_NLIMITS] = {
232 { "cputime", login_getcaptime },
233 { "filesize", login_getcapsize },
234 { "datasize", login_getcapsize },
235 { "stacksize", login_getcapsize },
236 { "coredumpsize", login_getcapsize },
237 { "memoryuse", login_getcapsize },
238 { "memorylocked", login_getcapsize },
239 { "maxproc", login_getcapnum },
240 { "openfiles", login_getcapnum },
241 { "sbsize", login_getcapsize },
242 { "vmemoryuse", login_getcapsize },
243 { "pseudoterminals",login_getcapnum },
244 { "swapuse", login_getcapsize },
245 { "kqueues", login_getcapnum },
246 { "umtxp", login_getcapnum },
247 };
248
249 /*
250 * One letter for each resource levels.
251 * NOTE: There is a dependency on the corresponding
252 * letter index being equal to the resource number.
253 * If sys/resource.h defines are changed, this needs
254 * to be modified accordingly!
255 */
256
257 #define RCS_STRING "tfdscmlunbvpwko"
258
259 static rlim_t resource_num(int which, int ch, const char *str);
260 static void usage(void);
261 static int getshelltype(void);
262 static void print_limit(rlim_t limit, unsigned divisor, const char *inf,
263 const char *pfx, const char *sfx, const char *which);
264 static void getrlimit_proc(pid_t pid, int resource, struct rlimit *rlp);
265 static void setrlimit_proc(pid_t pid, int resource, const struct rlimit *rlp);
266 extern char **environ;
267
268 static const char rcs_string[] = RCS_STRING;
269
270 int
main(int argc,char * argv[])271 main(int argc, char *argv[])
272 {
273 char *p, *cls = NULL;
274 char *cleanenv[1];
275 struct passwd * pwd = NULL;
276 int rcswhich, shelltype;
277 int i, num_limits = 0;
278 int ch, doeval = 0, doall = 0;
279 int rtrn, setproc;
280 login_cap_t * lc = NULL;
281 enum { ANY=0, SOFT=1, HARD=2, BOTH=3, DISPLAYONLY=4 } type = ANY;
282 enum { RCSUNKNOWN=0, RCSSET=1, RCSSEL=2 } todo = RCSUNKNOWN;
283 int which_limits[RLIM_NLIMITS];
284 rlim_t set_limits[RLIM_NLIMITS];
285 struct rlimit limits[RLIM_NLIMITS];
286 pid_t pid;
287
288 /* init resource tables */
289 for (i = 0; i < RLIM_NLIMITS; i++) {
290 which_limits[i] = 0; /* Don't set/display any */
291 set_limits[i] = RLIM_INFINITY;
292 }
293
294 pid = -1;
295 optarg = NULL;
296 while ((ch = getopt(argc, argv,
297 ":EeC:U:BSHP:ab:c:d:f:l:m:n:s:t:u:v:p:w:k:o:")) != -1) {
298 switch(ch) {
299 case 'a':
300 doall = 1;
301 break;
302 case 'E':
303 environ = cleanenv;
304 cleanenv[0] = NULL;
305 break;
306 case 'e':
307 doeval = 1;
308 break;
309 case 'C':
310 cls = optarg;
311 break;
312 case 'U':
313 if ((pwd = getpwnam(optarg)) == NULL) {
314 if (!isdigit(*optarg) ||
315 (pwd = getpwuid(atoi(optarg))) == NULL) {
316 warnx("invalid user `%s'", optarg);
317 usage();
318 }
319 }
320 break;
321 case 'H':
322 type = HARD;
323 break;
324 case 'S':
325 type = SOFT;
326 break;
327 case 'B':
328 type = SOFT|HARD;
329 break;
330 case 'P':
331 if (!isdigit(*optarg) || (pid = atoi(optarg)) < 0) {
332 warnx("invalid pid `%s'", optarg);
333 usage();
334 }
335 break;
336 default:
337 case ':': /* Without arg */
338 if ((p = strchr(rcs_string, optopt)) != NULL) {
339 int rcswhich1 = p - rcs_string;
340 if (optarg && *optarg == '-') { /* 'arg' is actually a switch */
341 --optind; /* back one arg, and make arg NULL */
342 optarg = NULL;
343 }
344 todo = optarg == NULL ? RCSSEL : RCSSET;
345 if (type == ANY)
346 type = BOTH;
347 which_limits[rcswhich1] = optarg ? type : DISPLAYONLY;
348 set_limits[rcswhich1] = resource_num(rcswhich1, optopt, optarg);
349 num_limits++;
350 break;
351 }
352 /* FALLTHRU */
353 case '?':
354 usage();
355 }
356 optarg = NULL;
357 }
358
359 if (pid != -1) {
360 if (cls != NULL) {
361 warnx("-C cannot be used with -P option");
362 usage();
363 }
364 if (pwd != NULL) {
365 warnx("-U cannot be used with -P option");
366 usage();
367 }
368 }
369
370 /* Get current resource values */
371 setproc = 0;
372 for (i = 0; i < RLIM_NLIMITS; i++) {
373 if (pid == -1) {
374 getrlimit(i, &limits[i]);
375 } else if (doall || num_limits == 0) {
376 getrlimit_proc(pid, i, &limits[i]);
377 } else if (which_limits[i] != 0) {
378 getrlimit_proc(pid, i, &limits[i]);
379 setproc = 1;
380 }
381 }
382
383 /* If user was specified, get class from that */
384 if (pwd != NULL)
385 lc = login_getpwclass(pwd);
386 else if (cls != NULL && *cls != '\0') {
387 lc = login_getclassbyname(cls, NULL);
388 if (lc == NULL || strcmp(cls, lc->lc_class) != 0)
389 fprintf(stderr, "login class '%s' non-existent, using %s\n",
390 cls, lc?lc->lc_class:"current settings");
391 }
392
393 /* If we have a login class, update resource table from that */
394 if (lc != NULL) {
395 for (rcswhich = 0; rcswhich < RLIM_NLIMITS; rcswhich++) {
396 char str[40];
397 rlim_t val;
398
399 /* current value overridden by resourcename or resourcename-cur */
400 sprintf(str, "%s-cur", resources[rcswhich].cap);
401 val = resources[rcswhich].func(lc, resources[rcswhich].cap, limits[rcswhich].rlim_cur, limits[rcswhich].rlim_cur);
402 limits[rcswhich].rlim_cur = resources[rcswhich].func(lc, str, val, val);
403 /* maximum value overridden by resourcename or resourcename-max */
404 sprintf(str, "%s-max", resources[rcswhich].cap);
405 val = resources[rcswhich].func(lc, resources[rcswhich].cap, limits[rcswhich].rlim_max, limits[rcswhich].rlim_max);
406 limits[rcswhich].rlim_max = resources[rcswhich].func(lc, str, val, val);
407 }
408 }
409
410 /* now, let's determine what we wish to do with all this */
411
412 argv += optind;
413
414 /* If we're setting limits or doing an eval (ie. we're not just
415 * displaying), then check that hard limits are not lower than
416 * soft limits, and force rasing the hard limit if we need to if
417 * we are raising the soft limit, or lower the soft limit if we
418 * are lowering the hard limit.
419 */
420 if ((*argv || doeval) && getuid() == 0) {
421
422 for (rcswhich = 0; rcswhich < RLIM_NLIMITS; rcswhich++) {
423 if (limits[rcswhich].rlim_max != RLIM_INFINITY) {
424 if (limits[rcswhich].rlim_cur == RLIM_INFINITY) {
425 limits[rcswhich].rlim_max = RLIM_INFINITY;
426 which_limits[rcswhich] |= HARD;
427 } else if (limits[rcswhich].rlim_cur > limits[rcswhich].rlim_max) {
428 if (which_limits[rcswhich] == SOFT) {
429 limits[rcswhich].rlim_max = limits[rcswhich].rlim_cur;
430 which_limits[rcswhich] |= HARD;
431 } else if (which_limits[rcswhich] == HARD) {
432 limits[rcswhich].rlim_cur = limits[rcswhich].rlim_max;
433 which_limits[rcswhich] |= SOFT;
434 } else {
435 /* else.. if we're specifically setting both to
436 * silly values, then let it error out.
437 */
438 }
439 }
440 }
441 }
442 }
443
444 /* See if we've overridden anything specific on the command line */
445 if (num_limits && todo == RCSSET) {
446 for (rcswhich = 0; rcswhich < RLIM_NLIMITS; rcswhich++) {
447 if (which_limits[rcswhich] & HARD)
448 limits[rcswhich].rlim_max = set_limits[rcswhich];
449 if (which_limits[rcswhich] & SOFT)
450 limits[rcswhich].rlim_cur = set_limits[rcswhich];
451 }
452 }
453
454 /* If *argv is not NULL, then we are being asked to
455 * (perhaps) set environment variables and run a program
456 */
457 if (*argv) {
458 if (doeval) {
459 warnx("-e cannot be used with `cmd' option");
460 usage();
461 }
462 if (pid != -1) {
463 warnx("-P cannot be used with `cmd' option");
464 usage();
465 }
466
467 login_close(lc);
468
469 /* set leading environment variables, like eval(1) */
470 while (*argv && (p = strchr(*argv, '='))) {
471 *p = '\0';
472 rtrn = setenv(*argv++, p + 1, 1);
473 *p = '=';
474 if (rtrn == -1)
475 err(EXIT_FAILURE, "setenv %s", *argv);
476 }
477
478 /* Set limits */
479 for (rcswhich = 0; rcswhich < RLIM_NLIMITS; rcswhich++) {
480 if (doall || num_limits == 0 || which_limits[rcswhich] != 0)
481 if (setrlimit(rcswhich, &limits[rcswhich]) == -1)
482 err(1, "setrlimit %s", resources[rcswhich].cap);
483 }
484
485 if (*argv == NULL)
486 usage();
487
488 execvp(*argv, argv);
489 err(1, "%s", *argv);
490 }
491
492 if (setproc) {
493 for (rcswhich = 0; rcswhich < RLIM_NLIMITS; rcswhich++) {
494 if (which_limits[rcswhich] != 0)
495 setrlimit_proc(pid, rcswhich, &limits[rcswhich]);
496 }
497 exit(EXIT_SUCCESS);
498 }
499
500 shelltype = doeval ? getshelltype() : SH_NONE;
501
502 if (type == ANY) /* Default to soft limits */
503 type = SOFT;
504
505 /* Display limits */
506 printf(shellparm[shelltype].cmd,
507 lc ? " for class " : " (current)",
508 lc ? lc->lc_class : "");
509
510 for (rcswhich = 0; rcswhich < RLIM_NLIMITS; rcswhich++) {
511 if (doall || num_limits == 0 || which_limits[rcswhich] != 0) {
512 if (which_limits[rcswhich] == ANY)
513 which_limits[rcswhich] = type;
514 if (shellparm[shelltype].lprm[rcswhich].pfx) {
515 if (shellparm[shelltype].both && limits[rcswhich].rlim_cur == limits[rcswhich].rlim_max) {
516 print_limit(limits[rcswhich].rlim_max,
517 shellparm[shelltype].lprm[rcswhich].divisor,
518 shellparm[shelltype].inf,
519 shellparm[shelltype].lprm[rcswhich].pfx,
520 shellparm[shelltype].lprm[rcswhich].sfx,
521 shellparm[shelltype].both);
522 } else {
523 if (which_limits[rcswhich] & HARD) {
524 print_limit(limits[rcswhich].rlim_max,
525 shellparm[shelltype].lprm[rcswhich].divisor,
526 shellparm[shelltype].inf,
527 shellparm[shelltype].lprm[rcswhich].pfx,
528 shellparm[shelltype].lprm[rcswhich].sfx,
529 shellparm[shelltype].hard);
530 }
531 if (which_limits[rcswhich] & SOFT) {
532 print_limit(limits[rcswhich].rlim_cur,
533 shellparm[shelltype].lprm[rcswhich].divisor,
534 shellparm[shelltype].inf,
535 shellparm[shelltype].lprm[rcswhich].pfx,
536 shellparm[shelltype].lprm[rcswhich].sfx,
537 shellparm[shelltype].soft);
538 }
539 }
540 }
541 }
542 }
543
544 login_close(lc);
545 exit(EXIT_SUCCESS);
546 }
547
548
549 static void
usage(void)550 usage(void)
551 {
552 (void)fprintf(stderr,
553 "usage: limits [-C class|-P pid|-U user] [-eaSHBE] "
554 "[-bcdfklmnostuvpw [val]] [[name=val ...] cmd]\n");
555 exit(EXIT_FAILURE);
556 }
557
558 static void
print_limit(rlim_t limit,unsigned divisor,const char * inf,const char * pfx,const char * sfx,const char * which)559 print_limit(rlim_t limit, unsigned divisor, const char * inf, const char * pfx, const char * sfx, const char * which)
560 {
561 char numbr[64];
562
563 if (limit == RLIM_INFINITY)
564 strlcpy(numbr, inf, sizeof(numbr));
565 else
566 sprintf(numbr, "%jd", (intmax_t)((limit + divisor/2) / divisor));
567 printf(pfx, which, numbr);
568 printf(sfx, which);
569
570 }
571
572
573 static rlim_t
resource_num(int which,int ch,const char * str)574 resource_num(int which, int ch, const char *str)
575 {
576 rlim_t res = RLIM_INFINITY;
577
578 if (str != NULL &&
579 !(strcasecmp(str, "inf") == 0 ||
580 strcasecmp(str, "infinity") == 0 ||
581 strcasecmp(str, "unlimit") == 0 ||
582 strcasecmp(str, "unlimited") == 0)) {
583 const char * s = str;
584 char *e;
585
586 switch (which) {
587 case RLIMIT_CPU: /* time values */
588 errno = 0;
589 res = 0;
590 while (*s) {
591 rlim_t tim = strtoq(s, &e, 0);
592 if (e == NULL || e == s || errno)
593 break;
594 switch (*e++) {
595 case 0: /* end of string */
596 e--;
597 default:
598 case 's': case 'S': /* seconds */
599 break;
600 case 'm': case 'M': /* minutes */
601 tim *= 60L;
602 break;
603 case 'h': case 'H': /* hours */
604 tim *= (60L * 60L);
605 break;
606 case 'd': case 'D': /* days */
607 tim *= (60L * 60L * 24L);
608 break;
609 case 'w': case 'W': /* weeks */
610 tim *= (60L * 60L * 24L * 7L);
611 break;
612 case 'y': case 'Y': /* Years */
613 tim *= (60L * 60L * 24L * 365L);
614 }
615 s = e;
616 res += tim;
617 }
618 break;
619 case RLIMIT_FSIZE: /* Size values */
620 case RLIMIT_DATA:
621 case RLIMIT_STACK:
622 case RLIMIT_CORE:
623 case RLIMIT_RSS:
624 case RLIMIT_MEMLOCK:
625 case RLIMIT_SBSIZE:
626 case RLIMIT_VMEM:
627 case RLIMIT_SWAP:
628 errno = 0;
629 res = 0;
630 while (*s) {
631 rlim_t mult, tim = strtoq(s, &e, 0);
632 if (e == NULL || e == s || errno)
633 break;
634 switch (*e++) {
635 case 0: /* end of string */
636 e--;
637 default:
638 mult = 1;
639 break;
640 case 'b': case 'B': /* 512-byte blocks */
641 mult = 512;
642 break;
643 case 'k': case 'K': /* 1024-byte Kilobytes */
644 mult = 1024;
645 break;
646 case 'm': case 'M': /* 1024-k kbytes */
647 mult = 1024 * 1024;
648 break;
649 case 'g': case 'G': /* 1Gbyte */
650 mult = 1024 * 1024 * 1024;
651 break;
652 case 't': case 'T': /* 1TBte */
653 mult = 1024LL * 1024LL * 1024LL * 1024LL;
654 break;
655 }
656 s = e;
657 res += (tim * mult);
658 }
659 break;
660 case RLIMIT_NPROC:
661 case RLIMIT_NOFILE:
662 case RLIMIT_NPTS:
663 case RLIMIT_KQUEUES:
664 case RLIMIT_UMTXP:
665 res = strtoq(s, &e, 0);
666 s = e;
667 break;
668 }
669 if (*s) {
670 warnx("invalid value -%c `%s'", ch, str);
671 usage();
672 }
673 }
674 return res;
675 }
676
677
678 static int
getshellbyname(const char * shell)679 getshellbyname(const char * shell)
680 {
681 int i;
682 const char * q;
683 const char * p = strrchr(shell, '/');
684
685 p = p ? p+1 : shell;
686 for (i = 0; (q = shellparm[i].name) != NULL; i++) {
687 while (*q) {
688 int j = strcspn(q, "|");
689
690 if (j == 0)
691 break;
692 if (strncmp(p, q, j) == 0)
693 return i;
694 if (*(q += j))
695 ++q;
696 }
697 }
698 return SH_SH;
699 }
700
701
702 /*
703 * Determine the type of shell our parent process is
704 * This is quite tricky, not 100% reliable and probably
705 * not nearly as thorough as it should be. Basically, this
706 * is a "best guess" only, but hopefully will work in
707 * most cases.
708 */
709
710 static int
getshelltype(void)711 getshelltype(void)
712 {
713 pid_t ppid = getppid();
714
715 if (ppid != 1) {
716 struct kinfo_proc kp;
717 struct stat st;
718 char path[MAXPATHLEN];
719 char * shell = getenv("SHELL");
720 int mib[4];
721 size_t len;
722
723 mib[0] = CTL_KERN;
724 mib[1] = KERN_PROC;
725 mib[3] = ppid;
726
727 if (shell != NULL && stat(shell, &st) != -1) {
728 struct stat st1;
729
730 mib[2] = KERN_PROC_PATHNAME;
731 len = sizeof(path);
732 if (sysctl(mib, 4, path, &len, NULL, 0) != -1) {
733 /* $SHELL is actual shell? */
734 if (stat(path, &st1) != -1 && memcmp(&st, &st1, sizeof st) == 0)
735 return getshellbyname(shell);
736 }
737 }
738 mib[2] = KERN_PROC_PID;
739 len = sizeof(kp);
740 if (sysctl(mib, 4, &kp, &len, NULL, 0) != -1)
741 return getshellbyname(kp.ki_comm);
742 }
743 return SH_SH;
744 }
745
746 static void
getrlimit_proc(pid_t pid,int resource,struct rlimit * rlp)747 getrlimit_proc(pid_t pid, int resource, struct rlimit *rlp)
748 {
749 int error;
750 int name[5];
751 size_t len;
752
753 name[0] = CTL_KERN;
754 name[1] = KERN_PROC;
755 name[2] = KERN_PROC_RLIMIT;
756 name[3] = pid;
757 name[4] = resource;
758 len = sizeof(*rlp);
759 error = sysctl(name, 5, rlp, &len, NULL, 0);
760 if (error == -1)
761 err(EXIT_FAILURE, "sysctl: kern.proc.rlimit: %d", pid);
762 if (len != sizeof(*rlp))
763 errx(EXIT_FAILURE, "sysctl() returns wrong size");
764 }
765
766 static void
setrlimit_proc(pid_t pid,int resource,const struct rlimit * rlp)767 setrlimit_proc(pid_t pid, int resource, const struct rlimit *rlp)
768 {
769 int error;
770 int name[5];
771
772 name[0] = CTL_KERN;
773 name[1] = KERN_PROC;
774 name[2] = KERN_PROC_RLIMIT;
775 name[3] = pid;
776 name[4] = resource;
777 error = sysctl(name, 5, NULL, 0, rlp, sizeof(*rlp));
778 if (error == -1)
779 err(EXIT_FAILURE, "sysctl: kern.proc.rlimit: %d", pid);
780 }
781