runsv: robustify signal handling - SIGTERM to child between vfork and exec could mess things up
While at it, rename bb_signals_recursive_norestart() to bb_signals_norestart():
"recursive" was implying we are setting SA_NODEFER allowing signal handler
to be entered recursively, but we do not do that.
function old new delta
bb_signals_norestart - 70 +70
startservice 380 394 +14
bb_signals_recursive_norestart 70 - -70
------------------------------------------------------------------------------
(add/remove: 1/1 grow/shrink: 1/0 up/down: 84/-70) Total: 14 bytes
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
diff --git a/runit/runsv.c b/runit/runsv.c
index 61ea240..7fad563 100644
--- a/runit/runsv.c
+++ b/runit/runsv.c
@@ -149,11 +149,15 @@
warn2_cannot(m, "");
}
+/* SIGCHLD/TERM handlers are reentrancy-safe because they are unmasked
+ * only over poll() call, not over memory allocations
+ * or printouts. Do not need to save/restore errno either,
+ * as poll() error is not checked there.
+ */
static void s_child(int sig_no UNUSED_PARAM)
{
write(selfpipe.wr, "", 1);
}
-
static void s_term(int sig_no UNUSED_PARAM)
{
sigterm = 1;
@@ -380,14 +384,14 @@
xdup2(logpipe.wr, 1);
}
}
- /* Non-ignored signals revert to SIG_DFL on exec anyway.
- * But we can get signals BEFORE execl(), this is unlikely
- * but wouldn't be good...
+ /* Non-ignored signals revert to SIG_DFL on exec.
+ * But we can get signals BEFORE execl(), unlikely as that may be.
+ * SIGCHLD is safe (would merely write to selfpipe),
+ * but SIGTERM would set sigterm = 1 (with vfork, we affect parent).
+ * Avoid that.
*/
- /*bb_signals(0
- + (1 << SIGCHLD)
- + (1 << SIGTERM)
- , SIG_DFL);*/
+ /*signal(SIGCHLD, SIG_DFL);*/
+ signal(SIGTERM, SIG_DFL);
sig_unblock(SIGCHLD);
sig_unblock(SIGTERM);
execv(arg[0], (char**) arg);
@@ -514,9 +518,13 @@
ndelay_on(selfpipe.wr);
sig_block(SIGCHLD);
- bb_signals_recursive_norestart(1 << SIGCHLD, s_child);
sig_block(SIGTERM);
- bb_signals_recursive_norestart(1 << SIGTERM, s_term);
+ /* No particular reason why we don't set SA_RESTART
+ * (poll() wouldn't restart regardless of that flag),
+ * we just follow what runit-2.1.2 does:
+ */
+ bb_signals_norestart(1 << SIGCHLD, s_child);
+ bb_signals_norestart(1 << SIGTERM, s_term);
xchdir(dir);
/* bss: svd[0].pid = 0; */
@@ -628,6 +636,7 @@
sig_unblock(SIGTERM);
sig_unblock(SIGCHLD);
poll(x, 2 + haslog, 3600*1000);
+ /* NB: signal handlers can trash errno of poll() */
sig_block(SIGTERM);
sig_block(SIGCHLD);