diff options
author | Denis Vlasenko <vda.linux@googlemail.com> | 2009-03-31 12:03:40 +0000 |
---|---|---|
committer | Denis Vlasenko <vda.linux@googlemail.com> | 2009-03-31 12:03:40 +0000 |
commit | abedaac8423d72325ae05602bf1bdcde28ecd7fa (patch) | |
tree | 144592f8f52aafafa0af0a70e9656dd1467b343d /shell | |
parent | d5762932fbcbc0a385047945276f10e2f3fea12d (diff) |
hush: do not reset sighaldlers after fork - instead, prevent them from doing any harm
if they will be called. saves many sigaction calls on every fork.
function old new delta
hush_main 887 971 +84
sigexit 54 65 +11
run_list 2006 2001 -5
maybe_set_sighandler 76 50 -26
set_fatal_sighandler 186 - -186
------------------------------------------------------------------------------
(add/remove: 0/1 grow/shrink: 2/2 up/down: 95/-217) Total: -122 bytes
Diffstat (limited to 'shell')
-rw-r--r-- | shell/hush.c | 205 |
1 files changed, 59 insertions, 146 deletions
diff --git a/shell/hush.c b/shell/hush.c index bcccfb0bb..01690d9bc 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -493,8 +493,6 @@ struct globals { char **traps; /* char *traps[NSIG] */ /* which signals have non-DFL handler (even with no traps set)? */ unsigned non_DFL_mask; - /* which signals are known to be IGNed on entry to shell? */ - unsigned IGN_mask; sigset_t blocked_set; sigset_t inherited_set; }; @@ -895,66 +893,71 @@ static void check_and_run_traps(void) } } -/* The stuff below needs to be migrated to "Special action" above */ +#if ENABLE_HUSH_JOB -/////* Signals are grouped, we handle them in batches */ -////static void set_misc_sighandler(void (*handler)(int)) -////{ -//// bb_signals(0 -//// + (1 << SIGINT) -//// + (1 << SIGQUIT) -//// + (1 << SIGTERM) -//// , handler); -////} +/* Restores tty foreground process group, and exits. + * May be called as signal handler for fatal signal + * (will faithfully resend signal to itself, producing correct exit state) + * or called directly with -EXITCODE. + * We also call it if xfunc is exiting. */ +static void sigexit(int sig) NORETURN; +static void sigexit(int sig) +{ + /* Disable all signals: job control, SIGPIPE, etc. */ + sigprocmask_allsigs(SIG_BLOCK); -#if ENABLE_HUSH_JOB +#if ENABLE_HUSH_INTERACTIVE + /* Careful: we can end up here after [v]fork. Do not restore + * tty pgrp, only top-level shell process does that */ + if (G.interactive_fd && getpid() == G.root_pid) + tcsetpgrp(G.interactive_fd, G.saved_tty_pgrp); +#endif + + /* Not a signal, just exit */ + if (sig <= 0) + _exit(- sig); + + kill_myself_with_sig(sig); /* does not return */ +} /* helper */ -static unsigned maybe_set_sighandler(int sig, void (*handler)(int)) +static void maybe_set_sighandler(int sig) { + void (*handler)(int); /* non_DFL_mask'ed signals are, well, masked, * no need to set handler for them. - * IGN_mask'ed signals were found to be IGN on entry, - * do not change that -> no need to set handler for them either */ - unsigned ign = ((G.IGN_mask|G.non_DFL_mask) >> sig) & 1; - if (!ign) { - handler = signal(sig, handler); - ign = (handler == SIG_IGN); - if (ign) /* restore back to IGN! */ + if (!((G.non_DFL_mask >> sig) & 1)) { + handler = signal(sig, sigexit); + if (handler == SIG_IGN) /* restore back to IGN! */ signal(sig, handler); } - return ign << sig; /* pass back knowledge about SIG_IGN */ } /* Used only to set handler to restore pgrp on exit, and to reset it to DFL */ -static void set_fatal_sighandler(void (*handler)(int)) +static void set_fatal_signals_to_sigexit(void) { - unsigned mask = 0; - if (HUSH_DEBUG) { - mask |= maybe_set_sighandler(SIGILL , handler); - mask |= maybe_set_sighandler(SIGFPE , handler); - mask |= maybe_set_sighandler(SIGBUS , handler); - mask |= maybe_set_sighandler(SIGSEGV, handler); - mask |= maybe_set_sighandler(SIGTRAP, handler); + maybe_set_sighandler(SIGILL ); + maybe_set_sighandler(SIGFPE ); + maybe_set_sighandler(SIGBUS ); + maybe_set_sighandler(SIGSEGV); + maybe_set_sighandler(SIGTRAP); } /* else: hush is perfect. what SEGV? */ - mask |= maybe_set_sighandler(SIGABRT, handler); + maybe_set_sighandler(SIGABRT); /* bash 3.2 seems to handle these just like 'fatal' ones */ - mask |= maybe_set_sighandler(SIGPIPE, handler); - mask |= maybe_set_sighandler(SIGALRM, handler); - mask |= maybe_set_sighandler(SIGHUP , handler); + maybe_set_sighandler(SIGPIPE); + maybe_set_sighandler(SIGALRM); + maybe_set_sighandler(SIGHUP ); /* if we aren't interactive... but in this case * we never want to restore pgrp on exit, and this fn is not called */ - /*mask |= maybe_set_sighandler(SIGTERM, handler); */ - /*mask |= maybe_set_sighandler(SIGINT , handler); */ - - G.IGN_mask = mask; + /*maybe_set_sighandler(SIGTERM);*/ + /*maybe_set_sighandler(SIGINT );*/ } /* Used only to suppress ^Z in `cmd` */ -static void IGN_jobctrl_signals(void) +static void set_jobctrl_signals_to_IGN(void) { bb_signals(0 + (1 << SIGTSTP) @@ -962,88 +965,11 @@ static void IGN_jobctrl_signals(void) + (1 << SIGTTOU) , SIG_IGN); } -/* SIGCHLD is special and handled separately */ - -////static void set_every_sighandler(void (*handler)(int)) -////{ -//// set_fatal_sighandler(handler); -//// set_jobctrl_sighandler(handler); -//// set_misc_sighandler(handler); -//// signal(SIGCHLD, handler); -////} - -////static void handler_ctrl_c(int sig UNUSED_PARAM) -////{ -//// debug_printf_jobs("got sig %d\n", sig); -////// as usual we can have all kinds of nasty problems with leaked malloc data here -//// siglongjmp(G.toplevel_jb, 1); -////} - -////static void handler_ctrl_z(int sig UNUSED_PARAM) -////{ -//// pid_t pid; -//// -//// debug_printf_jobs("got tty sig %d in pid %d\n", sig, getpid()); -//// -//// if (!BB_MMU) { -//// fputs("Sorry, backgrounding (CTRL+Z) of foreground scripts not supported on nommu\n", stderr); -//// return; -//// } -//// -//// pid = fork(); -//// if (pid < 0) /* can't fork. Pretend there was no ctrl-Z */ -//// return; -//// G.ctrl_z_flag = 1; -//// if (!pid) { /* child */ -//// if (ENABLE_HUSH_JOB) -//// die_sleep = 0; /* let nofork's xfuncs die */ -//// bb_setpgrp(); -//// debug_printf_jobs("set pgrp for child %d ok\n", getpid()); -//////// set_every_sighandler(SIG_DFL); -//// raise(SIGTSTP); /* resend TSTP so that child will be stopped */ -//// debug_printf_jobs("returning in child\n"); -//// /* return to nofork, it will eventually exit now, -//// * not return back to shell */ -//// return; -//// } -//// /* parent */ -//// /* finish filling up pipe info */ -//// G.toplevel_list->pgrp = pid; /* child is in its own pgrp */ -//// G.toplevel_list->cmds[0].pid = pid; -//// /* parent needs to longjmp out of running nofork. -//// * we will "return" exitcode 0, with child put in background */ -////// as usual we can have all kinds of nasty problems with leaked malloc data here -//// debug_printf_jobs("siglongjmp in parent\n"); -//// siglongjmp(G.toplevel_jb, 1); -////} - -/* Restores tty foreground process group, and exits. - * May be called as signal handler for fatal signal - * (will faithfully resend signal to itself, producing correct exit state) - * or called directly with -EXITCODE. - * We also call it if xfunc is exiting. */ -static void sigexit(int sig) NORETURN; -static void sigexit(int sig) -{ - /* Disable all signals: job control, SIGPIPE, etc. */ - sigprocmask_allsigs(SIG_BLOCK); - -#if ENABLE_HUSH_INTERACTIVE - if (G.interactive_fd) - tcsetpgrp(G.interactive_fd, G.saved_tty_pgrp); -#endif - - /* Not a signal, just exit */ - if (sig <= 0) - _exit(- sig); - - kill_myself_with_sig(sig); /* does not return */ -} #else /* !JOB */ -#define set_fatal_sighandler(handler) ((void)0) -#define IGN_jobctrl_signals(handler) ((void)0) +#define set_fatal_signals_to_sigexit(handler) ((void)0) +#define set_jobctrl_signals_to_IGN(handler) ((void)0) #endif /* JOB */ @@ -1057,11 +983,12 @@ static void hush_exit(int exitcode) free(argv[1]); } - if (ENABLE_HUSH_JOB) { - fflush(NULL); /* flush all streams */ - sigexit(- (exitcode & 0xff)); - } else - exit(exitcode); +#if ENABLE_HUSH_JOB + fflush(NULL); /* flush all streams */ + sigexit(- (exitcode & 0xff)); +#else + exit(exitcode); +#endif } @@ -2641,10 +2568,6 @@ static int run_pipe(struct pipe *pi) * might include `cmd` runs! Do not rerun it! We *must* * use argv_expanded if it's non-NULL */ - /* Disable job control signals for shell (parent) and - * for initial child code after fork */ -//// set_jobctrl_sighandler(SIG_IGN); - /* Going to fork a child per each pipe member */ pi->alive_cmds = 0; nextin = 0; @@ -2677,8 +2600,6 @@ static int run_pipe(struct pipe *pi) * with pgid == pid_of_first_child_in_pipe */ if (G.run_list_level == 1 && G.interactive_fd) { pid_t pgrp; - /* Don't do pgrp restore anymore on fatal signals */ - set_fatal_sighandler(SIG_DFL); pgrp = pi->pgrp; if (pgrp < 0) /* true for 1st process only */ pgrp = getpid(); @@ -2698,9 +2619,8 @@ static int run_pipe(struct pipe *pi) setup_redirects(command, NULL); /* Restore default handlers just prior to exec */ -//// set_jobctrl_sighandler(SIG_DFL); -//// set_misc_sighandler(SIG_DFL); -//// signal(SIGCHLD, SIG_DFL); + /*signal(SIGCHLD, SIG_DFL); - so far we don't have any handlers */ + /* Stores to nommu_save list of env vars putenv'ed * (NOMMU, on MMU we don't need that) */ /* cast away volatility... */ @@ -2877,7 +2797,7 @@ static int run_list(struct pipe *pi) * in order to return, no direct "return" statements please. * This helps to ensure that no memory is leaked. */ -//TODO: needs re-thinking +////TODO: ctrl-Z handling needs re-thinking and re-testing #if ENABLE_HUSH_JOB /* Example of nested list: "while true; do { sleep 1 | exit 2; } done". @@ -3674,12 +3594,11 @@ static FILE *generate_stream_from_list(struct pipe *head) #endif /* Process substitution is not considered to be usual * 'command execution'. - * SUSv3 says ctrl-Z should be ignored, ctrl-C should not. */ -//// /* Not needed, we are relying on it being disabled -//// * everywhere outside actual command execution. */ - IGN_jobctrl_signals(); -//// set_misc_sighandler(SIG_DFL); - /* Freeing 'head' here would break NOMMU. */ + * SUSv3 says ctrl-Z should be ignored, ctrl-C should not. + */ + set_jobctrl_signals_to_IGN(); + + /* Note: freeing 'head' here would break NOMMU. */ _exit(run_list(head)); } close(channel[1]); @@ -4493,13 +4412,8 @@ static void setup_job_control(void) shell_pgrp = getpgrp(); } - /* Ignore job-control and misc signals. */ -//// set_jobctrl_sighandler(SIG_IGN); -//// set_misc_sighandler(SIG_IGN); -//huh? signal(SIGCHLD, SIG_IGN); - /* We _must_ restore tty pgrp on fatal signals */ - set_fatal_sighandler(sigexit); + set_fatal_signals_to_sigexit(); /* Put ourselves in our own process group. */ bb_setpgrp(); /* is the same as setpgid(our_pid, our_pid); */ @@ -4682,7 +4596,6 @@ int hush_main(int argc, char **argv) } if (G.interactive_fd) { fcntl(G.interactive_fd, F_SETFD, FD_CLOEXEC); -//// set_misc_sighandler(SIG_IGN); } } init_signal_mask(); |