aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--shell/hush.c41
-rw-r--r--shell/hush_test/hush-trap/subshell.right6
-rwxr-xr-xshell/hush_test/hush-trap/subshell.tests20
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 */
1123enum { 1120enum {
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 @@
1Ok
2Ok
3Ok
4Ok
5TERM
6Done
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
4trap '' HUP
5# QUIT is always special
6trap '' QUIT
7# SYS is not special
8trap '' SYS
9# WINCH is harmless
10trap 'bad: caught WINCH' WINCH
11# With TERM we'll check whether it is reset
12trap '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)
20echo Done