diff --git a/TODO b/TODO
index 79643583dd..4c32a4a4ca 100644
--- a/TODO
+++ b/TODO
@@ -153,6 +153,10 @@ Features:
dont), of sd-stub and data supplied by user. Then measure sbat too in
sd-stub, explicitly.
+* figure out what to do about credentials sealed to PCRs in kexec + soft-reboot
+ scenarios. Maybe insist sealing is done additionally against some keypair in
+ the TPM to which access is updated on each boot, for the next, or so?
+
* open up creds for uses in generators, and document clearly that encrypted
creds are only supported if strictly tpm bound, but not when using the host
secret (as that is only avilable if /var/ is around.
@@ -162,6 +166,11 @@ Features:
idea, and specifically works around the fact the autofs ignores busy by mount
namespaces)
+* refuse using the switch-root operation without /etc/initrd-release. Now
+ that we have a concept of userspace reboot, we can clearly say: switch-root
+ is for transitioning from initrd to host (or initrd to next initrd), while
+ userspace reboot is for switching host to next version of the host.
+
* mount most file systems with a restrictive uidmap. e.g. mount /usr/ with a
uidmap that blocks out anything outside 0…1000 (i.e. system users) and similar.
diff --git a/man/org.freedesktop.login1.xml b/man/org.freedesktop.login1.xml
index 034fd1078f..867662e057 100644
--- a/man/org.freedesktop.login1.xml
+++ b/man/org.freedesktop.login1.xml
@@ -576,16 +576,15 @@ node /org/freedesktop/login1 {
PowerOff(), Reboot(), Halt(),
Suspend(), and Hibernate() result in the system being powered
- off, rebooted, halted (shut down without turning off power), suspended (the system state is
- saved to RAM and the CPU is turned off), or hibernated (the system state is saved to disk and
- the machine is powered down). HybridSleep() results in the system entering a
- hybrid-sleep mode, i.e. the system is both hibernated and suspended.
- SuspendThenHibernate() results in the system being suspended, then later woken
- using an RTC timer and hibernated. The only argument is the polkit interactivity boolean
- interactive (see below). The main purpose of these calls is that they enforce
- polkit policy and hence allow powering off/rebooting/suspending/hibernating even by unprivileged
- users. They also enforce inhibition locks for non-privileged users. UIs should expose these calls
- as the primary mechanism to poweroff/reboot/suspend/hibernate the machine. Methods
+ off, rebooted, halted (shut down without turning off power), suspended (the system state is saved to
+ RAM and the CPU is turned off), or hibernated (the system state is saved to disk and the machine is
+ powered down). HybridSleep() results in the system entering a hybrid-sleep mode,
+ i.e. the system is both hibernated and suspended. SuspendThenHibernate() results
+ in the system being suspended, then later woken using an RTC timer and hibernated. The only argument is
+ the polkit interactivity boolean interactive (see below). The main purpose of these
+ calls is that they enforce polkit policy and hence allow powering off/rebooting/suspending/hibernating
+ even by unprivileged users. They also enforce inhibition locks for non-privileged users. UIs should
+ expose these calls as the primary mechanism to poweroff/reboot/suspend/hibernate the machine. Methods
PowerOffWithFlags(), RebootWithFlags(),
HaltWithFlags(), SuspendWithFlags(),
HibernateWithFlags(), HybridSleepWithFlags() and
@@ -594,12 +593,14 @@ node /org/freedesktop/login1 {
#define SD_LOGIND_ROOT_CHECK_INHIBITORS (UINT64_C(1) << 0)
#define SD_LOGIND_KEXEC_REBOOT (UINT64_C(1) << 1)
+#define SD_LOGIND_SOFT_REBOOT (UINT64_C(1) << 2)
- When the flags is 0 then these methods behave just like the versions
- without flags. When SD_LOGIND_ROOT_CHECK_INHIBITORS (0x01) is set, active
- inhibitors are honoured for privileged users too. When SD_LOGIND_KEXEC_REBOOT
- (0x02) is set, then RebootWithFlags() perform kexec reboot if kexec
- kernel is loaded.
+ When the flags is 0 then these methods behave just like the versions without
+ flags. When SD_LOGIND_ROOT_CHECK_INHIBITORS (0x01) is set, active inhibitors are
+ honoured for privileged users too. When SD_LOGIND_KEXEC_REBOOT (0x02) is set, then
+ RebootWithFlags() performs a kexec reboot if kexec kernel is loaded. When
+ SD_LOGIND_SOFT_REBOOT (0x04) is set, then RebootWithFlags()
+ performs a userspace reboot only.SetRebootParameter() sets a parameter for a subsequent reboot operation.
See the description of reboot in
diff --git a/man/org.freedesktop.systemd1.xml b/man/org.freedesktop.systemd1.xml
index 0835481f37..a08108dd88 100644
--- a/man/org.freedesktop.systemd1.xml
+++ b/man/org.freedesktop.systemd1.xml
@@ -183,6 +183,8 @@ node /org/freedesktop/systemd1 {
@org.freedesktop.systemd1.Privileged("true")
Reboot();
@org.freedesktop.systemd1.Privileged("true")
+ SoftReboot(in s new_root);
+ @org.freedesktop.systemd1.Privileged("true")
PowerOff();
@org.freedesktop.systemd1.Privileged("true")
Halt();
@@ -912,6 +914,8 @@ node /org/freedesktop/systemd1 {
+
+
@@ -1415,15 +1419,18 @@ node /org/freedesktop/systemd1 {
Exit() may be invoked to ask the manager to exit. This is not available for
the system manager and is useful only for user session managers.
- Reboot(), PowerOff(), Halt(), or
- KExec() may be used to ask for immediate reboot, powering down, halt or kexec
- based reboot of the system. Note that this does not shut down any services and immediately transitions
- into the reboot process. These functions are normally only called as the last step of shutdown and should
- not be called directly. To shut down the machine, it is generally a better idea to invoke
- Reboot() or PowerOff() on the
+ Reboot(), PowerOff(), Halt(),
+ KExec() and SoftReboot() may be used to ask for immediate
+ reboot, powering down, halt, kexec based reboot, or soft reboot of the system. Note that this does not
+ shut down any services and immediately transitions into the later shutdown operation. These functions
+ are normally only called as the last step of shutdown and should not be called directly. To shut down
+ the machine, it is generally a better idea to invoke Reboot(),
+ RebootWithFlags() or PowerOff() on the
systemd-logind manager object; see
org.freedesktop.login15
- for more information.
+ for more information. SoftReboot() accepts an argument indicating the path for the
+ root file system to activate for the next boot cycle. If an empty string is specified the
+ /run/nextroot/ path is used if it exists.SwitchRoot() may be used to transition to a new root directory. This is
intended to be used in the initrd, and also to transition from the host system into a shutdown initrd.
diff --git a/man/rules/meson.build b/man/rules/meson.build
index 4658ef99f0..166d8a5e22 100644
--- a/man/rules/meson.build
+++ b/man/rules/meson.build
@@ -1036,6 +1036,7 @@ manpages = [
['systemd-sleep.conf', '5', ['sleep.conf.d'], ''],
['systemd-socket-activate', '1', [], ''],
['systemd-socket-proxyd', '8', [], ''],
+ ['systemd-soft-reboot.service', '8', [], ''],
['systemd-stdio-bridge', '1', [], ''],
['systemd-stub',
'7',
diff --git a/man/systemctl.xml b/man/systemctl.xml
index e54dd22aae..29e5fc65c2 100644
--- a/man/systemctl.xml
+++ b/man/systemctl.xml
@@ -1559,6 +1559,24 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
+
+ soft-reboot
+
+
+ Shut down and reboot userspace. This is equivalent to systemctl start
+ soft-reboot.target --job-mode=replace-irreversibly --no-block. This command is
+ asynchronous; it will return after the reboot operation is enqueued, without waiting for it to
+ complete.
+
+ This command honors and in a similar way
+ as halt.
+
+ This operation only reboots userspace, leaving the kernel running. See
+ systemd-soft-reboot.service8
+ for details.
+
+
+
exitEXIT_CODE
diff --git a/man/systemd-poweroff.service.xml b/man/systemd-poweroff.service.xml
index 98c20471da..b430170bec 100644
--- a/man/systemd-poweroff.service.xml
+++ b/man/systemd-poweroff.service.xml
@@ -68,6 +68,10 @@
Note that systemd-poweroff.service (and the related units) should never be
executed directly. Instead, trigger system shutdown with a command such as systemctl
poweroff.
+
+ Another form of shutdown is provided by the
+ systemd-soft-reboot.service8
+ functionality. It reboots only the OS userspace, leaving the kernel, firmware, and hardware as it is.
@@ -78,6 +82,7 @@
systemd.special7,
reboot2,
systemd-suspend.service8,
+ systemd-soft-reboot.service8,
bootup7
diff --git a/man/systemd-soft-reboot.service.xml b/man/systemd-soft-reboot.service.xml
new file mode 100644
index 0000000000..0e95cba3be
--- /dev/null
+++ b/man/systemd-soft-reboot.service.xml
@@ -0,0 +1,158 @@
+
+
+
+
+
+
+
+ systemd-soft-reboot.service
+ systemd
+
+
+
+ systemd-soft-reboot.service
+ 8
+
+
+
+ systemd-soft-reboot.service
+ Userspace reboot operation
+
+
+
+ systemd-soft-reboot.service
+
+
+
+ Description
+
+ systemd-soft-reboot.service is a system service that is pulled in by
+ soft-reboot.target and is responsible for performing a userspace-only reboot
+ operation. When invoked, it will send the SIGTERM signal to any processes left
+ running (but does not follow up with SIGKILL, and does not wait for the processes to
+ exit). If the /run/nextroot/ directory exists (which may be a regular directory, a
+ directory mount point or a symlink to either) then it will switch the file system root to it. It then
+ reexecutes the service manager off the (possibly now new) root file system, which will enqueue a new boot
+ transaction as in a normal reboot.
+
+ Such a userspace-only reboot operation permits updating or resetting the entirety of userspace with
+ minimal downtime, as the reboot operation does not transition through:
+
+
+ The second phase of regular shutdown, as implemented by
+ systemd-shutdown8.
+
+ The third phase of regular shutdown, i.e. the return to the initrd
+ context
+
+ The hardware reboot operation
+
+ The firmware initialization
+
+ The boot loader initialization
+
+ The kernel initialization
+
+ The initrd initialization
+
+
+ However this form of reboot comes with drawbacks as well:
+
+
+ The OS update remains incomplete, as the kernel is not reset and continues
+ running.
+
+ Kernel settings (such as /proc/sys/ settings, a.k.a. "sysctl", or
+ /sys/ settings) are not reset.
+
+
+ These limitations may be addressed by various means, which are outside of the scope of this
+ documentation, such as kernel live-patching and sufficiently comprehensive
+ /etc/sysctl.d/ files.
+
+
+
+ Resource Pass-Through
+
+ Various runtime OS resources can passed from a system runtime to the next, through the userspace
+ reboot operation. Specificially:
+
+
+ File descriptors placed in the file descriptor store of services that remain active
+ until the very end are passed to the next boot, where they are placed in the file descriptor store of
+ the same unit. For this to work, units must declare DefaultDependencies=no (and
+ avoid a manual Conflicts=shutdown.target or similar) to ensure they are not
+ terminated as usual during the system shutdown operation. Alternatively, use
+ FileDescriptorStorePreserve= to allow the file descriptor store to remain pinned
+ even when the unit is down. See
+ systemd.service5 for
+ details about the file descriptor store.
+
+ Similar to this, file descriptors associated with .socket units
+ remain open (and connectible) if the units are not stopped during the transition. (Achieved by
+ DefaultDependencies=no.)
+
+ The /run/ file system remains mounted and populated and may be
+ used to pass state information between such userspace reboot cycles.
+
+ Service processes may continue to run over the transition, if they are placed in
+ services that remain active until the very end of shutdown (which again is achieved via
+ DefaultDependencies=no). They must also be set up to avoid being killed by the
+ aforementioned SIGTERM spree (as per systemd and Storage Daemons for the Root File
+ System).
+
+ File system mounts may remain mounted during the transition, and complex storage
+ attached, if configured to remain until the very end of the shutdown process. (Also achieved via
+ DefaultDependencies=no, and by avoiding
+ Conflicts=umount.target)
+
+
+ Even though passing resources from one soft reboot cycle to the next is possible this way, we
+ strongly suggest to use this functionality sparingly only, as it creates a more fragile system as
+ resources from different versions of the OS and applications might be mixed with unforeseen
+ consequences. In particular it's recommended to avoid allowing processes to survive
+ the soft reboot operation, as this means code updates will necessarily be incomplete, and processes
+ typically pin various other resources (such as the file system they are backed by), thus increasing
+ memory usage (as two versions of the OS/application/file system might be kept in memory). Leaving
+ processes running during a soft-reboot operation requires disconnecting the service comprehensively from
+ the rest of the OS, i.e. minimizing IPC and reducing sharing of resources with the rest of the OS. A
+ possible mechanism to achieve this is the concept of Portable Services.
+
+ If units shall be left running until the very end of shutdown during a soft reboot operation, but
+ shall be terminated regularly during other forms of shutdown, it's recommended to set
+ DefaultDependencies=no and then place
+ Conflicts=/Before= onto reboot.target,
+ kexec.target, poweroff.target and
+ halt.target (but not onto
+ soft-reboot.target).
+
+
+
+ Notes
+
+ Note that because
+ systemd-shutdown8 is
+ not executed, the executables in /usr/lib/systemd/system-shutdown/ are not executed
+ either.
+
+ Note that systemd-soft-reboot.service (and related units) should never be
+ executed directly. Instead, trigger system shutdown with a command such as systemctl
+ soft-reboot.
+
+
+
+ See Also
+
+ systemd1,
+ systemctl1,
+ systemd.special7,
+ systemd-poweroff.service8,
+ systemd-suspend.service8,
+ bootup7
+
+
+
+
diff --git a/man/systemd.mount.xml b/man/systemd.mount.xml
index 550f006601..0d14d702a8 100644
--- a/man/systemd.mount.xml
+++ b/man/systemd.mount.xml
@@ -437,8 +437,12 @@
An additional filesystem to be mounted in the initrd. See
initrd-fs.target description in
- systemd.special7.
-
+ systemd.special7. This
+ is both an indicator to the initrd to mount this partition early and an indicator to the host to
+ leave the partition mounted until final shutdown. Or in other words, if this flag is set it is
+ assumed the mount shall be active during the entire regular runtime of the system, i.e. established
+ before the initrd transitions into the host all the way until the host transitions to the final
+ shutdown phase.
diff --git a/man/systemd.special.xml b/man/systemd.special.xml
index 1620895511..c90e7156ed 100644
--- a/man/systemd.special.xml
+++ b/man/systemd.special.xml
@@ -81,6 +81,7 @@
slices.target,
smartcard.target,
sockets.target,
+ soft-reboot.target,
sound.target,
suspend.target,
swap.target,
@@ -442,15 +443,18 @@
kexec.target
- A special target unit for shutting down and rebooting
- the system via kexec.
+ A special target unit for shutting down and rebooting the system via kexec.
- Applications wanting to reboot the system should not start this unit
- directly, but should instead execute systemctl kexec
- (possibly with the option) or call
- systemd1's
- org.freedesktop.systemd1.Manager.KExec D-Bus method
+ Applications wanting to reboot the system should not start this unit directly, but should
+ instead execute systemctl kexec (possibly with the
+ option) or call
+ systemd-logind8's
+ org.freedesktop.login1.Manager.RebootWithFlags() D-Bus method
directly.
+
+ See
+ systemd-kexec.service8
+ for further details of the operation this target pulls in.
@@ -559,18 +563,20 @@
reboot.target
- A special target unit for shutting down and rebooting
- the system.
+ A special target unit for shutting down and rebooting the system.
- Applications wanting to reboot the system should not start this unit
- directly, but should instead execute systemctl reboot
- (possibly with the option) or call
+ Applications wanting to reboot the system should not start this unit directly, but should
+ instead execute systemctl reboot (possibly with the
+ option) or call
systemd-logind8's
- org.freedesktop.login1.Manager.Reboot D-Bus method
- directly.
+ org.freedesktop.login1.Manager.Reboot() D-Bus method directly.
- runlevel6.target is an alias for
- this target unit, for compatibility with SysV.
+ See
+ systemd-reboot.service8
+ for further details of the operation this target pulls in.
+
+ runlevel6.target is an alias for this target unit, for compatibility
+ with SysV.
@@ -703,6 +709,24 @@
section.
+
+ soft-reboot.target
+
+ A special target unit for shutting down and rebooting the userspace of the system (leaving
+ the kernel running).
+
+ Applications wanting to reboot the system should not start this unit directly, but should
+ instead execute systemctl soft-reboot (possibly with the
+ option) or call
+ systemd-logind8's
+ org.freedesktop.login1.Manager.RebootWithFlags() D-Bus method
+ directly.
+
+ See
+ systemd-soft-reboot.service8
+ for further details of the operation this target pulls in.
+
+ suspend.target
diff --git a/man/systemd.unit.xml b/man/systemd.unit.xml
index fcd1f914a8..77c3cd24f8 100644
--- a/man/systemd.unit.xml
+++ b/man/systemd.unit.xml
@@ -999,27 +999,34 @@
FailureAction=SuccessAction=
- Configure the action to take when the unit stops and enters a failed state or inactive state.
- Takes one of , , ,
- , , ,
- , , and . In system mode,
- all options are allowed. In user mode, only , , and
- are allowed. Both options default to .
+ Configure the action to take when the unit stops and enters a failed state or
+ inactive state. Takes one of , ,
+ , , ,
+ , , ,
+ , and . In
+ system mode, all options are allowed. In user mode, only ,
+ , , and
+ are allowed. Both options default to .
- If is set, no action will be triggered. causes a reboot
- following the normal shutdown procedure (i.e. equivalent to systemctl reboot).
- causes a forced reboot which will terminate all processes forcibly but should
- cause no dirty file systems on reboot (i.e. equivalent to systemctl reboot -f) and
- causes immediate execution of the
- reboot2 system call, which
- might result in data loss (i.e. equivalent to systemctl reboot -ff). Similarly,
- , , have the effect
- of powering down the system with similar semantics. causes the manager to exit following
- the normal shutdown procedure, and causes it terminate without shutting down
- services. When or is used by default the exit status of the
- main process of the unit (if this applies) is returned from the service manager. However, this may be overridden
- with FailureActionExitStatus=/SuccessActionExitStatus=, see
- below.
+ If is set, no action will be triggered. causes a
+ reboot following the normal shutdown procedure (i.e. equivalent to systemctl
+ reboot). causes a forced reboot which will terminate all
+ processes forcibly but should cause no dirty file systems on reboot (i.e. equivalent to
+ systemctl reboot -f) and causes immediate
+ execution of the
+ reboot2 system
+ call, which might result in data loss (i.e. equivalent to systemctl reboot
+ -ff). Similarly, , ,
+ have the effect of powering down the system with similar
+ semantics. causes the manager to exit following the normal shutdown procedure,
+ and causes it terminate without shutting down services. When
+ or is used by default the exit status of the main
+ process of the unit (if this applies) is returned from the service manager. However, this may be
+ overridden with
+ FailureActionExitStatus=/SuccessActionExitStatus=, see
+ below. will trigger a userspace reboot
+ operation. does that too, but does not go through the shutdown
+ transaction beforehand.
diff --git a/man/systemd.xml b/man/systemd.xml
index ca9e4e9988..95dc1fef83 100644
--- a/man/systemd.xml
+++ b/man/systemd.xml
@@ -519,6 +519,15 @@
+
+ SIGRTMIN+7
+
+ Reboots userspace, starts the soft-reboot.target unit. This is
+ mostly equivalent to systemctl start soft-reboot.target
+ --job-mode=replace-irreversibly.
+
+
+
SIGRTMIN+13
@@ -543,6 +552,12 @@
Immediately reboots the machine with kexec.
+
+ SIGRTMIN+17
+
+ Immediately reboots the userspace.
+
+
SIGRTMIN+20
diff --git a/src/basic/login-util.h b/src/basic/login-util.h
index 841fd324f1..613fb0f86f 100644
--- a/src/basic/login-util.h
+++ b/src/basic/login-util.h
@@ -6,11 +6,12 @@
#define SD_LOGIND_ROOT_CHECK_INHIBITORS (UINT64_C(1) << 0)
#define SD_LOGIND_REBOOT_VIA_KEXEC (UINT64_C(1) << 1)
+#define SD_LOGIND_SOFT_REBOOT (UINT64_C(1) << 2)
/* For internal use only */
#define SD_LOGIND_INTERACTIVE (UINT64_C(1) << 63)
-#define SD_LOGIND_SHUTDOWN_AND_SLEEP_FLAGS_PUBLIC (SD_LOGIND_ROOT_CHECK_INHIBITORS|SD_LOGIND_REBOOT_VIA_KEXEC)
+#define SD_LOGIND_SHUTDOWN_AND_SLEEP_FLAGS_PUBLIC (SD_LOGIND_ROOT_CHECK_INHIBITORS|SD_LOGIND_REBOOT_VIA_KEXEC|SD_LOGIND_SOFT_REBOOT)
#define SD_LOGIND_SHUTDOWN_AND_SLEEP_FLAGS_ALL (SD_LOGIND_SHUTDOWN_AND_SLEEP_FLAGS_PUBLIC|SD_LOGIND_INTERACTIVE)
bool session_id_valid(const char *id);
diff --git a/src/basic/special.h b/src/basic/special.h
index ed3852a450..98fcddf631 100644
--- a/src/basic/special.h
+++ b/src/basic/special.h
@@ -14,6 +14,7 @@
#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"
diff --git a/src/core/dbus-manager.c b/src/core/dbus-manager.c
index c02ad09c07..f156b809cb 100644
--- a/src/core/dbus-manager.c
+++ b/src/core/dbus-manager.c
@@ -1713,6 +1713,45 @@ static int method_reboot(sd_bus_message *message, void *userdata, sd_bus_error *
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;
@@ -1772,8 +1811,8 @@ static int method_kexec(sd_bus_message *message, void *userdata, sd_bus_error *e
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);
@@ -3260,6 +3299,11 @@ const sd_bus_vtable bus_manager_vtable[] = {
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,
diff --git a/src/core/emergency-action.c b/src/core/emergency-action.c
index 5b9ec75922..0b458ebf28 100644
--- a/src/core/emergency-action.c
+++ b/src/core/emergency-action.c
@@ -22,6 +22,8 @@ static const char* const emergency_action_table[_EMERGENCY_ACTION_MAX] = {
[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) {
@@ -47,7 +49,7 @@ void emergency_action(
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.",
@@ -80,7 +82,6 @@ void emergency_action(
(void) update_reboot_parameter_and_warn(reboot_arg, true);
m->objective = MANAGER_REBOOT;
-
break;
case EMERGENCY_ACTION_REBOOT_IMMEDIATE:
@@ -98,6 +99,18 @@ void emergency_action(
(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)
diff --git a/src/core/emergency-action.h b/src/core/emergency-action.h
index 89a1dead23..2c61c9c7d6 100644
--- a/src/core/emergency-action.h
+++ b/src/core/emergency-action.h
@@ -16,6 +16,8 @@ typedef enum EmergencyAction {
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;
diff --git a/src/core/main.c b/src/core/main.c
index 3eb53577eb..f067b13fff 100644
--- a/src/core/main.c
+++ b/src/core/main.c
@@ -1788,7 +1788,7 @@ static int do_reexecute(
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);
@@ -1823,13 +1823,24 @@ static int do_reexecute(
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);
- /* 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);
+ 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) {
+ r = switch_root(/* new_root= */ switch_root_dir,
+ /* old_root_after= */ NULL,
+ /* flags= */ objective == MANAGER_SWITCH_ROOT ? SWITCH_ROOT_DESTROY_OLD_ROOT : 0);
if (r < 0)
log_error_errno(r, "Failed to switch root, trying to continue: %m");
}
@@ -1851,7 +1862,7 @@ static int do_reexecute(
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;
@@ -2036,6 +2047,24 @@ static int invoke_main_loop(
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.");
@@ -3093,6 +3122,7 @@ int main(int argc, char *argv[]) {
MANAGER_RELOAD,
MANAGER_REEXECUTE,
MANAGER_REBOOT,
+ MANAGER_SOFT_REBOOT,
MANAGER_POWEROFF,
MANAGER_HALT,
MANAGER_KEXEC,
@@ -3109,7 +3139,7 @@ finish:
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,
diff --git a/src/core/manager.c b/src/core/manager.c
index 78d1a032e6..1df48d3cbc 100644
--- a/src/core/manager.c
+++ b/src/core/manager.c
@@ -561,6 +561,7 @@ static int manager_setup_signals(Manager *m) {
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 ... */
@@ -568,9 +569,7 @@ static int manager_setup_signals(Manager *m) {
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 ... */
@@ -1627,7 +1626,7 @@ Manager* manager_free(Manager *m) {
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);
@@ -2979,13 +2978,14 @@ static int manager_dispatch_signal_fd(sd_event_source *source, int fd, uint32_t
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 */
@@ -2994,6 +2994,7 @@ static int manager_dispatch_signal_fd(sd_event_source *source, int fd, uint32_t
[1] = MANAGER_POWEROFF,
[2] = MANAGER_REBOOT,
[3] = MANAGER_KEXEC,
+ [4] = MANAGER_SOFT_REBOOT,
};
if ((int) sfsi.ssi_signo >= SIGRTMIN+0 &&
diff --git a/src/core/manager.h b/src/core/manager.h
index 08dcee3ce7..a6c87dc852 100644
--- a/src/core/manager.h
+++ b/src/core/manager.h
@@ -44,6 +44,7 @@ typedef enum ManagerObjective {
MANAGER_RELOAD,
MANAGER_REEXECUTE,
MANAGER_REBOOT,
+ MANAGER_SOFT_REBOOT,
MANAGER_POWEROFF,
MANAGER_HALT,
MANAGER_KEXEC,
diff --git a/src/login/logind-action.c b/src/login/logind-action.c
index c6a36a1556..42dc36b670 100644
--- a/src/login/logind-action.c
+++ b/src/login/logind-action.c
@@ -67,6 +67,18 @@ static const HandleActionData handle_action_data_table[_HANDLE_ACTION_MAX] = {
.message = "System is rebooting with kexec",
.log_verb = "kexec",
},
+ [HANDLE_SOFT_REBOOT] = {
+ .handle = HANDLE_SOFT_REBOOT,
+ .target = SPECIAL_SOFT_REBOOT_TARGET,
+ .inhibit_what = INHIBIT_SHUTDOWN,
+ .polkit_action = "org.freedesktop.login1.reboot",
+ .polkit_action_multiple_sessions = "org.freedesktop.login1.reboot-multiple-sessions",
+ .polkit_action_ignore_inhibit = "org.freedesktop.login1.reboot-ignore-inhibit",
+ .sleep_operation = _SLEEP_OPERATION_INVALID,
+ .message_id = SD_MESSAGE_SHUTDOWN_STR,
+ .message = "System userspace is rebooting",
+ .log_verb = "soft-reboot",
+ },
[HANDLE_SUSPEND] = {
.handle = HANDLE_SUSPEND,
.target = SPECIAL_SUSPEND_TARGET,
@@ -133,6 +145,7 @@ int manager_handle_action(
[HANDLE_REBOOT] = "Rebooting...",
[HANDLE_HALT] = "Halting...",
[HANDLE_KEXEC] = "Rebooting via kexec...",
+ [HANDLE_SOFT_REBOOT] = "Rebooting userspace...",
[HANDLE_SUSPEND] = "Suspending...",
[HANDLE_HIBERNATE] = "Hibernating...",
[HANDLE_HYBRID_SLEEP] = "Hibernating and suspending...",
@@ -257,6 +270,7 @@ static const char* const handle_action_verb_table[_HANDLE_ACTION_MAX] = {
[HANDLE_REBOOT] = "reboot",
[HANDLE_HALT] = "halt",
[HANDLE_KEXEC] = "kexec",
+ [HANDLE_SOFT_REBOOT] = "soft-reboot",
[HANDLE_SUSPEND] = "suspend",
[HANDLE_HIBERNATE] = "hibernate",
[HANDLE_HYBRID_SLEEP] = "enter hybrid sleep",
@@ -273,6 +287,7 @@ static const char* const handle_action_table[_HANDLE_ACTION_MAX] = {
[HANDLE_REBOOT] = "reboot",
[HANDLE_HALT] = "halt",
[HANDLE_KEXEC] = "kexec",
+ [HANDLE_SOFT_REBOOT] = "soft-reboot",
[HANDLE_SUSPEND] = "suspend",
[HANDLE_HIBERNATE] = "hibernate",
[HANDLE_HYBRID_SLEEP] = "hybrid-sleep",
diff --git a/src/login/logind-action.h b/src/login/logind-action.h
index 9a01af1690..dfef6400e9 100644
--- a/src/login/logind-action.h
+++ b/src/login/logind-action.h
@@ -9,6 +9,7 @@ typedef enum HandleAction {
HANDLE_REBOOT,
HANDLE_HALT,
HANDLE_KEXEC,
+ HANDLE_SOFT_REBOOT,
HANDLE_SUSPEND,
HANDLE_HIBERNATE,
HANDLE_HYBRID_SLEEP,
diff --git a/src/login/logind-dbus.c b/src/login/logind-dbus.c
index c5e0fda5e5..e1e9b723c8 100644
--- a/src/login/logind-dbus.c
+++ b/src/login/logind-dbus.c
@@ -1860,9 +1860,19 @@ static int method_do_shutdown_or_sleep(
if ((flags & ~SD_LOGIND_SHUTDOWN_AND_SLEEP_FLAGS_PUBLIC) != 0)
return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS,
"Invalid flags parameter");
- if (a->handle != HANDLE_REBOOT && (flags & SD_LOGIND_REBOOT_VIA_KEXEC))
+
+ if (FLAGS_SET(flags, (SD_LOGIND_REBOOT_VIA_KEXEC|SD_LOGIND_SOFT_REBOOT)))
return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS,
- "Reboot via kexec is only applicable with reboot operations");
+ "Both reboot via kexec and soft reboot selected, which is not supported");
+
+ if (a->handle != HANDLE_REBOOT) {
+ if (flags & SD_LOGIND_REBOOT_VIA_KEXEC)
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS,
+ "Reboot via kexec option is only applicable with reboot operations");
+ if (flags & SD_LOGIND_SOFT_REBOOT)
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS,
+ "Soft reboot option is only applicable with reboot operations");
+ }
} else {
/* Old style method: no flags parameter, but interactive bool passed as boolean in
* payload. Let's convert this argument to the new-style flags parameter for our internal
@@ -1878,6 +1888,8 @@ static int method_do_shutdown_or_sleep(
if ((flags & SD_LOGIND_REBOOT_VIA_KEXEC) && kexec_loaded())
a = handle_action_lookup(HANDLE_KEXEC);
+ else if ((flags & SD_LOGIND_SOFT_REBOOT))
+ a = handle_action_lookup(HANDLE_SOFT_REBOOT);
/* Don't allow multiple jobs being executed at the same time */
if (m->delayed_action)
@@ -2221,7 +2233,7 @@ static int method_schedule_shutdown(sd_bus_message *message, void *userdata, sd_
}
handle = handle_action_from_string(type);
- if (!IN_SET(handle, HANDLE_POWEROFF, HANDLE_REBOOT, HANDLE_HALT, HANDLE_KEXEC))
+ if (!IN_SET(handle, HANDLE_POWEROFF, HANDLE_REBOOT, HANDLE_SOFT_REBOOT, HANDLE_HALT, HANDLE_KEXEC))
return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Unsupported shutdown type");
a = handle_action_lookup(handle);
diff --git a/src/shared/fstab-util.c b/src/shared/fstab-util.c
index d22cff2d82..b0e274afcc 100644
--- a/src/shared/fstab-util.c
+++ b/src/shared/fstab-util.c
@@ -47,6 +47,7 @@ bool fstab_is_extrinsic(const char *mount, const char *opts) {
if (PATH_STARTSWITH_SET(mount,
"/run/initramfs", /* This should stay around from before we boot until after we shutdown */
+ "/run/nextroot", /* Similar (though might be updated from the host) */
"/proc", /* All of this is API VFS */
"/sys", /* … dito … */
"/dev")) /* … dito … */
diff --git a/src/shared/mount-setup.c b/src/shared/mount-setup.c
index fd14cd8598..ede4e8483a 100644
--- a/src/shared/mount-setup.c
+++ b/src/shared/mount-setup.c
@@ -376,8 +376,9 @@ static int relabel_cb(
return RECURSE_DIR_CONTINUE;
case RECURSE_DIR_ENTER:
- /* /run/initramfs is static data and big, no need to dynamically relabel its contents at boot... */
- if (path_equal(path, "/run/initramfs"))
+ /* /run/initramfs/ + /run/nextroot/ are static data and big, no need to dynamically relabel
+ * its contents at boot... */
+ if (PATH_STARTSWITH_SET(path, "/run/initramfs", "/run/nextroot"))
return RECURSE_DIR_SKIP_ENTRY;
_fallthrough_;
diff --git a/src/shared/mount-util.c b/src/shared/mount-util.c
index 81b681afa7..366f59ee82 100644
--- a/src/shared/mount-util.c
+++ b/src/shared/mount-util.c
@@ -1099,6 +1099,24 @@ int make_mount_point(const char *path) {
return 1;
}
+int fd_make_mount_point(int fd) {
+ int r;
+
+ assert(fd >= 0);
+
+ r = fd_is_mount_point(fd, NULL, 0);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to determine whether file descriptor is a mount point: %m");
+ if (r > 0)
+ return 0;
+
+ r = mount_follow_verbose(LOG_DEBUG, FORMAT_PROC_FD_PATH(fd), FORMAT_PROC_FD_PATH(fd), NULL, MS_BIND|MS_REC, NULL);
+ if (r < 0)
+ return r;
+
+ return 1;
+}
+
int make_userns(uid_t uid_shift, uid_t uid_range, uid_t owner, RemountIdmapping idmapping) {
_cleanup_close_ int userns_fd = -EBADF;
_cleanup_free_ char *line = NULL;
diff --git a/src/shared/mount-util.h b/src/shared/mount-util.h
index b33e739b42..7214cf1e52 100644
--- a/src/shared/mount-util.h
+++ b/src/shared/mount-util.h
@@ -100,6 +100,7 @@ int bind_mount_in_namespace(pid_t target, const char *propagate_path, const char
int mount_image_in_namespace(pid_t target, const char *propagate_path, const char *incoming_path, const char *src, const char *dest, bool read_only, bool make_file_or_directory, const MountOptions *options, const ImagePolicy *image_policy);
int make_mount_point(const char *path);
+int fd_make_mount_point(int fd);
typedef enum RemountIdmapping {
REMOUNT_IDMAPPING_NONE,
diff --git a/src/shared/switch-root.c b/src/shared/switch-root.c
index c7b562143d..070d5bd908 100644
--- a/src/shared/switch-root.c
+++ b/src/shared/switch-root.c
@@ -10,6 +10,7 @@
#include "base-filesystem.h"
#include "chase.h"
+#include "creds-util.h"
#include "fd-util.h"
#include "initrd-util.h"
#include "log.h"
@@ -27,14 +28,26 @@
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/ */
+ SwitchRootFlags flags) {
+
+ struct {
+ const char *path;
+ unsigned long mount_flags;
+ } transfer_table[] = {
+ { "/dev", MS_BIND|MS_REC }, /* Recursive, because we want to save the original /dev/shm + /dev/pts and similar */
+ { "/sys", MS_BIND|MS_REC }, /* Similar, we want to retain various API VFS, or the cgroupv1 /sys/fs/cgroup/ tree */
+ { "/proc", MS_BIND|MS_REC }, /* Similar */
+ { "/run", MS_BIND }, /* Stuff mounted below this we don't save, as it might have lost its relevance, i.e. credentials, removable media and such, we rather want that the new boot mounts this fresh */
+ { SYSTEM_CREDENTIALS_DIRECTORY, MS_BIND }, /* Credentials passed into the system should survive */
+ { ENCRYPTED_SYSTEM_CREDENTIALS_DIRECTORY, MS_BIND }, /* Similar */
+ { "/run/host", MS_BIND|MS_REC }, /* Host supplied hierarchy should also survive */
+ };
_cleanup_close_ int old_root_fd = -EBADF, new_root_fd = -EBADF;
_cleanup_free_ char *resolved_old_root_after = NULL;
int r, istmp;
assert(new_root);
- assert(IN_SET(mount_flags, MS_MOVE, MS_BIND));
/* Check if we shall remove the contents of the old root */
old_root_fd = open("/", O_DIRECTORY|O_CLOEXEC);
@@ -53,11 +66,19 @@ int switch_root(const char *new_root,
return 0;
}
- istmp = fd_is_temporary_fs(old_root_fd);
- if (istmp < 0)
- return log_error_errno(istmp, "Failed to stat root directory: %m");
- if (istmp > 0)
- log_debug("Root directory is on tmpfs, will do cleanup later.");
+ /* Make the new root directory a mount point if it isn't */
+ r = fd_make_mount_point(new_root_fd);
+ if (r < 0)
+ return log_error_errno(r, "Failed to make new root directory a mount point: %m");
+
+ if (FLAGS_SET(flags, SWITCH_ROOT_DESTROY_OLD_ROOT)) {
+ istmp = fd_is_temporary_fs(old_root_fd);
+ if (istmp < 0)
+ return log_error_errno(istmp, "Failed to stat root directory: %m");
+ if (istmp > 0)
+ log_debug("Root directory is on tmpfs, will do cleanup later.");
+ } else
+ istmp = -1; /* don't know */
if (old_root_after) {
/* Determine where we shall place the old root after the transition */
@@ -73,7 +94,8 @@ int switch_root(const char *new_root,
* all while making them invisible/inaccessible in the file system tree for later code. That makes
* sync'ing them then difficult. Let's hence issue a manual sync() here, so that we at least can
* guarantee all file systems are an a good state before entering this state. */
- sync();
+ if (!FLAGS_SET(flags, SWITCH_ROOT_DONT_SYNC))
+ sync();
/* Work-around for kernel design: the kernel refuses MS_MOVE if any file systems are mounted
* MS_SHARED. Hence remount them MS_PRIVATE here as a work-around.
@@ -82,32 +104,35 @@ int switch_root(const char *new_root,
if (mount(NULL, "/", NULL, MS_REC|MS_PRIVATE, NULL) < 0)
return log_error_errno(errno, "Failed to set \"/\" mount propagation to private: %m");
- FOREACH_STRING(path, "/sys", "/dev", "/run", "/proc") {
- _cleanup_free_ char *chased = NULL;
-
- r = chase(path, new_root, CHASE_PREFIX_ROOT|CHASE_NONEXISTENT, &chased, NULL);
- if (r < 0)
- return log_error_errno(r, "Failed to resolve %s/%s: %m", new_root, path);
- if (r > 0) {
- /* Already exists. Let's see if it is a mount point already. */
- r = path_is_mount_point(chased, NULL, 0);
- if (r < 0)
- return log_error_errno(r, "Failed to determine whether %s is a mount point: %m", chased);
- if (r > 0) /* If it is already mounted, then do nothing */
- continue;
- } else
- /* Doesn't exist yet? */
- (void) mkdir_p_label(chased, 0755);
-
- if (mount(path, chased, NULL, mount_flags, NULL) < 0)
- return log_error_errno(errno, "Failed to mount %s to %s: %m", path, chased);
- }
-
/* Do not fail if base_filesystem_create() fails. Not all switch roots are like base_filesystem_create() wants
* them to look like. They might even boot, if they are RO and don't have the FS layout. Just ignore the error
* and switch_root() nevertheless. */
(void) base_filesystem_create_fd(new_root_fd, new_root, UID_INVALID, GID_INVALID);
+ FOREACH_ARRAY(transfer, transfer_table, ELEMENTSOF(transfer_table)) {
+ _cleanup_free_ char *chased = NULL;
+
+ if (access(transfer->path, F_OK) < 0) {
+ log_debug_errno(errno, "Path '%s' to move to target root directory, not found, ignoring: %m", transfer->path);
+ continue;
+ }
+
+ r = chase(transfer->path, new_root, CHASE_PREFIX_ROOT, &chased, NULL);
+ if (r < 0)
+ return log_error_errno(r, "Failed to resolve %s/%s: %m", new_root, transfer->path);
+
+ /* Let's see if it is a mount point already. */
+ r = path_is_mount_point(chased, NULL, 0);
+ if (r < 0)
+ return log_error_errno(r, "Failed to determine whether %s is a mount point: %m", chased);
+ if (r > 0) /* If it is already mounted, then do nothing */
+ continue;
+
+ r = mount_nofollow_verbose(LOG_ERR, transfer->path, chased, NULL, transfer->mount_flags, NULL);
+ if (r < 0)
+ return r;
+ }
+
if (fchdir(new_root_fd) < 0)
return log_error_errno(errno, "Failed to change directory to %s: %m", new_root);
@@ -144,7 +169,7 @@ int switch_root(const char *new_root,
return log_error_errno(errno, "Failed to change directory: %m");
}
- if (istmp) {
+ if (istmp > 0) {
struct stat rb;
if (fstat(old_root_fd, &rb) < 0)
diff --git a/src/shared/switch-root.h b/src/shared/switch-root.h
index e3fabae3d9..79e47c67f4 100644
--- a/src/shared/switch-root.h
+++ b/src/shared/switch-root.h
@@ -3,4 +3,9 @@
#include
-int switch_root(const char *new_root, const char *old_root_after, unsigned long mount_flags);
+typedef enum SwitchRootFlags {
+ SWITCH_ROOT_DESTROY_OLD_ROOT = 1 << 0, /* rm -rf old root when switching – under the condition that it is backed by non-persistent tmpfs/ramfs/… */
+ SWITCH_ROOT_DONT_SYNC = 1 << 1, /* don't call sync() immediately before switching root */
+} SwitchRootFlags;
+
+int switch_root(const char *new_root, const char *old_root_after, SwitchRootFlags flags);
diff --git a/src/shutdown/shutdown.c b/src/shutdown/shutdown.c
index 08cc114d3a..8395bb429d 100644
--- a/src/shutdown/shutdown.c
+++ b/src/shutdown/shutdown.c
@@ -163,17 +163,15 @@ static int parse_argv(int argc, char *argv[]) {
}
static int switch_root_initramfs(void) {
- if (mount("/run/initramfs", "/run/initramfs", NULL, MS_BIND, NULL) < 0)
- return log_error_errno(errno, "Failed to mount bind /run/initramfs on /run/initramfs: %m");
-
- if (mount(NULL, "/run/initramfs", NULL, MS_PRIVATE, NULL) < 0)
- return log_error_errno(errno, "Failed to make /run/initramfs private mount: %m");
-
- /* switch_root with MS_BIND, because there might still be processes lurking around, which have open file descriptors.
- * /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);
+ /* Do not detach the old root, because /run/initramfs/shutdown needs to access it.
+ *
+ * Disable sync() during switch-root, we after all sync'ed here plenty, and a dumb sync (as opposed
+ * to the "smart" sync() we did here that looks at progress parameters) would defeat much of our
+ * efforts here. */
+ return switch_root(
+ /* new_root= */ "/run/initramfs",
+ /* old_root_after= */ "/oldroot",
+ /* flags= */ SWITCH_ROOT_DONT_SYNC);
}
/* Read the following fields from /proc/meminfo:
diff --git a/src/systemctl/systemctl-logind.c b/src/systemctl/systemctl-logind.c
index fd8ca09de8..a4486dcce9 100644
--- a/src/systemctl/systemctl-logind.c
+++ b/src/systemctl/systemctl-logind.c
@@ -45,6 +45,7 @@ int logind_reboot(enum action a) {
[ACTION_POWEROFF] = "PowerOff",
[ACTION_REBOOT] = "Reboot",
[ACTION_KEXEC] = "Reboot",
+ [ACTION_SOFT_REBOOT] = "Reboot",
[ACTION_HALT] = "Halt",
[ACTION_SUSPEND] = "Suspend",
[ACTION_HIBERNATE] = "Hibernate",
@@ -80,6 +81,7 @@ int logind_reboot(enum action a) {
SET_FLAG(flags, SD_LOGIND_ROOT_CHECK_INHIBITORS, arg_check_inhibitors > 0);
SET_FLAG(flags, SD_LOGIND_REBOOT_VIA_KEXEC, a == ACTION_KEXEC);
+ SET_FLAG(flags, SD_LOGIND_SOFT_REBOOT, a == ACTION_SOFT_REBOOT);
r = bus_call_method(bus, bus_login_mgr, method_with_flags, &error, NULL, "t", flags);
if (r >= 0)
diff --git a/src/systemctl/systemctl-start-special.c b/src/systemctl/systemctl-start-special.c
index c6084f373e..6e2238f2a5 100644
--- a/src/systemctl/systemctl-start-special.c
+++ b/src/systemctl/systemctl-start-special.c
@@ -199,6 +199,7 @@ int verb_start_special(int argc, char *argv[], void *userdata) {
case ACTION_REBOOT:
case ACTION_KEXEC:
case ACTION_HALT:
+ case ACTION_SOFT_REBOOT:
if (arg_when == 0)
r = logind_reboot(a);
else if (arg_when != USEC_INFINITY)
diff --git a/src/systemctl/systemctl-start-unit.c b/src/systemctl/systemctl-start-unit.c
index 3dac7da460..8268217967 100644
--- a/src/systemctl/systemctl-start-unit.c
+++ b/src/systemctl/systemctl-start-unit.c
@@ -222,6 +222,7 @@ const struct action_metadata action_table[_ACTION_MAX] = {
[ACTION_POWEROFF] = { SPECIAL_POWEROFF_TARGET, "poweroff", "replace-irreversibly" },
[ACTION_REBOOT] = { SPECIAL_REBOOT_TARGET, "reboot", "replace-irreversibly" },
[ACTION_KEXEC] = { SPECIAL_KEXEC_TARGET, "kexec", "replace-irreversibly" },
+ [ACTION_SOFT_REBOOT] = { SPECIAL_SOFT_REBOOT_TARGET, "soft-reboot", "replace-irreversibly" },
[ACTION_RUNLEVEL2] = { SPECIAL_MULTI_USER_TARGET, NULL, "isolate" },
[ACTION_RUNLEVEL3] = { SPECIAL_MULTI_USER_TARGET, NULL, "isolate" },
[ACTION_RUNLEVEL4] = { SPECIAL_MULTI_USER_TARGET, NULL, "isolate" },
diff --git a/src/systemctl/systemctl-trivial-method.c b/src/systemctl/systemctl-trivial-method.c
index 5e530f3a14..02a29123e7 100644
--- a/src/systemctl/systemctl-trivial-method.c
+++ b/src/systemctl/systemctl-trivial-method.c
@@ -30,6 +30,7 @@ int verb_trivial_method(int argc, char *argv[], void *userdata) {
streq(argv[0], "halt") ? "Halt" :
streq(argv[0], "reboot") ? "Reboot" :
streq(argv[0], "kexec") ? "KExec" :
+ streq(argv[0], "soft-reboot") ? "SoftReboot" :
streq(argv[0], "exit") ? "Exit" :
/* poweroff */ "PowerOff";
diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c
index b31a59785b..143c8b41b2 100644
--- a/src/systemctl/systemctl.c
+++ b/src/systemctl/systemctl.c
@@ -242,6 +242,7 @@ static int systemctl_help(void) {
" poweroff Shut down and power-off the system\n"
" reboot Shut down and reboot the system\n"
" kexec Shut down and reboot the system with kexec\n"
+ " soft-reboot Shut down and reboot userspace\n"
" exit [EXIT_CODE] Request user instance or container exit\n"
" switch-root [ROOT [INIT]] Change to a different root file system\n"
" suspend Suspend the system\n"
@@ -285,8 +286,9 @@ static int systemctl_help(void) {
" --now Start or stop unit after enabling or disabling it\n"
" --dry-run Only print what would be done\n"
" Currently supported by verbs: halt, poweroff, reboot,\n"
- " kexec, suspend, hibernate, suspend-then-hibernate,\n"
- " hybrid-sleep, default, rescue, emergency, and exit.\n"
+ " kexec, soft-reboot, suspend, hibernate, \n"
+ " suspend-then-hibernate, hybrid-sleep, default,\n"
+ " rescue, emergency, and exit.\n"
" -q --quiet Suppress output\n"
" --no-warn Suppress several warnings shown by default\n"
" --wait For (re)start, wait until service stopped again\n"
@@ -1180,6 +1182,7 @@ static int systemctl_main(int argc, char *argv[]) {
{ "poweroff", VERB_ANY, 1, VERB_ONLINE_ONLY, verb_start_system_special },
{ "reboot", VERB_ANY, 1, VERB_ONLINE_ONLY, verb_start_system_special },
{ "kexec", VERB_ANY, 1, VERB_ONLINE_ONLY, verb_start_system_special },
+ { "soft-reboot", VERB_ANY, 1, VERB_ONLINE_ONLY, verb_start_system_special },
{ "suspend", VERB_ANY, 1, VERB_ONLINE_ONLY, verb_start_system_special },
{ "hibernate", VERB_ANY, 1, VERB_ONLINE_ONLY, verb_start_system_special },
{ "hybrid-sleep", VERB_ANY, 1, VERB_ONLINE_ONLY, verb_start_system_special },
diff --git a/src/systemctl/systemctl.h b/src/systemctl/systemctl.h
index 03645624ee..e8ba8f76a0 100644
--- a/src/systemctl/systemctl.h
+++ b/src/systemctl/systemctl.h
@@ -16,6 +16,7 @@ enum action {
ACTION_POWEROFF,
ACTION_REBOOT,
ACTION_KEXEC,
+ ACTION_SOFT_REBOOT,
ACTION_EXIT,
ACTION_SUSPEND,
ACTION_HIBERNATE,
diff --git a/src/test/test-mount-util.c b/src/test/test-mount-util.c
index 2529b2f0ee..021eff1548 100644
--- a/src/test/test-mount-util.c
+++ b/src/test/test-mount-util.c
@@ -519,6 +519,55 @@ TEST(umount_recursive) {
}
}
+TEST(fd_make_mount_point) {
+ _cleanup_(rm_rf_physical_and_freep) char *t = NULL;
+ _cleanup_free_ char *s = NULL;
+ int r;
+
+ if (geteuid() != 0 || have_effective_cap(CAP_SYS_ADMIN) <= 0) {
+ (void) log_tests_skipped("not running privileged");
+ return;
+ }
+
+ assert_se(mkdtemp_malloc(NULL, &t) >= 0);
+
+ assert_se(asprintf(&s, "%s/somerandomname%" PRIu64, t, random_u64()) >= 0);
+ assert_se(s);
+ assert_se(mkdir(s, 0700) >= 0);
+
+ r = safe_fork("(make_mount-point)",
+ FORK_RESET_SIGNALS |
+ FORK_CLOSE_ALL_FDS |
+ FORK_DEATHSIG |
+ FORK_WAIT |
+ FORK_REOPEN_LOG |
+ FORK_LOG |
+ FORK_NEW_MOUNTNS |
+ FORK_MOUNTNS_SLAVE,
+ NULL);
+ assert_se(r >= 0);
+
+ if (r == 0) {
+ _cleanup_close_ int fd = -EBADF, fd2 = -EBADF;
+
+ fd = open(s, O_PATH|O_CLOEXEC);
+ assert_se(fd >= 0);
+
+ assert_se(fd_is_mount_point(fd, NULL, AT_SYMLINK_FOLLOW) == 0);
+
+ assert_se(fd_make_mount_point(fd) > 0);
+
+ /* Reopen the inode so that we end up on the new mount */
+ fd2 = open(s, O_PATH|O_CLOEXEC);
+
+ assert_se(fd_is_mount_point(fd2, NULL, AT_SYMLINK_FOLLOW) > 0);
+
+ assert_se(fd_make_mount_point(fd2) == 0);
+
+ _exit(EXIT_SUCCESS);
+ }
+}
+
static int intro(void) {
/* Create a dummy network interface for testing remount_sysfs(). */
(void) system("ip link add dummy-test-mnt type dummy");
diff --git a/test/TEST-82-SOFTREBOOT/Makefile b/test/TEST-82-SOFTREBOOT/Makefile
new file mode 120000
index 0000000000..e9f93b1104
--- /dev/null
+++ b/test/TEST-82-SOFTREBOOT/Makefile
@@ -0,0 +1 @@
+../TEST-01-BASIC/Makefile
\ No newline at end of file
diff --git a/test/TEST-82-SOFTREBOOT/test.sh b/test/TEST-82-SOFTREBOOT/test.sh
new file mode 100755
index 0000000000..9d88c94fb7
--- /dev/null
+++ b/test/TEST-82-SOFTREBOOT/test.sh
@@ -0,0 +1,21 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -e
+
+TEST_DESCRIPTION="Test Soft-Rebooting"
+
+# shellcheck source=test/test-functions
+. "$TEST_BASE_DIR/test-functions"
+
+test_append_files() {
+ local workspace="${1:?}"
+ # prevent shutdown in test suite, the expect script does that manually.
+ mkdir -p "${workspace:?}/etc/systemd/system/end.service.d"
+ cat >"$workspace/etc/systemd/system/end.service.d/99-override.conf" <"$T"
+ systemd-notify --fd=3 --pid=parent 3<"$T"
+ rm "$T"
+
+ # Check that the surviving service is still around
+ test "$(systemctl show -P ActiveState testsuite-82-survive.service)" = "active"
+ test "$(systemctl show -P ActiveState testsuite-82-nosurvive.service)" != "active"
+
+ # Test that we really are in the new overlayfs root fs
+ read -r x "$T"
+ systemd-notify --fd=3 --pid=parent 3<"$T"
+ rm "$T"
+
+ # Check that the surviving service is still around
+ test "$(systemctl show -P ActiveState testsuite-82-survive.service)" = "active"
+ test "$(systemctl show -P ActiveState testsuite-82-nosurvive.service)" != "active"
+
+ # This time we test the /run/nextroot/ root switching logic. (We synthesize a new rootfs from the old via overlayfs)
+ mkdir -p /run/nextroot /tmp/nextroot-lower /original-root
+ mount -t tmpfs tmpfs /tmp/nextroot-lower
+ echo miep >/tmp/nextroot-lower/lower
+ mount -t overlay nextroot /run/nextroot -o lowerdir=/:/tmp/nextroot-lower,ro
+
+ # Bind our current root into the target so that we later can return to it
+ mount --bind / /run/nextroot/original-root
+
+ # Now issue the soft reboot. We should be right back soon.
+ touch /run/testsuite82.touch2
+ systemctl --no-block soft-reboot
+
+ # Now block until the soft-boot killing spree kills us
+ exec sleep infinity
+else
+ # This is the first boot
+ systemd-notify --status="First Boot"
+
+ # Let's upload an fd to the fdstore, so that we can verify fdstore passing works correcly
+ T="/dev/shm/fdstore.$RANDOM"
+ echo "wuffwuff" >"$T"
+ systemd-notify --fd=3 --pid=parent 3<"$T"
+ rm "$T"
+
+ # Create a script that can survive the soft reboot by ignoring SIGTERM (we
+ # do this instead of the argv[0][0] = '@' thing because that's so hard to
+ # do from a shell
+ T="/dev/shm/survive-$RANDOM.sh"
+ cat >$T </testok
+
+systemctl --no-block poweroff
+
+exit 0
diff --git a/units/meson.build b/units/meson.build
index fa76946ddb..e4f064c1ab 100644
--- a/units/meson.build
+++ b/units/meson.build
@@ -67,6 +67,7 @@ units = [
['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',
@@ -137,6 +138,7 @@ units = [
['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'],
diff --git a/units/soft-reboot.target b/units/soft-reboot.target
new file mode 100644
index 0000000000..6a6c772875
--- /dev/null
+++ b/units/soft-reboot.target
@@ -0,0 +1,18 @@
+# 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
diff --git a/units/systemd-soft-reboot.service b/units/systemd-soft-reboot.service
new file mode 100644
index 0000000000..35ba3a9fc4
--- /dev/null
+++ b/units/systemd-soft-reboot.service
@@ -0,0 +1,16 @@
+# 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