diff options
author | Mike Frysinger <vapier@gentoo.org> | 2009-03-29 23:49:37 +0000 |
---|---|---|
committer | Mike Frysinger <vapier@gentoo.org> | 2009-03-29 23:49:37 +0000 |
commit | 9f8128f48005da550e32f38e97ce349ba36faf8b (patch) | |
tree | 194be90516b34ee890219fc343d111c5a977840f | |
parent | 0d907eab6cd424963bf8b788710e7a68d50d0511 (diff) | |
download | busybox-w32-9f8128f48005da550e32f38e97ce349ba36faf8b.tar.gz busybox-w32-9f8128f48005da550e32f38e97ce349ba36faf8b.tar.bz2 busybox-w32-9f8128f48005da550e32f38e97ce349ba36faf8b.zip |
first pass at `trap` support in hush
-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; |