diff options
author | Denys Vlasenko <vda.linux@googlemail.com> | 2019-04-27 21:01:35 +0200 |
---|---|---|
committer | Denys Vlasenko <vda.linux@googlemail.com> | 2019-04-27 21:01:35 +0200 |
commit | a4d76ea1373a7cf06d3d0a3c8905646638e97a13 (patch) | |
tree | 3db6c879b4fa44cbc4d87b7a4062ee386c511266 /shell/shell_common.c | |
parent | 100fa20c68564311cef8f625bcb1b423e96c97d6 (diff) | |
download | busybox-w32-a4d76ea1373a7cf06d3d0a3c8905646638e97a13.tar.gz busybox-w32-a4d76ea1373a7cf06d3d0a3c8905646638e97a13.tar.bz2 busybox-w32-a4d76ea1373a7cf06d3d0a3c8905646638e97a13.zip |
ash,hush: fix ulimit to be more bash-compat, closes 11791
function old new delta
shell_builtin_ulimit 486 651 +165
limit_chars - 14 +14
------------------------------------------------------------------------------
(add/remove: 1/0 grow/shrink: 1/0 up/down: 179/0) Total: 179 bytes
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
Diffstat (limited to '')
-rw-r--r-- | shell/shell_common.c | 265 |
1 files changed, 181 insertions, 84 deletions
diff --git a/shell/shell_common.c b/shell/shell_common.c index f2bf5ab65..686c18f54 100644 --- a/shell/shell_common.c +++ b/shell/shell_common.c | |||
@@ -335,62 +335,93 @@ shell_builtin_read(struct builtin_read_params *params) | |||
335 | struct limits { | 335 | struct limits { |
336 | uint8_t cmd; /* RLIMIT_xxx fit into it */ | 336 | uint8_t cmd; /* RLIMIT_xxx fit into it */ |
337 | uint8_t factor_shift; /* shift by to get rlim_{cur,max} values */ | 337 | uint8_t factor_shift; /* shift by to get rlim_{cur,max} values */ |
338 | char option; | ||
339 | const char *name; | 338 | const char *name; |
340 | }; | 339 | }; |
341 | 340 | ||
342 | static const struct limits limits_tbl[] = { | 341 | static const struct limits limits_tbl[] = { |
343 | #ifdef RLIMIT_FSIZE | 342 | /* No RLIMIT_FSIZE define guard since -f is the default limit and this must exist */ |
344 | { RLIMIT_FSIZE, 9, 'f', "file size (blocks)" }, | 343 | { RLIMIT_FSIZE, 9, "file size (blocks)" }, // -f |
345 | #endif | ||
346 | #ifdef RLIMIT_CPU | 344 | #ifdef RLIMIT_CPU |
347 | { RLIMIT_CPU, 0, 't', "cpu time (seconds)" }, | 345 | { RLIMIT_CPU, 0, "cpu time (seconds)" }, // -t |
348 | #endif | 346 | #endif |
349 | #ifdef RLIMIT_DATA | 347 | #ifdef RLIMIT_DATA |
350 | { RLIMIT_DATA, 10, 'd', "data seg size (kb)" }, | 348 | { RLIMIT_DATA, 10, "data seg size (kb)" }, // -d |
351 | #endif | 349 | #endif |
352 | #ifdef RLIMIT_STACK | 350 | #ifdef RLIMIT_STACK |
353 | { RLIMIT_STACK, 10, 's', "stack size (kb)" }, | 351 | { RLIMIT_STACK, 10, "stack size (kb)" }, // -s |
354 | #endif | 352 | #endif |
355 | #ifdef RLIMIT_CORE | 353 | #ifdef RLIMIT_CORE |
356 | { RLIMIT_CORE, 9, 'c', "core file size (blocks)" }, | 354 | { RLIMIT_CORE, 9, "core file size (blocks)" }, // -c |
357 | #endif | 355 | #endif |
358 | #ifdef RLIMIT_RSS | 356 | #ifdef RLIMIT_RSS |
359 | { RLIMIT_RSS, 10, 'm', "resident set size (kb)" }, | 357 | { RLIMIT_RSS, 10, "resident set size (kb)" }, // -m |
360 | #endif | 358 | #endif |
361 | #ifdef RLIMIT_MEMLOCK | 359 | #ifdef RLIMIT_MEMLOCK |
362 | { RLIMIT_MEMLOCK, 10, 'l', "locked memory (kb)" }, | 360 | { RLIMIT_MEMLOCK, 10, "locked memory (kb)" }, // -l |
363 | #endif | 361 | #endif |
364 | #ifdef RLIMIT_NPROC | 362 | #ifdef RLIMIT_NPROC |
365 | { RLIMIT_NPROC, 0, 'p', "processes" }, | 363 | { RLIMIT_NPROC, 0, "processes" }, // -p |
366 | #endif | 364 | #endif |
367 | #ifdef RLIMIT_NOFILE | 365 | #ifdef RLIMIT_NOFILE |
368 | { RLIMIT_NOFILE, 0, 'n', "file descriptors" }, | 366 | { RLIMIT_NOFILE, 0, "file descriptors" }, // -n |
369 | #endif | 367 | #endif |
370 | #ifdef RLIMIT_AS | 368 | #ifdef RLIMIT_AS |
371 | { RLIMIT_AS, 10, 'v', "address space (kb)" }, | 369 | { RLIMIT_AS, 10, "address space (kb)" }, // -v |
372 | #endif | 370 | #endif |
373 | #ifdef RLIMIT_LOCKS | 371 | #ifdef RLIMIT_LOCKS |
374 | { RLIMIT_LOCKS, 0, 'w', "locks" }, | 372 | { RLIMIT_LOCKS, 0, "locks" }, // -w |
375 | #endif | 373 | #endif |
376 | #ifdef RLIMIT_NICE | 374 | #ifdef RLIMIT_NICE |
377 | { RLIMIT_NICE, 0, 'e', "scheduling priority" }, | 375 | { RLIMIT_NICE, 0, "scheduling priority" }, // -e |
378 | #endif | 376 | #endif |
379 | #ifdef RLIMIT_RTPRIO | 377 | #ifdef RLIMIT_RTPRIO |
380 | { RLIMIT_RTPRIO, 0, 'r', "real-time priority" }, | 378 | { RLIMIT_RTPRIO, 0, "real-time priority" }, // -r |
381 | #endif | 379 | #endif |
382 | }; | 380 | }; |
383 | 381 | ||
384 | enum { | 382 | static const char limit_chars[] ALIGN1 = |
385 | OPT_hard = (1 << 0), | 383 | "f" |
386 | OPT_soft = (1 << 1), | 384 | #ifdef RLIMIT_CPU |
387 | }; | 385 | "t" |
386 | #endif | ||
387 | #ifdef RLIMIT_DATA | ||
388 | "d" | ||
389 | #endif | ||
390 | #ifdef RLIMIT_STACK | ||
391 | "s" | ||
392 | #endif | ||
393 | #ifdef RLIMIT_CORE | ||
394 | "c" | ||
395 | #endif | ||
396 | #ifdef RLIMIT_RSS | ||
397 | "m" | ||
398 | #endif | ||
399 | #ifdef RLIMIT_MEMLOCK | ||
400 | "l" | ||
401 | #endif | ||
402 | #ifdef RLIMIT_NPROC | ||
403 | "p" | ||
404 | #endif | ||
405 | #ifdef RLIMIT_NOFILE | ||
406 | "n" | ||
407 | #endif | ||
408 | #ifdef RLIMIT_AS | ||
409 | "v" | ||
410 | #endif | ||
411 | #ifdef RLIMIT_LOCKS | ||
412 | "w" | ||
413 | #endif | ||
414 | #ifdef RLIMIT_NICE | ||
415 | "e" | ||
416 | #endif | ||
417 | #ifdef RLIMIT_RTPRIO | ||
418 | "r" | ||
419 | #endif | ||
420 | ; | ||
388 | 421 | ||
389 | /* "-": treat args as parameters of option with ASCII code 1 */ | 422 | /* "-": treat args as parameters of option with ASCII code 1 */ |
390 | static const char ulimit_opt_string[] ALIGN1 = "-HSa" | 423 | static const char ulimit_opt_string[] ALIGN1 = "-HSa" |
391 | #ifdef RLIMIT_FSIZE | ||
392 | "f::" | 424 | "f::" |
393 | #endif | ||
394 | #ifdef RLIMIT_CPU | 425 | #ifdef RLIMIT_CPU |
395 | "t::" | 426 | "t::" |
396 | #endif | 427 | #endif |
@@ -429,13 +460,19 @@ static const char ulimit_opt_string[] ALIGN1 = "-HSa" | |||
429 | #endif | 460 | #endif |
430 | ; | 461 | ; |
431 | 462 | ||
463 | enum { | ||
464 | OPT_hard = (1 << 0), | ||
465 | OPT_soft = (1 << 1), | ||
466 | OPT_all = (1 << 2), | ||
467 | }; | ||
468 | |||
432 | static void printlim(unsigned opts, const struct rlimit *limit, | 469 | static void printlim(unsigned opts, const struct rlimit *limit, |
433 | const struct limits *l) | 470 | const struct limits *l) |
434 | { | 471 | { |
435 | rlim_t val; | 472 | rlim_t val; |
436 | 473 | ||
437 | val = limit->rlim_max; | 474 | val = limit->rlim_max; |
438 | if (!(opts & OPT_hard)) | 475 | if (opts & OPT_soft) |
439 | val = limit->rlim_cur; | 476 | val = limit->rlim_cur; |
440 | 477 | ||
441 | if (val == RLIM_INFINITY) | 478 | if (val == RLIM_INFINITY) |
@@ -449,8 +486,11 @@ static void printlim(unsigned opts, const struct rlimit *limit, | |||
449 | int FAST_FUNC | 486 | int FAST_FUNC |
450 | shell_builtin_ulimit(char **argv) | 487 | shell_builtin_ulimit(char **argv) |
451 | { | 488 | { |
489 | struct rlimit limit; | ||
490 | unsigned opt_cnt; | ||
452 | unsigned opts; | 491 | unsigned opts; |
453 | unsigned argc; | 492 | unsigned argc; |
493 | unsigned i; | ||
454 | 494 | ||
455 | /* We can't use getopt32: need to handle commands like | 495 | /* We can't use getopt32: need to handle commands like |
456 | * ulimit 123 -c2 -l 456 | 496 | * ulimit 123 -c2 -l 456 |
@@ -461,12 +501,48 @@ shell_builtin_ulimit(char **argv) | |||
461 | */ | 501 | */ |
462 | GETOPT_RESET(); | 502 | GETOPT_RESET(); |
463 | 503 | ||
504 | // bash 4.4.23: | ||
505 | // | ||
506 | // -H and/or -S change meaning even of options *before* them: ulimit -f 2000 -H | ||
507 | // sets hard limit, ulimit -a -H prints hard limits. | ||
508 | // | ||
509 | // -a is equivalent for requesting all limits to be shown. | ||
510 | // | ||
511 | // If -a is specified, attempts to set limits are ignored: | ||
512 | // ulimit -m 1000; ulimit -m 2000 -a | ||
513 | // shows 1000, not 2000. HOWEVER, *implicit* -f form "ulimit 2000 -a" | ||
514 | // DOES set -f limit [we don't implement this quirk], "ulimit -a 2000" does not. | ||
515 | // Options are still parsed: ulimit -az complains about unknown -z opt. | ||
516 | // | ||
517 | // -a is not cumulative: "ulimit -a -a" = "ulimit -a -f -m" = "ulimit -a" | ||
518 | // | ||
519 | // -HSa can be combined in one argument and with one other option (example: -Sm), | ||
520 | // but other options can't: limit value is an optional argument, | ||
521 | // thus "-mf" means "-m f", f is the parameter of -m. | ||
522 | // | ||
523 | // Limit can be set and then printed: ulimit -m 2000 -m | ||
524 | // If set more than once, they are set and printed in order: | ||
525 | // try ulimit -m -m 1000 -m -m 2000 -m -m 3000 -m | ||
526 | // | ||
527 | // Limits are shown in the order of options given: | ||
528 | // ulimit -m -f is not the same as ulimit -f -m. | ||
529 | // | ||
530 | // If both -S and -H are given, show soft limit. | ||
531 | // | ||
532 | // Short printout (limit value only) is printed only if just one option | ||
533 | // is given: ulimit -m. ulimit -f -m prints verbose lines. | ||
534 | // ulimit -f -f prints same verbose line twice. | ||
535 | // ulimit -m 10000 -f prints verbose line for -f. | ||
536 | |||
464 | argc = string_array_len(argv); | 537 | argc = string_array_len(argv); |
465 | 538 | ||
539 | /* First pass over options: detect -H/-S/-a status, | ||
540 | * and "bare ulimit" and "only one option" cases | ||
541 | * by counting other opts. | ||
542 | */ | ||
543 | opt_cnt = 0; | ||
466 | opts = 0; | 544 | opts = 0; |
467 | while (1) { | 545 | while (1) { |
468 | struct rlimit limit; | ||
469 | const struct limits *l; | ||
470 | int opt_char = getopt(argc, argv, ulimit_opt_string); | 546 | int opt_char = getopt(argc, argv, ulimit_opt_string); |
471 | 547 | ||
472 | if (opt_char == -1) | 548 | if (opt_char == -1) |
@@ -479,72 +555,93 @@ shell_builtin_ulimit(char **argv) | |||
479 | opts |= OPT_soft; | 555 | opts |= OPT_soft; |
480 | continue; | 556 | continue; |
481 | } | 557 | } |
482 | |||
483 | if (opt_char == 'a') { | 558 | if (opt_char == 'a') { |
484 | for (l = limits_tbl; l != &limits_tbl[ARRAY_SIZE(limits_tbl)]; l++) { | 559 | opts |= OPT_all; |
485 | getrlimit(l->cmd, &limit); | ||
486 | printf("-%c: %-30s ", l->option, l->name); | ||
487 | printlim(opts, &limit, l); | ||
488 | } | ||
489 | continue; | 560 | continue; |
490 | } | 561 | } |
562 | if (opt_char == '?') { | ||
563 | /* bad option. getopt already complained. */ | ||
564 | return EXIT_FAILURE; | ||
565 | } | ||
566 | opt_cnt++; | ||
567 | } /* while (there are options) */ | ||
491 | 568 | ||
492 | if (opt_char == 1) | 569 | if (!(opts & (OPT_hard | OPT_soft))) |
493 | opt_char = 'f'; | 570 | opts |= (OPT_hard | OPT_soft); |
494 | for (l = limits_tbl; l != &limits_tbl[ARRAY_SIZE(limits_tbl)]; l++) { | 571 | if (opts & OPT_all) { |
495 | if (opt_char == l->option) { | 572 | for (i = 0; i < ARRAY_SIZE(limits_tbl); i++) { |
496 | char *val_str; | 573 | getrlimit(limits_tbl[i].cmd, &limit); |
497 | 574 | printf("-%c: %-30s ", limit_chars[i], limits_tbl[i].name); | |
498 | getrlimit(l->cmd, &limit); | 575 | printlim(opts, &limit, &limits_tbl[i]); |
499 | 576 | } | |
500 | val_str = optarg; | 577 | return EXIT_SUCCESS; |
501 | if (!val_str && argv[optind] && argv[optind][0] != '-') | 578 | } |
502 | val_str = argv[optind++]; /* ++ skips NN in "-c NN" case */ | ||
503 | if (val_str) { | ||
504 | rlim_t val; | ||
505 | |||
506 | if (strcmp(val_str, "unlimited") == 0) | ||
507 | val = RLIM_INFINITY; | ||
508 | else { | ||
509 | if (sizeof(val) == sizeof(int)) | ||
510 | val = bb_strtou(val_str, NULL, 10); | ||
511 | else if (sizeof(val) == sizeof(long)) | ||
512 | val = bb_strtoul(val_str, NULL, 10); | ||
513 | else | ||
514 | val = bb_strtoull(val_str, NULL, 10); | ||
515 | if (errno) { | ||
516 | bb_error_msg("invalid number '%s'", val_str); | ||
517 | return EXIT_FAILURE; | ||
518 | } | ||
519 | val <<= l->factor_shift; | ||
520 | } | ||
521 | //bb_error_msg("opt %c val_str:'%s' val:%lld", opt_char, val_str, (long long)val); | ||
522 | /* from man bash: "If neither -H nor -S | ||
523 | * is specified, both the soft and hard | ||
524 | * limits are set. */ | ||
525 | if (!opts) | ||
526 | opts = OPT_hard + OPT_soft; | ||
527 | if (opts & OPT_hard) | ||
528 | limit.rlim_max = val; | ||
529 | if (opts & OPT_soft) | ||
530 | limit.rlim_cur = val; | ||
531 | //bb_error_msg("setrlimit(%d, %lld, %lld)", l->cmd, (long long)limit.rlim_cur, (long long)limit.rlim_max); | ||
532 | if (setrlimit(l->cmd, &limit) < 0) { | ||
533 | bb_perror_msg("error setting limit"); | ||
534 | return EXIT_FAILURE; | ||
535 | } | ||
536 | } else { | ||
537 | printlim(opts, &limit, l); | ||
538 | } | ||
539 | break; | ||
540 | } | ||
541 | } /* for (every possible opt) */ | ||
542 | 579 | ||
543 | if (l == &limits_tbl[ARRAY_SIZE(limits_tbl)]) { | 580 | /* Second pass: set or print limits, in order */ |
544 | /* bad option. getopt already complained. */ | 581 | GETOPT_RESET(); |
582 | while (1) { | ||
583 | char *val_str; | ||
584 | int opt_char = getopt(argc, argv, ulimit_opt_string); | ||
585 | |||
586 | if (opt_char == -1) | ||
545 | break; | 587 | break; |
588 | if (opt_char == 'H') | ||
589 | continue; | ||
590 | if (opt_char == 'S') | ||
591 | continue; | ||
592 | //if (opt_char == 'a') - impossible | ||
593 | |||
594 | i = 0; /* if "ulimit NNN", -f is assumed */ | ||
595 | if (opt_char != 1) { | ||
596 | i = strchrnul(limit_chars, opt_char) - limit_chars; | ||
597 | //if (i >= ARRAY_SIZE(limits_tbl)) - bad option, impossible | ||
598 | } | ||
599 | |||
600 | val_str = optarg; | ||
601 | if (!val_str && argv[optind] && argv[optind][0] != '-') | ||
602 | val_str = argv[optind++]; /* ++ skips NN in "-c NN" case */ | ||
603 | |||
604 | getrlimit(limits_tbl[i].cmd, &limit); | ||
605 | if (!val_str) { | ||
606 | if (opt_cnt > 1) | ||
607 | printf("-%c: %-30s ", limit_chars[i], limits_tbl[i].name); | ||
608 | printlim(opts, &limit, &limits_tbl[i]); | ||
609 | } else { | ||
610 | rlim_t val = RLIM_INFINITY; | ||
611 | if (strcmp(val_str, "unlimited") != 0) { | ||
612 | if (sizeof(val) == sizeof(int)) | ||
613 | val = bb_strtou(val_str, NULL, 10); | ||
614 | else if (sizeof(val) == sizeof(long)) | ||
615 | val = bb_strtoul(val_str, NULL, 10); | ||
616 | else | ||
617 | val = bb_strtoull(val_str, NULL, 10); | ||
618 | if (errno) { | ||
619 | bb_error_msg("invalid number '%s'", val_str); | ||
620 | return EXIT_FAILURE; | ||
621 | } | ||
622 | val <<= limits_tbl[i].factor_shift; | ||
623 | } | ||
624 | //bb_error_msg("opt %c val_str:'%s' val:%lld", opt_char, val_str, (long long)val); | ||
625 | /* from man bash: "If neither -H nor -S | ||
626 | * is specified, both the soft and hard | ||
627 | * limits are set. */ | ||
628 | if (opts & OPT_hard) | ||
629 | limit.rlim_max = val; | ||
630 | if (opts & OPT_soft) | ||
631 | limit.rlim_cur = val; | ||
632 | //bb_error_msg("setrlimit(%d, %lld, %lld)", limits_tbl[i].cmd, (long long)limit.rlim_cur, (long long)limit.rlim_max); | ||
633 | if (setrlimit(limits_tbl[i].cmd, &limit) < 0) { | ||
634 | bb_perror_msg("error setting limit"); | ||
635 | return EXIT_FAILURE; | ||
636 | } | ||
546 | } | 637 | } |
547 | } /* while (there are options) */ | 638 | } /* while (there are options) */ |
548 | 639 | ||
549 | return 0; | 640 | if (opt_cnt == 0) { |
641 | /* "bare ulimit": treat it as if it was -f */ | ||
642 | getrlimit(limits_tbl[0].cmd, &limit); | ||
643 | printlim(opts, &limit, &limits_tbl[0]); | ||
644 | } | ||
645 | |||
646 | return EXIT_SUCCESS; | ||
550 | } | 647 | } |