Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add -k option to shutdown for kexec functionality #436

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions doc/manpages/shutdown.8.m4
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,13 @@ 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, without firmware
reinitialisation.
A suitable kernel image must be loaded first; see \fBkexec\fR(8).
This is only supported on Linux 2.6.13 or later with \fBCONFIG_KEXEC=y\fR; if
you're unsure, most distribution kernels qualify.
.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 {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove this, unless there's a good reason for it that I can't see?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The reason is that shutdown_type_t is cast to char to be piped to the daemon, and the default cast is narrowing from enum: int. I thought this was a good middle ground rather than ditching all type checking with the cast, and good to be reminded of what the type will be used for. But it isn't essential

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
55 changes: 53 additions & 2 deletions src/shutdown.cc
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,27 @@ 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 +308,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 @@ -306,8 +330,15 @@ int main(int argc, char **argv)
" --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 +349,12 @@ 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
}

if (reboot_cmd_unsupported(shutdown_type)) {
cerr << "Unsupported shutdown type\n";
return 1;
}

signal(SIGPIPE, SIG_IGN);
Expand Down Expand Up @@ -421,6 +457,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 @@ -499,7 +538,19 @@ void do_system_shutdown(shutdown_type_t shutdown_type)
#ifdef __NetBSD__
reboot(reboot_type, NULL);
#else
reboot(reboot_type);
if (reboot(reboot_type)) {
// we're in trouble now
sub_buf.append("reboot: ");
sub_buf.append(strerror(errno));
if (shutdown_type == shutdown_type_t::KEXEC) {
sub_buf.append(
"\nIt is possible that no suitable kernel image was loaded before"
"\nreboot with kexec was attempted.\n"
);
}
while (true)
loop.run();
}
#endif
}

Expand Down