init: fix "while true; do reboot; done" bug. +15 bytes. Closes bug 781
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
diff --git a/init/init.c b/init/init.c
index 3748a15..89bbafd 100644
--- a/init/init.c
+++ b/init/init.c
@@ -260,6 +260,20 @@
return 1; /* success */
}
+static void reset_sighandlers_and_unblock_sigs(void)
+{
+ bb_signals(0
+ + (1 << SIGUSR1)
+ + (1 << SIGUSR2)
+ + (1 << SIGTERM)
+ + (1 << SIGQUIT)
+ + (1 << SIGINT)
+ + (1 << SIGHUP)
+ + (1 << SIGTSTP)
+ , SIG_DFL);
+ sigprocmask_allsigs(SIG_UNBLOCK);
+}
+
/* Wrapper around exec:
* Takes string (max COMMAND_SIZE chars).
* If chars like '>' detected, execs '[-]/bin/sh -c "exec ......."'.
@@ -329,16 +343,7 @@
/* Child */
/* Reset signal handlers that were set by the parent process */
- bb_signals(0
- + (1 << SIGUSR1)
- + (1 << SIGUSR2)
- + (1 << SIGTERM)
- + (1 << SIGQUIT)
- + (1 << SIGINT)
- + (1 << SIGHUP)
- + (1 << SIGTSTP)
- , SIG_DFL);
- sigprocmask_allsigs(SIG_UNBLOCK);
+ reset_sighandlers_and_unblock_sigs();
/* Create a new session and make ourself the process group leader */
setsid();
@@ -651,12 +656,21 @@
* and only one will be remembered and acted upon.
*/
+/* The SIGUSR[12]/SIGTERM handler */
static void halt_reboot_pwoff(int sig) NORETURN;
static void halt_reboot_pwoff(int sig)
{
const char *m;
unsigned rb;
+ /* We may call run() and it unmasks signals,
+ * including the one masked inside this signal handler.
+ * Testcase which would start multiple reboot scripts:
+ * while true; do reboot; done
+ * Preventing it:
+ */
+ reset_sighandlers_and_unblock_sigs();
+
run_shutdown_and_kill_processes();
m = "halt";
@@ -673,6 +687,48 @@
/* not reached */
}
+/* Handler for QUIT - exec "restart" action,
+ * else (no such action defined) do nothing */
+static void restart_handler(int sig UNUSED_PARAM)
+{
+ struct init_action *a;
+
+ for (a = init_action_list; a; a = a->next) {
+ if (!(a->action_type & RESTART))
+ continue;
+
+ /* Starting from here, we won't return.
+ * Thus don't need to worry about preserving errno
+ * and such.
+ */
+
+ reset_sighandlers_and_unblock_sigs();
+
+ run_shutdown_and_kill_processes();
+
+ /* Allow Ctrl-Alt-Del to reboot the system.
+ * This is how kernel sets it up for init, we follow suit.
+ */
+ reboot(RB_ENABLE_CAD); /* misnomer */
+
+ if (open_stdio_to_tty(a->terminal)) {
+ dbg_message(L_CONSOLE, "Trying to re-exec %s", a->command);
+ /* Theoretically should be safe.
+ * But in practice, kernel bugs may leave
+ * unkillable processes, and wait() may block forever.
+ * Oh well. Hoping "new" init won't be too surprised
+ * by having children it didn't create.
+ */
+ //while (wait(NULL) > 0)
+ // continue;
+ init_exec(a->command);
+ }
+ /* Open or exec failed */
+ pause_and_low_level_reboot(RB_HALT_SYSTEM);
+ /* not reached */
+ }
+}
+
/* The SIGSTOP/SIGTSTP handler
* NB: inside it, all signals except SIGCONT are masked
* via appropriate setup in sigaction().
@@ -705,45 +761,6 @@
bb_got_signal = saved_bb_got_signal;
}
-/* Handler for QUIT - exec "restart" action,
- * else (no such action defined) do nothing */
-static void restart_handler(int sig UNUSED_PARAM)
-{
- struct init_action *a;
-
- for (a = init_action_list; a; a = a->next) {
- if (!(a->action_type & RESTART))
- continue;
-
- /* Starting from here, we won't return.
- * Thus don't need to worry about preserving errno
- * and such.
- */
- run_shutdown_and_kill_processes();
-
- /* Allow Ctrl-Alt-Del to reboot the system.
- * This is how kernel sets it up for init, we follow suit.
- */
- reboot(RB_ENABLE_CAD); /* misnomer */
-
- if (open_stdio_to_tty(a->terminal)) {
- dbg_message(L_CONSOLE, "Trying to re-exec %s", a->command);
- /* Theoretically should be safe.
- * But in practice, kernel bugs may leave
- * unkillable processes, and wait() may block forever.
- * Oh well. Hoping "new" init won't be too surprised
- * by having children it didn't create.
- */
- //while (wait(NULL) > 0)
- // continue;
- init_exec(a->command);
- }
- /* Open or exec failed */
- pause_and_low_level_reboot(RB_HALT_SYSTEM);
- /* not reached */
- }
-}
-
#if ENABLE_FEATURE_USE_INITTAB
static void reload_inittab(void)
{