aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDenis Vlasenko <vda.linux@googlemail.com>2009-03-10 16:01:57 +0000
committerDenis Vlasenko <vda.linux@googlemail.com>2009-03-10 16:01:57 +0000
commit4774179cb9e04030485773adf2b7b1055a10faeb (patch)
tree91bb5c51f9f4e3036298d7bb5490621b3e7a3ee6
parent245f91b6494063c5fa1f3f586771e2ac100a69a9 (diff)
downloadbusybox-4774179cb9e04030485773adf2b7b1055a10faeb.tar.gz
mail.c: more robust handling of SIGCHLD
init: more robust signal handling
-rw-r--r--init/init.c41
-rw-r--r--mailutils/mail.c11
2 files changed, 30 insertions, 22 deletions
diff --git a/init/init.c b/init/init.c
index 5b9820b4a..5c344cb63 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 71f46c86f..68883ff42 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