mail.c: more robust handling of SIGCHLD

init: more robust signal handling
This commit is contained in:
Denis Vlasenko 2009-03-10 16:01:57 +00:00
parent 245f91b649
commit 4774179cb9
2 changed files with 30 additions and 22 deletions

View File

@ -336,20 +336,22 @@ static pid_t run(const struct init_action *a)
{ {
pid_t pid; 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)) if (BB_MMU && (a->action_type & ASKFIRST))
pid = fork(); pid = fork();
else else
pid = vfork(); pid = vfork();
if (pid < 0) if (pid < 0)
message(L_LOG | L_CONSOLE, "can't fork"); message(L_LOG | L_CONSOLE, "can't fork");
if (pid) if (pid) {
sigprocmask_allsigs(SIG_UNBLOCK);
return pid; /* Parent or error */ return pid; /* Parent or error */
}
/* Child */ /* Child */
/* Reset signal handlers that were set by the parent process */ /* 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 bb_signals(0
+ (1 << SIGUSR1) + (1 << SIGUSR1)
+ (1 << SIGUSR2) + (1 << SIGUSR2)
@ -359,6 +361,7 @@ static pid_t run(const struct init_action *a)
+ (1 << SIGHUP) + (1 << SIGHUP)
+ (1 << SIGTSTP) + (1 << SIGTSTP)
, SIG_DFL); , SIG_DFL);
sigprocmask_allsigs(SIG_UNBLOCK);
/* Create a new session and make ourself the process group leader */ /* Create a new session and make ourself the process group leader */
setsid(); setsid();
@ -982,40 +985,42 @@ int init_main(int argc UNUSED_PARAM, char **argv)
* NB: if delayed signal happened, avoid blocking in wait(). * NB: if delayed signal happened, avoid blocking in wait().
*/ */
while (1) { while (1) {
pid_t wpid; int maybe_WNOHANG;
int got_sigs;
got_sigs = check_delayed_sigs(); maybe_WNOHANG = check_delayed_sigs();
/* (Re)run the respawn/askfirst stuff */ /* (Re)run the respawn/askfirst stuff */
run_actions(RESPAWN | ASKFIRST); run_actions(RESPAWN | ASKFIRST);
maybe_WNOHANG |= check_delayed_sigs();
got_sigs |= check_delayed_sigs();
/* Don't consume all CPU time - sleep a bit */ /* Don't consume all CPU time - sleep a bit */
sleep(1); sleep(1);
maybe_WNOHANG |= check_delayed_sigs();
got_sigs |= check_delayed_sigs(); /* Wait for any child process(es) to exit.
if (got_sigs)
goto dont_block;
/* Wait for any child process to exit.
* NB: "delayed" signals will also interrupt this wait(), * NB: "delayed" signals will also interrupt this wait(),
* bb_signals_recursive_norestart() set them up for that. * bb_signals_recursive_norestart() set them up for that.
* This guarantees we won't be stuck here * This guarantees we won't be stuck here
* till next orphan dies. * till next orphan dies.
*/ */
wpid = wait(NULL); if (maybe_WNOHANG)
while (wpid > 0) { maybe_WNOHANG = WNOHANG;
struct init_action *a = mark_terminated(wpid); 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) { if (a) {
message(L_LOG, "process '%s' (pid %d) exited. " message(L_LOG, "process '%s' (pid %d) exited. "
"Scheduling for restart.", "Scheduling for restart.",
a->command, wpid); a->command, wpid);
} }
/* See if anyone else is waiting to be reaped */ /* See if anyone else is waiting to be reaped */
dont_block: maybe_WNOHANG = WNOHANG;
wpid = wait_any_nohang(NULL);
} }
} /* while (1) */ } /* while (1) */
} }

View File

@ -48,6 +48,12 @@ void FAST_FUNC launch_helper(const char **argv)
xpipe(pipes); xpipe(pipes);
xpipe(pipes + 2); xpipe(pipes + 2);
// NB: handler must be installed before vfork
bb_signals(0
+ (1 << SIGCHLD)
+ (1 << SIGALRM)
, signal_handler);
G.helper_pid = vfork(); G.helper_pid = vfork();
if (G.helper_pid < 0) if (G.helper_pid < 0)
bb_perror_msg_and_die("vfork"); bb_perror_msg_and_die("vfork");
@ -60,15 +66,12 @@ void FAST_FUNC launch_helper(const char **argv)
if (!G.helper_pid) { if (!G.helper_pid) {
// child: try to execute connection helper // child: try to execute connection helper
// NB: SIGCHLD & SIGALRM revert to SIG_DFL on exec
BB_EXECVP(*argv, (char **)argv); BB_EXECVP(*argv, (char **)argv);
_exit(127); _exit(127);
} }
// parent // parent
bb_signals(0
+ (1 << SIGCHLD)
+ (1 << SIGALRM)
, signal_handler);
// check whether child is alive // check whether child is alive
//redundant:signal_handler(SIGCHLD); //redundant:signal_handler(SIGCHLD);
// child seems OK -> parent goes on // child seems OK -> parent goes on