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