diff options
Diffstat (limited to 'shell')
| -rw-r--r-- | shell/hush.c | 139 |
1 files changed, 129 insertions, 10 deletions
diff --git a/shell/hush.c b/shell/hush.c index 5203f2eef..17673c0cb 100644 --- a/shell/hush.c +++ b/shell/hush.c | |||
| @@ -489,6 +489,10 @@ struct globals { | |||
| 489 | #endif | 489 | #endif |
| 490 | unsigned char charmap[256]; | 490 | unsigned char charmap[256]; |
| 491 | char user_input_buf[ENABLE_FEATURE_EDITING ? BUFSIZ : 2]; | 491 | char user_input_buf[ENABLE_FEATURE_EDITING ? BUFSIZ : 2]; |
| 492 | struct { | ||
| 493 | char *cmd; | ||
| 494 | struct sigaction oact; | ||
| 495 | } *traps; | ||
| 492 | }; | 496 | }; |
| 493 | 497 | ||
| 494 | #define G (*ptr_to_globals) | 498 | #define G (*ptr_to_globals) |
| @@ -517,6 +521,8 @@ static int builtin_help(char **argv); | |||
| 517 | static int builtin_pwd(char **argv); | 521 | static int builtin_pwd(char **argv); |
| 518 | static int builtin_read(char **argv); | 522 | static int builtin_read(char **argv); |
| 519 | static int builtin_test(char **argv); | 523 | static int builtin_test(char **argv); |
| 524 | static void handle_trap(int sig); | ||
| 525 | static int builtin_trap(char **argv); | ||
| 520 | static int builtin_true(char **argv); | 526 | static int builtin_true(char **argv); |
| 521 | static int builtin_set(char **argv); | 527 | static int builtin_set(char **argv); |
| 522 | static int builtin_set_mode(const char, const char); | 528 | static int builtin_set_mode(const char, const char); |
| @@ -578,8 +584,8 @@ static const struct built_in_command bltins[] = { | |||
| 578 | // BLTIN("return", builtin_not_written, "Return from a function"), | 584 | // BLTIN("return", builtin_not_written, "Return from a function"), |
| 579 | BLTIN("set" , builtin_set, "Set/unset shell local variables"), | 585 | BLTIN("set" , builtin_set, "Set/unset shell local variables"), |
| 580 | BLTIN("shift" , builtin_shift, "Shift positional parameters"), | 586 | BLTIN("shift" , builtin_shift, "Shift positional parameters"), |
| 581 | // BLTIN("trap" , builtin_not_written, "Trap signals"), | ||
| 582 | BLTIN("test" , builtin_test, "Test condition"), | 587 | BLTIN("test" , builtin_test, "Test condition"), |
| 588 | BLTIN("trap" , builtin_trap, "Trap signals"), | ||
| 583 | // BLTIN("ulimit", builtin_not_written, "Control resource limits"), | 589 | // BLTIN("ulimit", builtin_not_written, "Control resource limits"), |
| 584 | BLTIN("umask" , builtin_umask, "Set file creation mask"), | 590 | BLTIN("umask" , builtin_umask, "Set file creation mask"), |
| 585 | BLTIN("unset" , builtin_unset, "Unset environment variable"), | 591 | BLTIN("unset" , builtin_unset, "Unset environment variable"), |
| @@ -857,22 +863,27 @@ static void sigexit(int sig) | |||
| 857 | kill_myself_with_sig(sig); /* does not return */ | 863 | kill_myself_with_sig(sig); /* does not return */ |
| 858 | } | 864 | } |
| 859 | 865 | ||
| 860 | /* Restores tty foreground process group, and exits. */ | ||
| 861 | static void hush_exit(int exitcode) NORETURN; | ||
| 862 | static void hush_exit(int exitcode) | ||
| 863 | { | ||
| 864 | fflush(NULL); /* flush all streams */ | ||
| 865 | sigexit(- (exitcode & 0xff)); | ||
| 866 | } | ||
| 867 | |||
| 868 | #else /* !JOB */ | 866 | #else /* !JOB */ |
| 869 | 867 | ||
| 870 | #define set_fatal_sighandler(handler) ((void)0) | 868 | #define set_fatal_sighandler(handler) ((void)0) |
| 871 | #define set_jobctrl_sighandler(handler) ((void)0) | 869 | #define set_jobctrl_sighandler(handler) ((void)0) |
| 872 | #define hush_exit(e) exit(e) | ||
| 873 | 870 | ||
| 874 | #endif /* JOB */ | 871 | #endif /* JOB */ |
| 875 | 872 | ||
| 873 | /* Restores tty foreground process group, and exits. */ | ||
| 874 | static void hush_exit(int exitcode) NORETURN; | ||
| 875 | static void hush_exit(int exitcode) | ||
| 876 | { | ||
| 877 | if (G.traps && G.traps[0].cmd) | ||
| 878 | handle_trap(0); | ||
| 879 | |||
| 880 | if (ENABLE_HUSH_JOB) { | ||
| 881 | fflush(NULL); /* flush all streams */ | ||
| 882 | sigexit(- (exitcode & 0xff)); | ||
| 883 | } else | ||
| 884 | exit(exitcode); | ||
| 885 | } | ||
| 886 | |||
| 876 | 887 | ||
| 877 | static const char *set_cwd(void) | 888 | static const char *set_cwd(void) |
| 878 | { | 889 | { |
| @@ -4516,6 +4527,114 @@ int lash_main(int argc, char **argv) | |||
| 4516 | /* | 4527 | /* |
| 4517 | * Built-ins | 4528 | * Built-ins |
| 4518 | */ | 4529 | */ |
| 4530 | /* http://www.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_28 | ||
| 4531 | * | ||
| 4532 | * Traps are also evaluated immediately instead of being delayed properly: | ||
| 4533 | * http://www.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_11 | ||
| 4534 | * Example: hush -c 'trap "echo hi" 31; sleep 10; echo moo' & sleep 1; kill -31 $! | ||
| 4535 | * "hi" should not be displayed until the sleep finishes | ||
| 4536 | * This will have to get fixed ... | ||
| 4537 | */ | ||
| 4538 | static void handle_trap(int sig) | ||
| 4539 | { | ||
| 4540 | int save_errno, save_rcode; | ||
| 4541 | char *argv[] = { NULL, G.traps[sig].cmd, NULL }; | ||
| 4542 | /* Race! We transitioned from handled to ignore/default, but | ||
| 4543 | * the signal came in after updating .cmd but before we could | ||
| 4544 | * register the new signal handler. | ||
| 4545 | */ | ||
| 4546 | if (!argv[1] || argv[1][0] == '\0') | ||
| 4547 | return; | ||
| 4548 | /* need to save/restore errno/$? across traps */ | ||
| 4549 | save_errno = errno; | ||
| 4550 | save_rcode = G.last_return_code; | ||
| 4551 | builtin_eval(argv); | ||
| 4552 | errno = save_errno; | ||
| 4553 | G.last_return_code = save_rcode; | ||
| 4554 | } | ||
| 4555 | static int builtin_trap(char **argv) | ||
| 4556 | { | ||
| 4557 | size_t i; | ||
| 4558 | int sig; | ||
| 4559 | bool ign = false; | ||
| 4560 | char *new_cmd = NULL; | ||
| 4561 | |||
| 4562 | if (!G.traps) | ||
| 4563 | G.traps = xzalloc(sizeof(*G.traps) * NSIG); | ||
| 4564 | |||
| 4565 | if (!argv[1]) { | ||
| 4566 | /* No args: print all trapped. This isn't 100% correct as we should | ||
| 4567 | * be escaping the cmd so that it can be pasted back in ... | ||
| 4568 | */ | ||
| 4569 | for (i = 0; i < NSIG; ++i) | ||
| 4570 | if (G.traps[i].cmd) | ||
| 4571 | printf("trap -- '%s' %s\n", G.traps[i].cmd, get_signame(i)); | ||
| 4572 | return EXIT_SUCCESS; | ||
| 4573 | } | ||
| 4574 | |||
| 4575 | /* first arg is decimal: reset all specified */ | ||
| 4576 | sig = bb_strtou(argv[1], NULL, 10); | ||
| 4577 | if (errno == 0) { | ||
| 4578 | int ret; | ||
| 4579 | i = 0; | ||
| 4580 | set_all: | ||
| 4581 | ret = EXIT_SUCCESS; | ||
| 4582 | while (argv[++i]) { | ||
| 4583 | char *old_cmd; | ||
| 4584 | |||
| 4585 | sig = get_signum(argv[i]); | ||
| 4586 | if (sig < 0 || sig >= NSIG) { | ||
| 4587 | ret = EXIT_FAILURE; | ||
| 4588 | bb_perror_msg("trap: %s: invalid signal specification", argv[i]); | ||
| 4589 | continue; | ||
| 4590 | } | ||
| 4591 | |||
| 4592 | /* Make sure .cmd is always a valid command list since | ||
| 4593 | * signals can occur at any time ... | ||
| 4594 | */ | ||
| 4595 | old_cmd = G.traps[sig].cmd; | ||
| 4596 | G.traps[sig].cmd = xstrdup(new_cmd); | ||
| 4597 | free(old_cmd); | ||
| 4598 | |||
| 4599 | debug_printf("trap: setting SIG%s (%i) to: %s", | ||
| 4600 | get_signame(sig), sig, G.traps[sig].cmd); | ||
| 4601 | |||
| 4602 | /* There is no signal for 0 (EXIT) */ | ||
| 4603 | if (sig == 0) | ||
| 4604 | continue; | ||
| 4605 | |||
| 4606 | if (new_cmd) { | ||
| 4607 | /* add/update a handler */ | ||
| 4608 | struct sigaction act = { | ||
| 4609 | .sa_handler = ign ? SIG_IGN : handle_trap, | ||
| 4610 | .sa_flags = SA_RESTART, | ||
| 4611 | }; | ||
| 4612 | sigemptyset(&act.sa_mask); | ||
| 4613 | sigaction(sig, &act, old_cmd ? NULL : &G.traps[sig].oact); | ||
| 4614 | } else if (old_cmd && !new_cmd) | ||
| 4615 | /* there was a handler, and we are removing it */ | ||
| 4616 | sigaction_set(sig, &G.traps[sig].oact); | ||
| 4617 | } | ||
| 4618 | return ret; | ||
| 4619 | } | ||
| 4620 | |||
| 4621 | /* first arg is "-": reset all specified to default */ | ||
| 4622 | /* first arg is "": ignore all specified */ | ||
| 4623 | /* everything else: execute first arg upon signal */ | ||
| 4624 | if (!argv[2]) { | ||
| 4625 | bb_error_msg("trap: invalid arguments"); | ||
| 4626 | return EXIT_FAILURE; | ||
| 4627 | } | ||
| 4628 | if (LONE_DASH(argv[1])) | ||
| 4629 | /* nothing! */; | ||
| 4630 | else | ||
| 4631 | new_cmd = argv[1]; | ||
| 4632 | if (argv[1][0] == '\0') | ||
| 4633 | ign = true; | ||
| 4634 | i = 1; | ||
| 4635 | goto set_all; | ||
| 4636 | } | ||
| 4637 | |||
| 4519 | static int builtin_true(char **argv UNUSED_PARAM) | 4638 | static int builtin_true(char **argv UNUSED_PARAM) |
| 4520 | { | 4639 | { |
| 4521 | return 0; | 4640 | return 0; |
