Currently, busybox init won't allow us to do a clean kexec; we have to
invoke kexec -e, which calls the reboot(LINUX_REBOOT_CMD_KEXEC) system
call directly, bypassing any shutdown/reboot init actions.

It'd be better if we can halt the system properly before the kernel
kexec, in order to unmount filesystems, take network devices down, etc.

As far as I can tell, this is handled in other inits in differring ways:
A kexec on systemd is performed by invoking `systemctl kexec`,
(alternatively `shutdown -r` will detect the presence of a loaded kexec
and change the reboot() magic accordingly). Upstart doesn't seem to
have native kexec support, so a clean kexec on Ubuntu is implemented
with an init script that calls `kexec -e` late in the shutdown path and
never returns.

This change adds a '-k' option to /sbin/reboot, which tells init (via
a SIGALRM) that we're requesting a kexec rather than a normal reboot.
init then handles this by specifying the correct magic to the reboot
syscall.

RFC: a couple of open items:

 * Not sure that SIGALRM is the best signal for this, let me know if
   there's something more appropriate

 * We could handle kexec in a method similar to upstart, by adding an
   rc.d script that performs the reboot() syscall with the correct
   kexec magic. However, this is a little inconsistent with the existing
   reboot methods.

Signed-off-by: Jeremy Kerr <[email protected]>

---
 init/halt.c   |   12 +++++++++---
 init/init.c   |    9 +++++++--
 init/reboot.h |    6 ++++++
 3 files changed, 22 insertions(+), 5 deletions(-)

diff --git a/init/halt.c b/init/halt.c
index 7974adb..68a60a7 100644
--- a/init/halt.c
+++ b/init/halt.c
@@ -65,6 +65,7 @@
 //usage:     "\n       -d SEC  Delay interval"
 //usage:     "\n       -n      Do not sync"
 //usage:     "\n       -f      Force (don't go through init)"
+//usage:     "\n       -k      Reboot to preloaded kexec kernel"
 
 #include "libbb.h"
 #include "reboot.h"
@@ -101,9 +102,11 @@ int halt_main(int argc UNUSED_PARAM, char **argv)
        static const int magic[] = {
                RB_HALT_SYSTEM,
                RB_POWER_OFF,
-               RB_AUTOBOOT
+               RB_AUTOBOOT,
+               RB_KEXEC,
        };
-       static const smallint signals[] = { SIGUSR1, SIGUSR2, SIGTERM };
+       static const smallint signals[] =
+               { SIGUSR1, SIGUSR2, SIGTERM, SIGALRM };
 
        int delay = 0;
        int which, flags, rc;
@@ -118,7 +121,7 @@ int halt_main(int argc UNUSED_PARAM, char **argv)
         * in order to not break scripts.
         * -i (shut down network interfaces) is ignored.
         */
-       flags = getopt32(argv, "d:nfwi", &delay);
+       flags = getopt32(argv, "d:nfwik", &delay);
 
        sleep(delay);
 
@@ -130,6 +133,9 @@ int halt_main(int argc UNUSED_PARAM, char **argv)
        if (!(flags & 2)) /* no -n */
                sync();
 
+       if (applet_name[0] == 'r' && flags & 0x20)
+               which = 4;
+
        /* Perform action. */
        rc = 1;
        if (!(flags & 4)) { /* no -f */
diff --git a/init/init.c b/init/init.c
index 15aad47..7c806d7 100644
--- a/init/init.c
+++ b/init/init.c
@@ -175,8 +175,8 @@
 #define CTRLALTDEL  0x20
 /*
  * Start these before killing all processes in preparation for
- * running RESTART actions or doing low-level halt/reboot/poweroff
- * (initiated by SIGUSR1/SIGTERM/SIGUSR2).
+ * running RESTART actions or doing low-level halt/reboot/poweroff/kexec
+ * (initiated by SIGUSR1/SIGTERM/SIGUSR2/SIGALRM).
  * Wait for completion before proceeding.
  */
 #define SHUTDOWN    0x40
@@ -393,6 +393,7 @@ static void reset_sighandlers_and_unblock_sigs(void)
                + (1 << SIGHUP)
                + (1 << SIGTSTP)
                + (1 << SIGSTOP)
+               + (1 << SIGALRM)
                , SIG_DFL);
        sigprocmask_allsigs(SIG_UNBLOCK);
 }
@@ -811,6 +812,9 @@ static void halt_reboot_pwoff(int sig)
        } else if (sig == SIGUSR2) {
                m = "poweroff";
                rb = RB_POWER_OFF;
+       } else if (sig == SIGALRM) {
+               m = "kexec";
+               rb = RB_KEXEC;
        }
        message(L_CONSOLE, "Requesting system %s", m);
        pause_and_low_level_reboot(rb);
@@ -1124,6 +1128,7 @@ int init_main(int argc UNUSED_PARAM, char **argv)
                        + (1 << SIGUSR1) /* halt */
                        + (1 << SIGTERM) /* reboot */
                        + (1 << SIGUSR2) /* poweroff */
+                       + (1 << SIGALRM) /* kexec */
                        , halt_reboot_pwoff);
                signal(SIGQUIT, restart_handler); /* re-exec another init */
 
diff --git a/init/reboot.h b/init/reboot.h
index 9497639..ea8b1ac 100644
--- a/init/reboot.h
+++ b/init/reboot.h
@@ -17,6 +17,12 @@
 # endif
 #endif
 
+#ifdef LINUX_REBOOT_CMD_KEXEC
+# define RB_KEXEC      LINUX_REBOOT_CMD_KEXEC
+#else
+# define RB_KEXEC      0
+#endif
+
 /* Stop system and switch power off if possible.  */
 #ifndef RB_POWER_OFF
 # if defined(RB_POWERDOWN)
_______________________________________________
busybox mailing list
[email protected]
http://lists.busybox.net/mailman/listinfo/busybox

Reply via email to