diff options
| author | Denys Vlasenko <vda.linux@googlemail.com> | 2009-09-25 14:21:06 +0200 |
|---|---|---|
| committer | Denys Vlasenko <vda.linux@googlemail.com> | 2009-09-25 14:21:06 +0200 |
| commit | 67f7186403dec3f61f6ea9e9a3f555bd0b0541bc (patch) | |
| tree | 2cc885ab0edebf17070b88aaaf56899814ed4744 /shell | |
| parent | 4ea0ca81934ad62887632fa53d093b0048ea7e03 (diff) | |
| download | busybox-w32-67f7186403dec3f61f6ea9e9a3f555bd0b0541bc.tar.gz busybox-w32-67f7186403dec3f61f6ea9e9a3f555bd0b0541bc.tar.bz2 busybox-w32-67f7186403dec3f61f6ea9e9a3f555bd0b0541bc.zip | |
hush: do not reset to default "" traps in subshell
function old new delta
reset_traps_to_defaults 164 211 +47
builtin_umask 123 121 -2
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
Diffstat (limited to 'shell')
| -rw-r--r-- | shell/hush.c | 41 | ||||
| -rw-r--r-- | shell/hush_test/hush-trap/subshell.right | 6 | ||||
| -rwxr-xr-x | shell/hush_test/hush-trap/subshell.tests | 20 |
3 files changed, 49 insertions, 18 deletions
diff --git a/shell/hush.c b/shell/hush.c index 00daf3ddd..d75b0da7e 100644 --- a/shell/hush.c +++ b/shell/hush.c | |||
| @@ -1112,13 +1112,10 @@ static void restore_G_args(save_arg_t *sv, char **argv) | |||
| 1112 | * are to count SIGCHLDs | 1112 | * are to count SIGCHLDs |
| 1113 | * and to restore tty pgrp on signal-induced exit. | 1113 | * and to restore tty pgrp on signal-induced exit. |
| 1114 | * | 1114 | * |
| 1115 | * TODO compat: | 1115 | * Note 2 (compat): |
| 1116 | * Standard says "When a subshell is entered, traps that are not being ignored | 1116 | * Standard says "When a subshell is entered, traps that are not being ignored |
| 1117 | * are set to the default actions". bash interprets it so that traps which | 1117 | * are set to the default actions". bash interprets it so that traps which |
| 1118 | * are set to "" (ignore) are NOT reset to defaults. We reset them to default. | 1118 | * are set to "" (ignore) are NOT reset to defaults. We do the same. |
| 1119 | * bash example: | ||
| 1120 | * # trap '' SYS; (bash -c 'kill -SYS $PPID'; echo YES) | ||
| 1121 | * YES <-- subshell was not killed by SIGSYS | ||
| 1122 | */ | 1119 | */ |
| 1123 | enum { | 1120 | enum { |
| 1124 | SPECIAL_INTERACTIVE_SIGS = 0 | 1121 | SPECIAL_INTERACTIVE_SIGS = 0 |
| @@ -2605,43 +2602,51 @@ static void reset_traps_to_defaults(void) | |||
| 2605 | { | 2602 | { |
| 2606 | /* This function is always called in a child shell | 2603 | /* This function is always called in a child shell |
| 2607 | * after fork (not vfork, NOMMU doesn't use this function). | 2604 | * after fork (not vfork, NOMMU doesn't use this function). |
| 2608 | * Child shells are not interactive. | ||
| 2609 | * SIGTTIN/SIGTTOU/SIGTSTP should not have special handling. | ||
| 2610 | * Testcase: (while :; do :; done) + ^Z should background. | ||
| 2611 | * Same goes for SIGTERM, SIGHUP, SIGINT. | ||
| 2612 | */ | 2605 | */ |
| 2613 | unsigned sig; | 2606 | unsigned sig; |
| 2614 | unsigned mask; | 2607 | unsigned mask; |
| 2615 | 2608 | ||
| 2609 | /* Child shells are not interactive. | ||
| 2610 | * SIGTTIN/SIGTTOU/SIGTSTP should not have special handling. | ||
| 2611 | * Testcase: (while :; do :; done) + ^Z should background. | ||
| 2612 | * Same goes for SIGTERM, SIGHUP, SIGINT. | ||
| 2613 | */ | ||
| 2616 | if (!G.traps && !(G.non_DFL_mask & SPECIAL_INTERACTIVE_SIGS)) | 2614 | if (!G.traps && !(G.non_DFL_mask & SPECIAL_INTERACTIVE_SIGS)) |
| 2617 | return; | 2615 | return; /* already no traps and no SPECIAL_INTERACTIVE_SIGS */ |
| 2618 | 2616 | ||
| 2619 | /* Stupid. It can be done with *single* &= op, but we can't use | 2617 | /* Switching off SPECIAL_INTERACTIVE_SIGS. |
| 2620 | * the fact that G.blocked_set is implemented as a bitmask... */ | 2618 | * Stupid. It can be done with *single* &= op, but we can't use |
| 2619 | * the fact that G.blocked_set is implemented as a bitmask | ||
| 2620 | * in libc... */ | ||
| 2621 | mask = (SPECIAL_INTERACTIVE_SIGS >> 1); | 2621 | mask = (SPECIAL_INTERACTIVE_SIGS >> 1); |
| 2622 | sig = 1; | 2622 | sig = 1; |
| 2623 | while (1) { | 2623 | while (1) { |
| 2624 | if (mask & 1) | 2624 | if (mask & 1) { |
| 2625 | sigdelset(&G.blocked_set, sig); | 2625 | /* Careful. Only if no trap or trap is not "" */ |
| 2626 | if (!G.traps || !G.traps[sig] || G.traps[sig][0]) | ||
| 2627 | sigdelset(&G.blocked_set, sig); | ||
| 2628 | } | ||
| 2626 | mask >>= 1; | 2629 | mask >>= 1; |
| 2627 | if (!mask) | 2630 | if (!mask) |
| 2628 | break; | 2631 | break; |
| 2629 | sig++; | 2632 | sig++; |
| 2630 | } | 2633 | } |
| 2631 | 2634 | /* Our homegrown sig mask is saner to work with :) */ | |
| 2632 | G.non_DFL_mask &= ~SPECIAL_INTERACTIVE_SIGS; | 2635 | G.non_DFL_mask &= ~SPECIAL_INTERACTIVE_SIGS; |
| 2636 | |||
| 2637 | /* Resetting all traps to default except empty ones */ | ||
| 2633 | mask = G.non_DFL_mask; | 2638 | mask = G.non_DFL_mask; |
| 2634 | if (G.traps) for (sig = 0; sig < NSIG; sig++, mask >>= 1) { | 2639 | if (G.traps) for (sig = 0; sig < NSIG; sig++, mask >>= 1) { |
| 2635 | if (!G.traps[sig]) | 2640 | if (!G.traps[sig] || !G.traps[sig][0]) |
| 2636 | continue; | 2641 | continue; |
| 2637 | free(G.traps[sig]); | 2642 | free(G.traps[sig]); |
| 2638 | G.traps[sig] = NULL; | 2643 | G.traps[sig] = NULL; |
| 2639 | /* There is no signal for 0 (EXIT) */ | 2644 | /* There is no signal for 0 (EXIT) */ |
| 2640 | if (sig == 0) | 2645 | if (sig == 0) |
| 2641 | continue; | 2646 | continue; |
| 2642 | /* There was a trap handler, we are removing it. | 2647 | /* There was a trap handler, we just removed it. |
| 2643 | * But if sig still has non-DFL handling, | 2648 | * But if sig still has non-DFL handling, |
| 2644 | * we should not unblock it. */ | 2649 | * we should not unblock the sig. */ |
| 2645 | if (mask & 1) | 2650 | if (mask & 1) |
| 2646 | continue; | 2651 | continue; |
| 2647 | sigdelset(&G.blocked_set, sig); | 2652 | sigdelset(&G.blocked_set, sig); |
diff --git a/shell/hush_test/hush-trap/subshell.right b/shell/hush_test/hush-trap/subshell.right new file mode 100644 index 000000000..0d20ed4e9 --- /dev/null +++ b/shell/hush_test/hush-trap/subshell.right | |||
| @@ -0,0 +1,6 @@ | |||
| 1 | Ok | ||
| 2 | Ok | ||
| 3 | Ok | ||
| 4 | Ok | ||
| 5 | TERM | ||
| 6 | Done | ||
diff --git a/shell/hush_test/hush-trap/subshell.tests b/shell/hush_test/hush-trap/subshell.tests new file mode 100755 index 000000000..b5d6094d6 --- /dev/null +++ b/shell/hush_test/hush-trap/subshell.tests | |||
| @@ -0,0 +1,20 @@ | |||
| 1 | # Non-empty traps should be reset in subshell | ||
| 2 | |||
| 3 | # HUP is special in interactive shells | ||
| 4 | trap '' HUP | ||
| 5 | # QUIT is always special | ||
| 6 | trap '' QUIT | ||
| 7 | # SYS is not special | ||
| 8 | trap '' SYS | ||
| 9 | # WINCH is harmless | ||
| 10 | trap 'bad: caught WINCH' WINCH | ||
| 11 | # With TERM we'll check whether it is reset | ||
| 12 | trap 'bad: caught TERM' TERM | ||
| 13 | |||
| 14 | # using bash, becuase we don't have $PPID (yet) | ||
| 15 | (bash -c 'kill -HUP $PPID'; echo Ok) | ||
| 16 | (bash -c 'kill -QUIT $PPID'; echo Ok) | ||
| 17 | (bash -c 'kill -SYS $PPID'; echo Ok) | ||
| 18 | (bash -c 'kill -WINCH $PPID'; echo Ok) | ||
| 19 | (bash -c 'kill -TERM $PPID'; echo Bad: TERM is not reset) | ||
| 20 | echo Done | ||
