#define SPECIAL_HALT_TARGET "halt.target"
#define SPECIAL_POWEROFF_TARGET "poweroff.target"
#define SPECIAL_REBOOT_TARGET "reboot.target"
+#define SPECIAL_SOFT_REBOOT_TARGET "soft-reboot.target"
#define SPECIAL_KEXEC_TARGET "kexec.target"
#define SPECIAL_EXIT_TARGET "exit.target"
#define SPECIAL_SUSPEND_TARGET "suspend.target"
return sd_bus_reply_method_return(message, NULL);
}
+static int method_soft_reboot(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ _cleanup_free_ char *rt = NULL;
+ Manager *m = ASSERT_PTR(userdata);
+ const char *root;
+ int r;
+
+ assert(message);
+
+ r = verify_run_space_permissive("soft reboot may fail", error);
+ if (r < 0)
+ return r;
+
+ r = mac_selinux_access_check(message, "reboot", error);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_read(message, "s", &root);
+ if (r < 0)
+ return r;
+
+ if (!isempty(root)) {
+ if (!path_is_valid(root))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
+ "New root directory '%s' must be a valid path.", root);
+ if (!path_is_absolute(root))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
+ "New root directory path '%s' is not absolute.", root);
+
+ rt = strdup(root);
+ if (!rt)
+ return -ENOMEM;
+ }
+
+ free_and_replace(m->switch_root, rt);
+ m->objective = MANAGER_SOFT_REBOOT;
+
+ return sd_bus_reply_method_return(message, NULL);
+}
+
static int method_poweroff(sd_bus_message *message, void *userdata, sd_bus_error *error) {
Manager *m = ASSERT_PTR(userdata);
int r;
static int method_switch_root(sd_bus_message *message, void *userdata, sd_bus_error *error) {
_cleanup_free_ char *ri = NULL, *rt = NULL;
- const char *root, *init;
Manager *m = ASSERT_PTR(userdata);
+ const char *root, *init;
int r;
assert(message);
NULL,
method_reboot,
SD_BUS_VTABLE_CAPABILITY(CAP_SYS_BOOT)),
+ SD_BUS_METHOD_WITH_ARGS("SoftReboot",
+ SD_BUS_ARGS("s", new_root),
+ SD_BUS_NO_RESULT,
+ method_soft_reboot,
+ SD_BUS_VTABLE_CAPABILITY(CAP_SYS_BOOT)),
SD_BUS_METHOD("PowerOff",
NULL,
NULL,
[EMERGENCY_ACTION_POWEROFF_IMMEDIATE] = "poweroff-immediate",
[EMERGENCY_ACTION_EXIT] = "exit",
[EMERGENCY_ACTION_EXIT_FORCE] = "exit-force",
+ [EMERGENCY_ACTION_SOFT_REBOOT] = "soft-reboot",
+ [EMERGENCY_ACTION_SOFT_REBOOT_FORCE] = "soft-reboot-force",
};
static void log_and_status(Manager *m, bool warn, const char *message, const char *reason) {
assert(action < _EMERGENCY_ACTION_MAX);
/* Is the special shutdown target active or queued? If so, we are in shutdown state */
- if (IN_SET(action, EMERGENCY_ACTION_REBOOT, EMERGENCY_ACTION_POWEROFF, EMERGENCY_ACTION_EXIT)) {
+ if (IN_SET(action, EMERGENCY_ACTION_REBOOT, EMERGENCY_ACTION_SOFT_REBOOT, EMERGENCY_ACTION_POWEROFF, EMERGENCY_ACTION_EXIT)) {
u = manager_get_unit(m, SPECIAL_SHUTDOWN_TARGET);
if (u && unit_active_or_pending(u)) {
log_notice("Shutdown is already active. Skipping emergency action request %s.",
(void) update_reboot_parameter_and_warn(reboot_arg, true);
m->objective = MANAGER_REBOOT;
-
break;
case EMERGENCY_ACTION_REBOOT_IMMEDIATE:
(void) reboot(RB_AUTOBOOT);
break;
+ case EMERGENCY_ACTION_SOFT_REBOOT:
+ log_and_status(m, warn, "Soft-rebooting", reason);
+
+ (void) manager_add_job_by_name_and_warn(m, JOB_START, SPECIAL_SOFT_REBOOT_TARGET, JOB_REPLACE_IRREVERSIBLY, NULL, NULL);
+ break;
+
+ case EMERGENCY_ACTION_SOFT_REBOOT_FORCE:
+ log_and_status(m, warn, "Forcibly soft-rebooting", reason);
+
+ m->objective = MANAGER_SOFT_REBOOT;
+ break;
+
case EMERGENCY_ACTION_EXIT:
if (exit_status >= 0)
EMERGENCY_ACTION_EXIT,
_EMERGENCY_ACTION_FIRST_USER_ACTION = EMERGENCY_ACTION_EXIT,
EMERGENCY_ACTION_EXIT_FORCE,
+ EMERGENCY_ACTION_SOFT_REBOOT,
+ EMERGENCY_ACTION_SOFT_REBOOT_FORCE,
_EMERGENCY_ACTION_MAX,
_EMERGENCY_ACTION_INVALID = -EINVAL,
} EmergencyAction;
const char **args;
int r;
- assert(IN_SET(objective, MANAGER_REEXECUTE, MANAGER_SWITCH_ROOT));
+ assert(IN_SET(objective, MANAGER_REEXECUTE, MANAGER_SWITCH_ROOT, MANAGER_SOFT_REBOOT));
assert(argc >= 0);
assert(saved_rlimit_nofile);
assert(saved_rlimit_memlock);
if (saved_rlimit_memlock->rlim_cur != RLIM_INFINITY)
(void) setrlimit(RLIMIT_MEMLOCK, saved_rlimit_memlock);
- if (switch_root_dir) {
- /* Kill all remaining processes from the initrd, but don't wait for them, so that we can
- * handle the SIGCHLD for them after deserializing. */
- broadcast_signal(SIGTERM, false, true, arg_default_timeout_stop_usec);
+ /* Kill all remaining processes from the initrd, but don't wait for them, so that we can handle the
+ * SIGCHLD for them after deserializing. */
+ if (IN_SET(objective, MANAGER_SWITCH_ROOT, MANAGER_SOFT_REBOOT))
+ broadcast_signal(SIGTERM, /* wait_for_exit= */ false, /* send_sighup= */ true, arg_default_timeout_stop_usec);
+
+ if (!switch_root_dir && objective == MANAGER_SOFT_REBOOT) {
+ /* If no switch root dir is specified, then check if /run/nextroot/ qualifies and use that */
+ r = path_is_os_tree("/run/nextroot");
+ if (r < 0 && r != -ENOENT)
+ log_debug_errno(r, "Failed to determine if /run/nextroot/ is a valid OS tree, ignoring: %m");
+ else if (r > 0)
+ switch_root_dir = "/run/nextroot";
+ }
+ if (switch_root_dir) {
/* And switch root with MS_MOVE, because we remove the old directory afterwards and detach it. */
- r = switch_root(switch_root_dir, /* old_root_after= */ NULL, MS_MOVE);
+ r = switch_root(/* new_root= */ switch_root_dir,
+ /* old_root_after= */ NULL,
+ MS_MOVE,
+ /* destroy_old_root= */ objective == MANAGER_SWITCH_ROOT);
if (r < 0)
log_error_errno(r, "Failed to switch root, trying to continue: %m");
}
i = 1; /* Leave args[0] empty for now. */
filter_args(args, &i, argv, argc);
- if (switch_root_dir)
+ if (IN_SET(objective, MANAGER_SWITCH_ROOT, MANAGER_SOFT_REBOOT))
args[i++] = "--switched-root";
args[i++] = runtime_scope_cmdline_option_to_string(arg_runtime_scope);
args[i++] = sfd;
return objective;
+ case MANAGER_SOFT_REBOOT:
+ manager_send_reloading(m);
+ manager_set_switching_root(m, true);
+
+ r = prepare_reexecute(m, &arg_serialization, ret_fds, /* switching_root= */ true);
+ if (r < 0) {
+ *ret_error_message = "Failed to prepare for reexecution";
+ return r;
+ }
+
+ log_notice("Soft-rebooting.");
+
+ *ret_retval = EXIT_SUCCESS;
+ *ret_switch_root_dir = TAKE_PTR(m->switch_root);
+ *ret_switch_root_init = NULL;
+
+ return objective;
+
case MANAGER_EXIT:
if (MANAGER_IS_USER(m)) {
log_debug("Exit.");
MANAGER_RELOAD,
MANAGER_REEXECUTE,
MANAGER_REBOOT,
+ MANAGER_SOFT_REBOOT,
MANAGER_POWEROFF,
MANAGER_HALT,
MANAGER_KEXEC,
mac_selinux_finish();
- if (IN_SET(r, MANAGER_REEXECUTE, MANAGER_SWITCH_ROOT))
+ if (IN_SET(r, MANAGER_REEXECUTE, MANAGER_SWITCH_ROOT, MANAGER_SOFT_REBOOT))
r = do_reexecute(r,
argc, argv,
&saved_rlimit_nofile,
SIGRTMIN+4, /* systemd: start poweroff.target */
SIGRTMIN+5, /* systemd: start reboot.target */
SIGRTMIN+6, /* systemd: start kexec.target */
+ SIGRTMIN+7, /* systemd: start soft-reboot.target */
/* ... space for more special targets ... */
SIGRTMIN+14, /* systemd: Immediate poweroff */
SIGRTMIN+15, /* systemd: Immediate reboot */
SIGRTMIN+16, /* systemd: Immediate kexec */
-
- /* ... space for one more immediate system state change ... */
-
+ SIGRTMIN+17, /* systemd: Immediate soft-reboot */
SIGRTMIN+18, /* systemd: control command */
/* ... space ... */
unit_vtable[c]->shutdown(m);
/* Keep the cgroup hierarchy in place except when we know we are going down for good */
- manager_shutdown_cgroup(m, IN_SET(m->objective, MANAGER_EXIT, MANAGER_REBOOT, MANAGER_POWEROFF, MANAGER_HALT, MANAGER_KEXEC));
+ manager_shutdown_cgroup(m, /* delete= */ IN_SET(m->objective, MANAGER_EXIT, MANAGER_REBOOT, MANAGER_POWEROFF, MANAGER_HALT, MANAGER_KEXEC));
lookup_paths_flush_generator(&m->lookup_paths);
const char *target;
JobMode mode;
} target_table[] = {
- [0] = { SPECIAL_DEFAULT_TARGET, JOB_ISOLATE },
- [1] = { SPECIAL_RESCUE_TARGET, JOB_ISOLATE },
- [2] = { SPECIAL_EMERGENCY_TARGET, JOB_ISOLATE },
- [3] = { SPECIAL_HALT_TARGET, JOB_REPLACE_IRREVERSIBLY },
- [4] = { SPECIAL_POWEROFF_TARGET, JOB_REPLACE_IRREVERSIBLY },
- [5] = { SPECIAL_REBOOT_TARGET, JOB_REPLACE_IRREVERSIBLY },
- [6] = { SPECIAL_KEXEC_TARGET, JOB_REPLACE_IRREVERSIBLY },
+ [0] = { SPECIAL_DEFAULT_TARGET, JOB_ISOLATE },
+ [1] = { SPECIAL_RESCUE_TARGET, JOB_ISOLATE },
+ [2] = { SPECIAL_EMERGENCY_TARGET, JOB_ISOLATE },
+ [3] = { SPECIAL_HALT_TARGET, JOB_REPLACE_IRREVERSIBLY },
+ [4] = { SPECIAL_POWEROFF_TARGET, JOB_REPLACE_IRREVERSIBLY },
+ [5] = { SPECIAL_REBOOT_TARGET, JOB_REPLACE_IRREVERSIBLY },
+ [6] = { SPECIAL_KEXEC_TARGET, JOB_REPLACE_IRREVERSIBLY },
+ [7] = { SPECIAL_SOFT_REBOOT_TARGET, JOB_REPLACE_IRREVERSIBLY },
};
/* Starting SIGRTMIN+13, so that target halt and system halt are 10 apart */
[1] = MANAGER_POWEROFF,
[2] = MANAGER_REBOOT,
[3] = MANAGER_KEXEC,
+ [4] = MANAGER_SOFT_REBOOT,
};
if ((int) sfsi.ssi_signo >= SIGRTMIN+0 &&
MANAGER_RELOAD,
MANAGER_REEXECUTE,
MANAGER_REBOOT,
+ MANAGER_SOFT_REBOOT,
MANAGER_POWEROFF,
MANAGER_HALT,
MANAGER_KEXEC,
int switch_root(const char *new_root,
const char *old_root_after, /* path below the new root, where to place the old root after the transition; may be NULL to unmount it */
- unsigned long mount_flags) { /* MS_MOVE or MS_BIND used for /proc/, /dev/, /run/, /sys/ */
+ unsigned long mount_flags, /* MS_MOVE or MS_BIND used for /proc/, /dev/, /run/, /sys/ */
+ bool destroy_old_root) {
_cleanup_close_ int old_root_fd = -EBADF, new_root_fd = -EBADF;
_cleanup_free_ char *resolved_old_root_after = NULL;
return log_error_errno(errno, "Failed to change directory: %m");
}
- if (istmp) {
+ if (istmp && destroy_old_root) {
struct stat rb;
if (fstat(old_root_fd, &rb) < 0)
#include <stdbool.h>
-int switch_root(const char *new_root, const char *old_root_after, unsigned long mount_flags);
+int switch_root(const char *new_root, const char *old_root_after, unsigned long mount_flags, bool destroy_old_root);
* /run/initramfs/shutdown will take care of these.
* Also do not detach the old root, because /run/initramfs/shutdown needs to access it.
*/
- return switch_root("/run/initramfs", "/oldroot", MS_BIND);
+ return switch_root(
+ /* new_root= */ "/run/initramfs",
+ /* old_root_after= */ "/oldroot",
+ MS_BIND,
+ /* destroy_old_root= */ false);
}
/* Read the following fields from /proc/meminfo:
['proc-sys-fs-binfmt_misc.mount', 'ENABLE_BINFMT'],
['reboot.target', '',
'ctrl-alt-del.target' + (with_runlevels ? ' runlevel6.target' : '')],
+ ['soft-reboot.target', ''],
['remote-cryptsetup.target', 'HAVE_LIBCRYPTSETUP',
'initrd-root-device.target.wants/'],
['remote-veritysetup.target', 'HAVE_LIBCRYPTSETUP',
['systemd-networkd.socket', 'ENABLE_NETWORKD'],
['systemd-poweroff.service', ''],
['systemd-reboot.service', ''],
+ ['systemd-soft-reboot.service', ''],
['systemd-rfkill.socket', 'ENABLE_RFKILL'],
['systemd-sysext.service', 'ENABLE_SYSEXT'],
['systemd-confext.service', 'ENABLE_SYSEXT'],
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+#
+# This file is part of systemd.
+#
+# systemd is free software; you can redistribute it and/or modify it
+# under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation; either version 2.1 of the License, or
+# (at your option) any later version.
+
+[Unit]
+Description=Reboot System Userspace
+Documentation=man:systemd.special(7)
+DefaultDependencies=no
+Requires=systemd-soft-reboot.service
+After=systemd-soft-reboot.service
+AllowIsolate=yes
+JobTimeoutSec=30min
+JobTimeoutAction=soft-reboot-force
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+#
+# This file is part of systemd.
+#
+# systemd is free software; you can redistribute it and/or modify it
+# under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation; either version 2.1 of the License, or
+# (at your option) any later version.
+
+[Unit]
+Description=Reboot System Userspace
+Documentation=man:systemd-soft-reboot.service(8)
+DefaultDependencies=no
+Requires=shutdown.target umount.target final.target
+After=shutdown.target umount.target final.target
+SuccessAction=soft-reboot-force