diff --git a/doc/manpages/shutdown.8.m4 b/doc/manpages/shutdown.8.m4 index 9ce9d1ed..f8de5d20 100644 --- a/doc/manpages/shutdown.8.m4 +++ b/doc/manpages/shutdown.8.m4 @@ -41,6 +41,11 @@ Display brief help text and then exit. Request a shutdown followed by restart. This is the default if executed as \fB$$$SHUTDOWN_PREFIX@@@reboot\fR. .TP +\fB\-k\fP +Shutdown the system and boot directly into a new kernel loaded via \fBkexec\fR(8), +without firmware reinitialisation. Don't forget to load a kernel image with +\fBkexec\fR(8) first! +.TP \fB\-s\fP Restart the service manager and all user-space services without restarting the system. This is the default if executed as \fB$$$SHUTDOWN_PREFIX@@@soft\-reboot\fR. diff --git a/src/control.cc b/src/control.cc index 58985430..13719a4c 100644 --- a/src/control.cc +++ b/src/control.cc @@ -75,7 +75,8 @@ bool control_conn_t::process_packet() } if (contains({shutdown_type_t::REMAIN, shutdown_type_t::HALT, - shutdown_type_t::POWEROFF, shutdown_type_t::REBOOT, shutdown_type_t::SOFTREBOOT}, rbuf[1])) { + shutdown_type_t::POWEROFF, shutdown_type_t::REBOOT, + shutdown_type_t::SOFTREBOOT, shutdown_type_t::KEXEC}, rbuf[1])) { auto sd_type = static_cast(rbuf[1]); services->stop_all_services(sd_type); diff --git a/src/dinit.cc b/src/dinit.cc index 31eaa60c..740847f5 100644 --- a/src/dinit.cc +++ b/src/dinit.cc @@ -700,6 +700,9 @@ int dinit_main(int argc, char **argv) else if (shutdown_type == shutdown_type_t::POWEROFF) { log_msg_end(" Will power down."); } + else if (shutdown_type == shutdown_type_t::KEXEC) { + log_msg_end(" Will kexec."); + } else if (shutdown_type == shutdown_type_t::NONE) { log_msg_end(" Will handle boot failure."); } @@ -768,6 +771,9 @@ int dinit_main(int argc, char **argv) else if (shutdown_type == shutdown_type_t::REBOOT) { cmd_arg = "-r"; } + else if (shutdown_type == shutdown_type_t::KEXEC) { + cmd_arg = "-k"; + } else { // power off. cmd_arg = "-p"; diff --git a/src/includes/service-constants.h b/src/includes/service-constants.h index d7ac8c7d..fb997810 100644 --- a/src/includes/service-constants.h +++ b/src/includes/service-constants.h @@ -36,13 +36,14 @@ enum class service_event_t { }; /* Shutdown types */ -enum class shutdown_type_t { +enum class shutdown_type_t: char { NONE, // No explicit shutdown REMAIN, // Continue running with no services HALT, // Halt system without powering down POWEROFF, // Power off system REBOOT, // Reboot system - SOFTREBOOT // Reboot dinit + SOFTREBOOT, // Reboot dinit + KEXEC // Reboot with kexec (without firmware reinitialisation) }; /* Reasons for why service stopped */ diff --git a/src/shutdown.cc b/src/shutdown.cc index 2e4ea915..6f01063e 100644 --- a/src/shutdown.cc +++ b/src/shutdown.cc @@ -246,6 +246,24 @@ class subproc_buffer : private cpbuffer } }; +static bool +reboot_cmd_unsupported(const shutdown_type_t type) +{ + // weed out unsupported values + switch (type) { +#if !defined(RB_HALT_SYSTEM) && !defined(RB_HALT) + case shutdown_type_t::HALT: return true; +#endif +#ifndef RB_POWER_OFF + case shutdown_type_t::POWEROFF: return true; +#endif +#ifndef RB_KEXEC + case shutdown_type_t::KEXEC: return true; +#endif + default: return false; + } +} + int main(int argc, char **argv) { @@ -287,6 +305,9 @@ int main(int argc, char **argv) else if (strcmp(argv[i], "-s") == 0) { shutdown_type = shutdown_type_t::SOFTREBOOT; } + else if (strcmp(argv[i], "-k") == 0) { + shutdown_type = shutdown_type_t::KEXEC; + } else if (strcmp(argv[i], "--use-passed-cfd") == 0) { use_passed_cfd = true; } @@ -301,13 +322,25 @@ int main(int argc, char **argv) } } + if (reboot_cmd_unsupported(shutdown_type)) { + cerr << "Unsupported shutdown type\n"; + return 1; + } + if (show_help) { cout << execname << " : shutdown the system\n" " --help : show this help\n" " -r : reboot\n" " -s : soft-reboot (restart dinit with same boot-time arguments)\n" +#if defined(RB_HALT_SYSTEM) || defined(RB_HALT) " -h : halt system\n" +#endif +#ifdef RB_POWER_OFF " -p : power down (default)\n" +#endif +#ifdef RB_KEXEC + " -k : stop dinit and reboot directly into kernel loaded with kexec\n" +#endif " --use-passed-cfd : use the socket file descriptor identified by the DINIT_CS_FD\n" " environment variable to communicate with the init daemon.\n" " --system : perform shutdown immediately, instead of issuing shutdown\n" @@ -318,7 +351,7 @@ int main(int argc, char **argv) if (sys_shutdown) { do_system_shutdown(shutdown_type); - return 0; + return 1; // likely to cause panic; the above shouldn't return } signal(SIGPIPE, SIG_IGN); @@ -421,6 +454,9 @@ void do_system_shutdown(shutdown_type_t shutdown_type) #elif defined(RB_HALT) if (shutdown_type == shutdown_type_t::HALT) reboot_type = RB_HALT; #endif +#if defined(RB_KEXEC) + if (shutdown_type == shutdown_type_t::KEXEC) reboot_type = RB_KEXEC; +#endif // Write to console rather than any terminal, since we lose the terminal it seems: int consfd = open("/dev/console", O_WRONLY); @@ -496,7 +532,16 @@ void do_system_shutdown(shutdown_type_t shutdown_type) sub_buf.append("Issuing shutdown via kernel...\n"); loop.poll(); // give message a chance to get to console - reboot(reboot_type); + if (reboot(reboot_type)) { + // we're in trouble now + sub_buf.append("reboot(2): "); + sub_buf.append(strerror(errno)); + sub_buf.append( + "\nThis may happen if you try to kexec without loading an image first, or if\n" + "\nsomehow a reboot type unsupported by the kernel is attempted\n" + ); + loop.poll(); + } } // Watcher for subprocess output.