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