diff options
Diffstat (limited to 'shell/shell_common.c')
-rw-r--r-- | shell/shell_common.c | 343 |
1 files changed, 214 insertions, 129 deletions
diff --git a/shell/shell_common.c b/shell/shell_common.c index 70ecf2095..d1df5888c 100644 --- a/shell/shell_common.c +++ b/shell/shell_common.c | |||
@@ -26,19 +26,6 @@ const char defifsvar[] ALIGN1 = "IFS= \t\n\r"; | |||
26 | #endif | 26 | #endif |
27 | const char defoptindvar[] ALIGN1 = "OPTIND=1"; | 27 | const char defoptindvar[] ALIGN1 = "OPTIND=1"; |
28 | 28 | ||
29 | |||
30 | int FAST_FUNC is_well_formed_var_name(const char *s, char terminator) | ||
31 | { | ||
32 | if (!s || !(isalpha(*s) || *s == '_')) | ||
33 | return 0; | ||
34 | |||
35 | do | ||
36 | s++; | ||
37 | while (isalnum(*s) || *s == '_'); | ||
38 | |||
39 | return *s == terminator; | ||
40 | } | ||
41 | |||
42 | /* read builtin */ | 29 | /* read builtin */ |
43 | 30 | ||
44 | /* Needs to be interruptible: shell must handle traps and shell-special signals | 31 | /* Needs to be interruptible: shell must handle traps and shell-special signals |
@@ -76,7 +63,7 @@ shell_builtin_read(struct builtin_read_params *params) | |||
76 | argv = params->argv; | 63 | argv = params->argv; |
77 | pp = argv; | 64 | pp = argv; |
78 | while (*pp) { | 65 | while (*pp) { |
79 | if (!is_well_formed_var_name(*pp, '\0')) { | 66 | if (endofname(*pp)[0] != '\0') { |
80 | /* Mimic bash message */ | 67 | /* Mimic bash message */ |
81 | bb_error_msg("read: '%s': not a valid identifier", *pp); | 68 | bb_error_msg("read: '%s': not a valid identifier", *pp); |
82 | return (const char *)(uintptr_t)1; | 69 | return (const char *)(uintptr_t)1; |
@@ -384,99 +371,138 @@ shell_builtin_read(struct builtin_read_params *params) | |||
384 | struct limits { | 371 | struct limits { |
385 | uint8_t cmd; /* RLIMIT_xxx fit into it */ | 372 | uint8_t cmd; /* RLIMIT_xxx fit into it */ |
386 | uint8_t factor_shift; /* shift by to get rlim_{cur,max} values */ | 373 | uint8_t factor_shift; /* shift by to get rlim_{cur,max} values */ |
387 | char option; | ||
388 | const char *name; | 374 | const char *name; |
389 | }; | 375 | }; |
390 | 376 | ||
391 | static const struct limits limits_tbl[] = { | 377 | static const struct limits limits_tbl[] = { |
392 | #ifdef RLIMIT_FSIZE | 378 | { RLIMIT_CORE, 9, "core file size (blocks)" }, // -c |
393 | { RLIMIT_FSIZE, 9, 'f', "file size (blocks)" }, | 379 | { RLIMIT_DATA, 10, "data seg size (kb)" }, // -d |
380 | { RLIMIT_NICE, 0, "scheduling priority" }, // -e | ||
381 | { RLIMIT_FSIZE, 9, "file size (blocks)" }, // -f | ||
382 | #define LIMIT_F_IDX 3 | ||
383 | #ifdef RLIMIT_SIGPENDING | ||
384 | { RLIMIT_SIGPENDING, 0, "pending signals" }, // -i | ||
394 | #endif | 385 | #endif |
395 | #ifdef RLIMIT_CPU | 386 | #ifdef RLIMIT_MEMLOCK |
396 | { RLIMIT_CPU, 0, 't', "cpu time (seconds)" }, | 387 | { RLIMIT_MEMLOCK, 10, "max locked memory (kb)" }, // -l |
397 | #endif | 388 | #endif |
398 | #ifdef RLIMIT_DATA | 389 | #ifdef RLIMIT_RSS |
399 | { RLIMIT_DATA, 10, 'd', "data seg size (kb)" }, | 390 | { RLIMIT_RSS, 10, "max memory size (kb)" }, // -m |
400 | #endif | 391 | #endif |
401 | #ifdef RLIMIT_STACK | 392 | #ifdef RLIMIT_NOFILE |
402 | { RLIMIT_STACK, 10, 's', "stack size (kb)" }, | 393 | { RLIMIT_NOFILE, 0, "open files" }, // -n |
403 | #endif | 394 | #endif |
404 | #ifdef RLIMIT_CORE | 395 | #ifdef RLIMIT_MSGQUEUE |
405 | { RLIMIT_CORE, 9, 'c', "core file size (blocks)" }, | 396 | { RLIMIT_MSGQUEUE, 0, "POSIX message queues (bytes)" }, // -q |
406 | #endif | 397 | #endif |
407 | #ifdef RLIMIT_RSS | 398 | #ifdef RLIMIT_RTPRIO |
408 | { RLIMIT_RSS, 10, 'm', "resident set size (kb)" }, | 399 | { RLIMIT_RTPRIO, 0, "real-time priority" }, // -r |
409 | #endif | 400 | #endif |
410 | #ifdef RLIMIT_MEMLOCK | 401 | #ifdef RLIMIT_STACK |
411 | { RLIMIT_MEMLOCK, 10, 'l', "locked memory (kb)" }, | 402 | { RLIMIT_STACK, 10, "stack size (kb)" }, // -s |
412 | #endif | 403 | #endif |
413 | #ifdef RLIMIT_NPROC | 404 | #ifdef RLIMIT_CPU |
414 | { RLIMIT_NPROC, 0, 'p', "processes" }, | 405 | { RLIMIT_CPU, 0, "cpu time (seconds)" }, // -t |
415 | #endif | 406 | #endif |
416 | #ifdef RLIMIT_NOFILE | 407 | #ifdef RLIMIT_NPROC |
417 | { RLIMIT_NOFILE, 0, 'n', "file descriptors" }, | 408 | { RLIMIT_NPROC, 0, "max user processes" }, // -u |
418 | #endif | 409 | #endif |
419 | #ifdef RLIMIT_AS | 410 | #ifdef RLIMIT_AS |
420 | { RLIMIT_AS, 10, 'v', "address space (kb)" }, | 411 | { RLIMIT_AS, 10, "virtual memory (kb)" }, // -v |
421 | #endif | 412 | #endif |
422 | #ifdef RLIMIT_LOCKS | 413 | #ifdef RLIMIT_LOCKS |
423 | { RLIMIT_LOCKS, 0, 'w', "locks" }, | 414 | { RLIMIT_LOCKS, 0, "file locks" }, // -x |
415 | #endif | ||
416 | }; | ||
417 | // bash also shows: | ||
418 | //pipe size (512 bytes, -p) 8 | ||
419 | |||
420 | static const char limit_chars[] ALIGN1 = | ||
421 | "c" | ||
422 | "d" | ||
423 | "e" | ||
424 | "f" | ||
425 | #ifdef RLIMIT_SIGPENDING | ||
426 | "i" | ||
427 | #endif | ||
428 | #ifdef RLIMIT_MEMLOCK | ||
429 | "l" | ||
430 | #endif | ||
431 | #ifdef RLIMIT_RSS | ||
432 | "m" | ||
433 | #endif | ||
434 | #ifdef RLIMIT_NOFILE | ||
435 | "n" | ||
424 | #endif | 436 | #endif |
425 | #ifdef RLIMIT_NICE | 437 | #ifdef RLIMIT_MSGQUEUE |
426 | { RLIMIT_NICE, 0, 'e', "scheduling priority" }, | 438 | "q" |
427 | #endif | 439 | #endif |
428 | #ifdef RLIMIT_RTPRIO | 440 | #ifdef RLIMIT_RTPRIO |
429 | { RLIMIT_RTPRIO, 0, 'r', "real-time priority" }, | 441 | "r" |
430 | #endif | 442 | #endif |
431 | }; | 443 | #ifdef RLIMIT_STACK |
432 | 444 | "s" | |
433 | enum { | ||
434 | OPT_hard = (1 << 0), | ||
435 | OPT_soft = (1 << 1), | ||
436 | }; | ||
437 | |||
438 | /* "-": treat args as parameters of option with ASCII code 1 */ | ||
439 | static const char ulimit_opt_string[] ALIGN1 = "-HSa" | ||
440 | #ifdef RLIMIT_FSIZE | ||
441 | "f::" | ||
442 | #endif | 445 | #endif |
443 | #ifdef RLIMIT_CPU | 446 | #ifdef RLIMIT_CPU |
444 | "t::" | 447 | "t" |
445 | #endif | 448 | #endif |
446 | #ifdef RLIMIT_DATA | 449 | #ifdef RLIMIT_NPROC |
447 | "d::" | 450 | "u" |
448 | #endif | 451 | #endif |
449 | #ifdef RLIMIT_STACK | 452 | #ifdef RLIMIT_AS |
450 | "s::" | 453 | "v" |
451 | #endif | 454 | #endif |
452 | #ifdef RLIMIT_CORE | 455 | #ifdef RLIMIT_LOCKS |
453 | "c::" | 456 | "x" |
454 | #endif | 457 | #endif |
455 | #ifdef RLIMIT_RSS | 458 | ; |
456 | "m::" | 459 | |
460 | /* "-": treat args as parameters of option with ASCII code 1 */ | ||
461 | static const char ulimit_opt_string[] ALIGN1 = "-HSa" | ||
462 | "c::" | ||
463 | "d::" | ||
464 | "e::" | ||
465 | "f::" | ||
466 | #ifdef RLIMIT_SIGPENDING | ||
467 | "i::" | ||
457 | #endif | 468 | #endif |
458 | #ifdef RLIMIT_MEMLOCK | 469 | #ifdef RLIMIT_MEMLOCK |
459 | "l::" | 470 | "l::" |
460 | #endif | 471 | #endif |
461 | #ifdef RLIMIT_NPROC | 472 | #ifdef RLIMIT_RSS |
462 | "p::" | 473 | "m::" |
463 | #endif | 474 | #endif |
464 | #ifdef RLIMIT_NOFILE | 475 | #ifdef RLIMIT_NOFILE |
465 | "n::" | 476 | "n::" |
466 | #endif | 477 | #endif |
478 | #ifdef RLIMIT_MSGQUEUE | ||
479 | "q::" | ||
480 | #endif | ||
481 | #ifdef RLIMIT_RTPRIO | ||
482 | "r::" | ||
483 | #endif | ||
484 | #ifdef RLIMIT_STACK | ||
485 | "s::" | ||
486 | #endif | ||
487 | #ifdef RLIMIT_CPU | ||
488 | "t::" | ||
489 | #endif | ||
490 | #ifdef RLIMIT_NPROC | ||
491 | "u::" | ||
492 | #endif | ||
467 | #ifdef RLIMIT_AS | 493 | #ifdef RLIMIT_AS |
468 | "v::" | 494 | "v::" |
469 | #endif | 495 | #endif |
470 | #ifdef RLIMIT_LOCKS | 496 | #ifdef RLIMIT_LOCKS |
471 | "w::" | 497 | "x::" |
472 | #endif | ||
473 | #ifdef RLIMIT_NICE | ||
474 | "e::" | ||
475 | #endif | ||
476 | #ifdef RLIMIT_RTPRIO | ||
477 | "r::" | ||
478 | #endif | 498 | #endif |
479 | ; | 499 | ; |
500 | |||
501 | enum { | ||
502 | OPT_hard = (1 << 0), | ||
503 | OPT_soft = (1 << 1), | ||
504 | OPT_all = (1 << 2), | ||
505 | }; | ||
480 | 506 | ||
481 | static void printlim(unsigned opts, const struct rlimit *limit, | 507 | static void printlim(unsigned opts, const struct rlimit *limit, |
482 | const struct limits *l) | 508 | const struct limits *l) |
@@ -484,7 +510,7 @@ static void printlim(unsigned opts, const struct rlimit *limit, | |||
484 | rlim_t val; | 510 | rlim_t val; |
485 | 511 | ||
486 | val = limit->rlim_max; | 512 | val = limit->rlim_max; |
487 | if (!(opts & OPT_hard)) | 513 | if (opts & OPT_soft) |
488 | val = limit->rlim_cur; | 514 | val = limit->rlim_cur; |
489 | 515 | ||
490 | if (val == RLIM_INFINITY) | 516 | if (val == RLIM_INFINITY) |
@@ -498,8 +524,11 @@ static void printlim(unsigned opts, const struct rlimit *limit, | |||
498 | int FAST_FUNC | 524 | int FAST_FUNC |
499 | shell_builtin_ulimit(char **argv) | 525 | shell_builtin_ulimit(char **argv) |
500 | { | 526 | { |
527 | struct rlimit limit; | ||
528 | unsigned opt_cnt; | ||
501 | unsigned opts; | 529 | unsigned opts; |
502 | unsigned argc; | 530 | unsigned argc; |
531 | unsigned i; | ||
503 | 532 | ||
504 | /* We can't use getopt32: need to handle commands like | 533 | /* We can't use getopt32: need to handle commands like |
505 | * ulimit 123 -c2 -l 456 | 534 | * ulimit 123 -c2 -l 456 |
@@ -510,12 +539,48 @@ shell_builtin_ulimit(char **argv) | |||
510 | */ | 539 | */ |
511 | GETOPT_RESET(); | 540 | GETOPT_RESET(); |
512 | 541 | ||
542 | // bash 4.4.23: | ||
543 | // | ||
544 | // -H and/or -S change meaning even of options *before* them: ulimit -f 2000 -H | ||
545 | // sets hard limit, ulimit -a -H prints hard limits. | ||
546 | // | ||
547 | // -a is equivalent for requesting all limits to be shown. | ||
548 | // | ||
549 | // If -a is specified, attempts to set limits are ignored: | ||
550 | // ulimit -m 1000; ulimit -m 2000 -a | ||
551 | // shows 1000, not 2000. HOWEVER, *implicit* -f form "ulimit 2000 -a" | ||
552 | // DOES set -f limit [we don't implement this quirk], "ulimit -a 2000" does not. | ||
553 | // Options are still parsed: ulimit -az complains about unknown -z opt. | ||
554 | // | ||
555 | // -a is not cumulative: "ulimit -a -a" = "ulimit -a -f -m" = "ulimit -a" | ||
556 | // | ||
557 | // -HSa can be combined in one argument and with one other option (example: -Sm), | ||
558 | // but other options can't: limit value is an optional argument, | ||
559 | // thus "-mf" means "-m f", f is the parameter of -m. | ||
560 | // | ||
561 | // Limit can be set and then printed: ulimit -m 2000 -m | ||
562 | // If set more than once, they are set and printed in order: | ||
563 | // try ulimit -m -m 1000 -m -m 2000 -m -m 3000 -m | ||
564 | // | ||
565 | // Limits are shown in the order of options given: | ||
566 | // ulimit -m -f is not the same as ulimit -f -m. | ||
567 | // | ||
568 | // If both -S and -H are given, show soft limit. | ||
569 | // | ||
570 | // Short printout (limit value only) is printed only if just one option | ||
571 | // is given: ulimit -m. ulimit -f -m prints verbose lines. | ||
572 | // ulimit -f -f prints same verbose line twice. | ||
573 | // ulimit -m 10000 -f prints verbose line for -f. | ||
574 | |||
513 | argc = string_array_len(argv); | 575 | argc = string_array_len(argv); |
514 | 576 | ||
577 | /* First pass over options: detect -H/-S/-a status, | ||
578 | * and "bare ulimit" and "only one option" cases | ||
579 | * by counting other opts. | ||
580 | */ | ||
581 | opt_cnt = 0; | ||
515 | opts = 0; | 582 | opts = 0; |
516 | while (1) { | 583 | while (1) { |
517 | struct rlimit limit; | ||
518 | const struct limits *l; | ||
519 | int opt_char = getopt(argc, argv, ulimit_opt_string); | 584 | int opt_char = getopt(argc, argv, ulimit_opt_string); |
520 | 585 | ||
521 | if (opt_char == -1) | 586 | if (opt_char == -1) |
@@ -528,74 +593,94 @@ shell_builtin_ulimit(char **argv) | |||
528 | opts |= OPT_soft; | 593 | opts |= OPT_soft; |
529 | continue; | 594 | continue; |
530 | } | 595 | } |
531 | |||
532 | if (opt_char == 'a') { | 596 | if (opt_char == 'a') { |
533 | for (l = limits_tbl; l != &limits_tbl[ARRAY_SIZE(limits_tbl)]; l++) { | 597 | opts |= OPT_all; |
534 | getrlimit(l->cmd, &limit); | ||
535 | printf("-%c: %-30s ", l->option, l->name); | ||
536 | printlim(opts, &limit, l); | ||
537 | } | ||
538 | continue; | 598 | continue; |
539 | } | 599 | } |
600 | if (opt_char == '?') { | ||
601 | /* bad option. getopt already complained. */ | ||
602 | return EXIT_FAILURE; | ||
603 | } | ||
604 | opt_cnt++; | ||
605 | } /* while (there are options) */ | ||
606 | |||
607 | if (!(opts & (OPT_hard | OPT_soft))) | ||
608 | opts |= (OPT_hard | OPT_soft); | ||
609 | if (opts & OPT_all) { | ||
610 | for (i = 0; i < ARRAY_SIZE(limits_tbl); i++) { | ||
611 | getrlimit(limits_tbl[i].cmd, &limit); | ||
612 | printf("%-32s(-%c) ", limits_tbl[i].name, limit_chars[i]); | ||
613 | printlim(opts, &limit, &limits_tbl[i]); | ||
614 | } | ||
615 | return EXIT_SUCCESS; | ||
616 | } | ||
540 | 617 | ||
541 | if (opt_char == 1) | 618 | /* Second pass: set or print limits, in order */ |
619 | GETOPT_RESET(); | ||
620 | while (1) { | ||
621 | char *val_str; | ||
622 | int opt_char = getopt(argc, argv, ulimit_opt_string); | ||
623 | |||
624 | if (opt_char == -1) | ||
625 | break; | ||
626 | if (opt_char == 'H') | ||
627 | continue; | ||
628 | if (opt_char == 'S') | ||
629 | continue; | ||
630 | //if (opt_char == 'a') - impossible | ||
631 | |||
632 | if (opt_char == 1) /* if "ulimit NNN", -f is assumed */ | ||
542 | opt_char = 'f'; | 633 | opt_char = 'f'; |
543 | for (l = limits_tbl; l != &limits_tbl[ARRAY_SIZE(limits_tbl)]; l++) { | 634 | i = strchrnul(limit_chars, opt_char) - limit_chars; |
544 | if (opt_char == l->option) { | 635 | //if (i >= ARRAY_SIZE(limits_tbl)) - bad option, impossible |
545 | char *val_str; | 636 | |
546 | 637 | val_str = optarg; | |
547 | getrlimit(l->cmd, &limit); | 638 | if (!val_str && argv[optind] && argv[optind][0] != '-') |
548 | 639 | val_str = argv[optind++]; /* ++ skips NN in "-c NN" case */ | |
549 | val_str = optarg; | 640 | |
550 | if (!val_str && argv[optind] && argv[optind][0] != '-') | 641 | getrlimit(limits_tbl[i].cmd, &limit); |
551 | val_str = argv[optind++]; /* ++ skips NN in "-c NN" case */ | 642 | if (!val_str) { |
552 | if (val_str) { | 643 | if (opt_cnt > 1) |
553 | rlim_t val; | 644 | printf("%-32s(-%c) ", limits_tbl[i].name, limit_chars[i]); |
554 | 645 | printlim(opts, &limit, &limits_tbl[i]); | |
555 | if (strcmp(val_str, "unlimited") == 0) | 646 | } else { |
556 | val = RLIM_INFINITY; | 647 | rlim_t val = RLIM_INFINITY; |
557 | else { | 648 | if (strcmp(val_str, "unlimited") != 0) { |
558 | if (sizeof(val) == sizeof(int)) | 649 | if (sizeof(val) == sizeof(int)) |
559 | val = bb_strtou(val_str, NULL, 10); | 650 | val = bb_strtou(val_str, NULL, 10); |
560 | else if (sizeof(val) == sizeof(long)) | 651 | else if (sizeof(val) == sizeof(long)) |
561 | val = bb_strtoul(val_str, NULL, 10); | 652 | val = bb_strtoul(val_str, NULL, 10); |
562 | else | 653 | else |
563 | val = bb_strtoull(val_str, NULL, 10); | 654 | val = bb_strtoull(val_str, NULL, 10); |
564 | if (errno) { | 655 | if (errno) { |
565 | bb_error_msg("invalid number '%s'", val_str); | 656 | bb_error_msg("invalid number '%s'", val_str); |
566 | return EXIT_FAILURE; | 657 | return EXIT_FAILURE; |
567 | } | ||
568 | val <<= l->factor_shift; | ||
569 | } | ||
570 | //bb_error_msg("opt %c val_str:'%s' val:%lld", opt_char, val_str, (long long)val); | ||
571 | /* from man bash: "If neither -H nor -S | ||
572 | * is specified, both the soft and hard | ||
573 | * limits are set. */ | ||
574 | if (!opts) | ||
575 | opts = OPT_hard + OPT_soft; | ||
576 | if (opts & OPT_hard) | ||
577 | limit.rlim_max = val; | ||
578 | if (opts & OPT_soft) | ||
579 | limit.rlim_cur = val; | ||
580 | //bb_error_msg("setrlimit(%d, %lld, %lld)", l->cmd, (long long)limit.rlim_cur, (long long)limit.rlim_max); | ||
581 | if (setrlimit(l->cmd, &limit) < 0) { | ||
582 | bb_perror_msg("error setting limit"); | ||
583 | return EXIT_FAILURE; | ||
584 | } | ||
585 | } else { | ||
586 | printlim(opts, &limit, l); | ||
587 | } | 658 | } |
588 | break; | 659 | val <<= limits_tbl[i].factor_shift; |
660 | } | ||
661 | //bb_error_msg("opt %c val_str:'%s' val:%lld", opt_char, val_str, (long long)val); | ||
662 | /* from man bash: "If neither -H nor -S | ||
663 | * is specified, both the soft and hard | ||
664 | * limits are set. */ | ||
665 | if (opts & OPT_hard) | ||
666 | limit.rlim_max = val; | ||
667 | if (opts & OPT_soft) | ||
668 | limit.rlim_cur = val; | ||
669 | //bb_error_msg("setrlimit(%d, %lld, %lld)", limits_tbl[i].cmd, (long long)limit.rlim_cur, (long long)limit.rlim_max); | ||
670 | if (setrlimit(limits_tbl[i].cmd, &limit) < 0) { | ||
671 | bb_perror_msg("error setting limit"); | ||
672 | return EXIT_FAILURE; | ||
589 | } | 673 | } |
590 | } /* for (every possible opt) */ | ||
591 | |||
592 | if (l == &limits_tbl[ARRAY_SIZE(limits_tbl)]) { | ||
593 | /* bad option. getopt already complained. */ | ||
594 | break; | ||
595 | } | 674 | } |
596 | } /* while (there are options) */ | 675 | } /* while (there are options) */ |
597 | 676 | ||
598 | return 0; | 677 | if (opt_cnt == 0) { |
678 | /* "bare ulimit": treat it as if it was -f */ | ||
679 | getrlimit(limits_tbl[LIMIT_F_IDX].cmd, &limit); | ||
680 | printlim(opts, &limit, &limits_tbl[LIMIT_F_IDX]); | ||
681 | } | ||
682 | |||
683 | return EXIT_SUCCESS; | ||
599 | } | 684 | } |
600 | #else | 685 | #else |
601 | int FAST_FUNC shell_builtin_ulimit(char **argv UNUSED_PARAM) | 686 | int FAST_FUNC shell_builtin_ulimit(char **argv UNUSED_PARAM) |