summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorDenis Vlasenko <vda.linux@googlemail.com>2009-04-17 13:52:51 +0000
committerDenis Vlasenko <vda.linux@googlemail.com>2009-04-17 13:52:51 +0000
commite4bd4f2cc8413bf3417ae902fcd14580a056a69c (patch)
treedaa1569fbda13dc0cce2caddf1748d24db915d69
parentf8c1f02d2f7733619437581c2264828d3d160089 (diff)
hush: unblock TERM, INT, HUP in child shells too.
-rw-r--r--shell/hush.c73
1 files changed, 42 insertions, 31 deletions
diff --git a/shell/hush.c b/shell/hush.c
index 8dda988ee..13a06a492 100644
--- a/shell/hush.c
+++ b/shell/hush.c
@@ -1022,6 +1022,15 @@ static void free_strings(char **strings)
* are to count SIGCHLDs [disabled - bug somewhere, + bloat]
* and to restore tty pgrp on signal-induced exit.
*/
+enum {
+ SPECIAL_INTERACTIVE_SIGS = 0
+#if ENABLE_HUSH_JOB
+ | (1 << SIGTTIN) | (1 << SIGTTOU) | (1 << SIGTSTP)
+#endif
+ | (1 << SIGTERM)
+//TODO | (1 << SIGHUP)
+ | (1 << SIGINT)
+};
//static void SIGCHLD_handler(int sig UNUSED_PARAM)
//{
@@ -1059,6 +1068,8 @@ static int check_and_run_traps(int sig)
// G.count_SIGCHLD++;
// break;
case SIGINT:
+//TODO: add putchar('\n') also when we detect that child was killed (sleep 5 + ^C)
+ /* Builtin was ^C'ed, make it look prettier: */
bb_putchar('\n');
G.flag_SIGINT = 1;
break;
@@ -2284,37 +2295,46 @@ void re_execute_shell(char ***to_free, const char *s, char *argv0, char **argv);
static void reset_traps_to_defaults(void)
{
- enum {
- JOBSIGS = (1 << SIGTTIN) | (1 << SIGTTOU) | (1 << SIGTSTP)
- };
- unsigned sig;
-
- if (!G.traps && !(G.non_DFL_mask & JOBSIGS))
- return;
-
- /* This function is always called in a child shell.
+ /* This function is always called in a child shell
+ * after fork (not vfork, NOMMU doesn't use this function).
* Child shells are not interactive.
* SIGTTIN/SIGTTOU/SIGTSTP should not have special handling.
* Testcase: (while :; do :; done) + ^Z should background.
+ * Same goes for SIGTERM, SIGHUP, SIGINT.
*/
- G.non_DFL_mask &= ~JOBSIGS;
- sigdelset(&G.blocked_set, SIGTTIN);
- sigdelset(&G.blocked_set, SIGTTOU);
- sigdelset(&G.blocked_set, SIGTSTP);
+ unsigned sig;
+ unsigned mask;
- if (G.traps) for (sig = 0; sig < NSIG; sig++) {
- if (!G.traps[sig]) {
+ if (!G.traps && !(G.non_DFL_mask & SPECIAL_INTERACTIVE_SIGS))
+ return;
+
+ /* Stupid. It can be done with *single* &= op, but we can't use
+ * the fact that G.blocked_set is implemented as a bitmask... */
+ mask = (SPECIAL_INTERACTIVE_SIGS >> 1);
+ sig = 1;
+ while (1) {
+ if (mask & 1)
+ sigdelset(&G.blocked_set, sig);
+ mask >>= 1;
+ if (!mask)
+ break;
+ sig++;
+ }
+
+ G.non_DFL_mask &= ~SPECIAL_INTERACTIVE_SIGS;
+ mask = G.non_DFL_mask;
+ if (G.traps) for (sig = 0; sig < NSIG; sig++, mask >>= 1) {
+ if (!G.traps[sig])
continue;
- }
free(G.traps[sig]);
G.traps[sig] = NULL;
/* There is no signal for 0 (EXIT) */
if (sig == 0)
continue;
- /* there was a trap handler, we are removing it
- * (if sig has non-DFL handling,
- * we don't need to do anything) */
- if (sig < 32 && (G.non_DFL_mask & (1 << sig)))
+ /* There was a trap handler, we are removing it.
+ * But if sig still has non-DFL handling,
+ * we should not unblock it. */
+ if (mask & 1)
continue;
sigdelset(&G.blocked_set, sig);
}
@@ -5740,17 +5760,8 @@ static void block_signals(int second_time)
unsigned mask;
mask = (1 << SIGQUIT);
- if (G_interactive_fd) {
- mask = 0
- | (1 << SIGQUIT)
- | (1 << SIGTERM)
-//TODO | (1 << SIGHUP)
-#if ENABLE_HUSH_JOB
- | (1 << SIGTTIN) | (1 << SIGTTOU) | (1 << SIGTSTP)
-#endif
- | (1 << SIGINT)
- ;
- }
+ if (G_interactive_fd)
+ mask = (1 << SIGQUIT) | SPECIAL_INTERACTIVE_SIGS;
G.non_DFL_mask = mask;
if (!second_time)