diff --git a/src/core/main.c b/src/core/main.c index c0f68222ed..5c02e30880 100644 --- a/src/core/main.c +++ b/src/core/main.c @@ -95,6 +95,7 @@ #include "proc-cmdline.h" #include "process-util.h" #include "random-util.h" +#include "reboot-util.h" #include "rlimit-util.h" #include "rm-rf.h" #include "seccomp-util.h" @@ -1861,6 +1862,52 @@ static int become_shutdown(int objective, int retval) { return -errno; } +static int fallback_shutdown(ManagerObjective objective, int retval) { + int cmd; + + /* Perform the requested shutdown operation ourselves, by issuing the matching reboot() directly. + * This skips the cleanup systemd-shutdown normally does (unmounting file systems, detaching storage, + * killing remaining processes, switching into the exitrd). */ + + switch (objective) { + + case MANAGER_KEXEC: + log_info("Rebooting with kexec."); + (void) kexec(); + /* If we are still here kexec didn't work, fall back to a plain reboot. */ + _fallthrough_; + + case MANAGER_REBOOT: + (void) reboot_with_parameter(REBOOT_LOG); + log_info("Rebooting."); + cmd = RB_AUTOBOOT; + break; + + case MANAGER_EXIT: + if (detect_container() > 0) { + log_info("Exiting container."); + exit(retval); + } + _fallthrough_; + + case MANAGER_POWEROFF: + log_info("Powering off."); + cmd = RB_POWER_OFF; + break; + + case MANAGER_HALT: + log_info("Halting system."); + cmd = RB_HALT_SYSTEM; + break; + + default: + assert_not_reached(); + } + + (void) reboot(cmd); + return log_error_errno(errno, "Failed to shut down: %m"); +} + static void initialize_clock_timewarp(void) { int r; @@ -3934,9 +3981,20 @@ finish: * If we failed above, we want to freeze after finishing cleanup. */ if (arg_runtime_scope == RUNTIME_SCOPE_SYSTEM && IN_SET(r, MANAGER_EXIT, MANAGER_REBOOT, MANAGER_POWEROFF, MANAGER_HALT, MANAGER_KEXEC)) { - r = become_shutdown(r, retval); - log_error_errno(r, "Failed to execute shutdown binary, %s: %m", getpid_cached() == 1 ? "freezing" : "quitting"); - error_message = "Failed to execute shutdown binary"; + ManagerObjective objective = r; + + r = become_shutdown(objective, retval); + assert(r < 0); + log_full_errno(r == -ENOENT ? LOG_DEBUG : LOG_WARNING, r, + "Failed to execute shutdown binary: %m"); + + /* As PID 1, carry out the requested operation ourselves rather than relying on the shutdown + * binary. This only returns if that didn't work, in which case we freeze/exit/reboot + * below. */ + r = fallback_shutdown(objective, retval); + assert(r < 0); + log_error_errno(r, "Failed to perform shutdown: %m"); + error_message = "Failed to perform shutdown"; } /* This is primarily useful when running systemd in a VM, as it provides the user running the VM with