Skip to content

Commit

Permalink
Add -k option to shutdown for kexec functionality
Browse files Browse the repository at this point in the history
  • Loading branch information
el-remph committed Feb 3, 2025
1 parent 712e1fa commit 630083d
Show file tree
Hide file tree
Showing 5 changed files with 63 additions and 5 deletions.
5 changes: 5 additions & 0 deletions doc/manpages/shutdown.8.m4
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
3 changes: 2 additions & 1 deletion src/control.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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<shutdown_type_t>(rbuf[1]);

services->stop_all_services(sd_type);
Expand Down
6 changes: 6 additions & 0 deletions src/dinit.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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.");
}
Expand Down Expand Up @@ -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";
Expand Down
5 changes: 3 additions & 2 deletions src/includes/service-constants.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down
49 changes: 47 additions & 2 deletions src/shutdown.cc
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,24 @@ class subproc_buffer : private cpbuffer<subproc_bufsize>
}
};

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)
{
Expand Down Expand Up @@ -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;
}
Expand All @@ -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"
Expand All @@ -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);
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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.
Expand Down

0 comments on commit 630083d

Please sign in to comment.