diff options
author | Denis Vlasenko | 2009-03-10 16:01:57 +0000 |
---|---|---|
committer | Denis Vlasenko | 2009-03-10 16:01:57 +0000 |
commit | 4774179cb9e04030485773adf2b7b1055a10faeb (patch) | |
tree | 91bb5c51f9f4e3036298d7bb5490621b3e7a3ee6 | |
parent | 245f91b6494063c5fa1f3f586771e2ac100a69a9 (diff) | |
download | busybox-4774179cb9e04030485773adf2b7b1055a10faeb.zip busybox-4774179cb9e04030485773adf2b7b1055a10faeb.tar.gz |
mail.c: more robust handling of SIGCHLD
init: more robust signal handling
-rw-r--r-- | init/init.c | 41 | ||||
-rw-r--r-- | mailutils/mail.c | 11 |
2 files changed, 30 insertions, 22 deletions
diff --git a/init/init.c b/init/init.c index 5b9820b..5c344cb 100644 --- a/init/init.c +++ b/init/init.c @@ -336,20 +336,22 @@ static pid_t run(const struct init_action *a) { pid_t pid; + /* Careful: don't be affected by a signal in vforked child */ + sigprocmask_allsigs(SIG_BLOCK); if (BB_MMU && (a->action_type & ASKFIRST)) pid = fork(); else pid = vfork(); if (pid < 0) message(L_LOG | L_CONSOLE, "can't fork"); - if (pid) + if (pid) { + sigprocmask_allsigs(SIG_UNBLOCK); return pid; /* Parent or error */ + } /* Child */ /* Reset signal handlers that were set by the parent process */ -//TODO: block signals across fork(), prevent them to affect child before -//signals are reset? bb_signals(0 + (1 << SIGUSR1) + (1 << SIGUSR2) @@ -359,6 +361,7 @@ static pid_t run(const struct init_action *a) + (1 << SIGHUP) + (1 << SIGTSTP) , SIG_DFL); + sigprocmask_allsigs(SIG_UNBLOCK); /* Create a new session and make ourself the process group leader */ setsid(); @@ -982,40 +985,42 @@ int init_main(int argc UNUSED_PARAM, char **argv) * NB: if delayed signal happened, avoid blocking in wait(). */ while (1) { - pid_t wpid; - int got_sigs; + int maybe_WNOHANG; - got_sigs = check_delayed_sigs(); + maybe_WNOHANG = check_delayed_sigs(); /* (Re)run the respawn/askfirst stuff */ run_actions(RESPAWN | ASKFIRST); - - got_sigs |= check_delayed_sigs(); + maybe_WNOHANG |= check_delayed_sigs(); /* Don't consume all CPU time - sleep a bit */ sleep(1); + maybe_WNOHANG |= check_delayed_sigs(); - got_sigs |= check_delayed_sigs(); - - if (got_sigs) - goto dont_block; - /* Wait for any child process to exit. + /* Wait for any child process(es) to exit. * NB: "delayed" signals will also interrupt this wait(), * bb_signals_recursive_norestart() set them up for that. * This guarantees we won't be stuck here * till next orphan dies. */ - wpid = wait(NULL); - while (wpid > 0) { - struct init_action *a = mark_terminated(wpid); + if (maybe_WNOHANG) + maybe_WNOHANG = WNOHANG; + while (1) { + pid_t wpid; + struct init_action *a; + + wpid = waitpid(-1, NULL, maybe_WNOHANG); + if (wpid <= 0) + break; + + a = mark_terminated(wpid); if (a) { message(L_LOG, "process '%s' (pid %d) exited. " "Scheduling for restart.", a->command, wpid); } /* See if anyone else is waiting to be reaped */ - dont_block: - wpid = wait_any_nohang(NULL); + maybe_WNOHANG = WNOHANG; } } /* while (1) */ } diff --git a/mailutils/mail.c b/mailutils/mail.c index 71f46c8..68883ff 100644 --- a/mailutils/mail.c +++ b/mailutils/mail.c @@ -48,6 +48,12 @@ void FAST_FUNC launch_helper(const char **argv) xpipe(pipes); xpipe(pipes + 2); + // NB: handler must be installed before vfork + bb_signals(0 + + (1 << SIGCHLD) + + (1 << SIGALRM) + , signal_handler); + G.helper_pid = vfork(); if (G.helper_pid < 0) bb_perror_msg_and_die("vfork"); @@ -60,15 +66,12 @@ void FAST_FUNC launch_helper(const char **argv) if (!G.helper_pid) { // child: try to execute connection helper + // NB: SIGCHLD & SIGALRM revert to SIG_DFL on exec BB_EXECVP(*argv, (char **)argv); _exit(127); } // parent - bb_signals(0 - + (1 << SIGCHLD) - + (1 << SIGALRM) - , signal_handler); // check whether child is alive //redundant:signal_handler(SIGCHLD); // child seems OK -> parent goes on |