1
0
mirror of https://github.com/systemd/systemd.git synced 2025-03-09 12:58:26 +03:00

Merge pull request #27435 from poettering/renew-reboot

pid1: add a new method of rebooting: userspace only under the name "soft-reboot"
This commit is contained in:
Lennart Poettering 2023-06-02 23:27:45 +02:00 committed by GitHub
commit 4d824ac0d3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
44 changed files with 827 additions and 133 deletions

9
TODO
View File

@ -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.

View File

@ -576,16 +576,15 @@ node /org/freedesktop/login1 {
<para><function>PowerOff()</function>, <function>Reboot()</function>, <function>Halt()</function>,
<function>Suspend()</function>, and <function>Hibernate()</function> 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). <function>HybridSleep()</function> results in the system entering a
hybrid-sleep mode, i.e. the system is both hibernated and suspended.
<function>SuspendThenHibernate()</function> results in the system being suspended, then later woken
using an RTC timer and hibernated. The only argument is the polkit interactivity boolean
<varname>interactive</varname> (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). <function>HybridSleep()</function> results in the system entering a hybrid-sleep mode,
i.e. the system is both hibernated and suspended. <function>SuspendThenHibernate()</function> results
in the system being suspended, then later woken using an RTC timer and hibernated. The only argument is
the polkit interactivity boolean <varname>interactive</varname> (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
<function>PowerOffWithFlags()</function>, <function>RebootWithFlags()</function>,
<function>HaltWithFlags()</function>, <function>SuspendWithFlags()</function>,
<function>HibernateWithFlags()</function>, <function>HybridSleepWithFlags()</function> and
@ -594,12 +593,14 @@ node /org/freedesktop/login1 {
<programlisting>
#define SD_LOGIND_ROOT_CHECK_INHIBITORS (UINT64_C(1) &lt;&lt; 0)
#define SD_LOGIND_KEXEC_REBOOT (UINT64_C(1) &lt;&lt; 1)
#define SD_LOGIND_SOFT_REBOOT (UINT64_C(1) &lt;&lt; 2)
</programlisting>
<para> When the <varname>flags</varname> is 0 then these methods behave just like the versions
without flags. When <constant>SD_LOGIND_ROOT_CHECK_INHIBITORS</constant> (0x01) is set, active
inhibitors are honoured for privileged users too. When <constant>SD_LOGIND_KEXEC_REBOOT</constant>
(0x02) is set, then <function>RebootWithFlags()</function> perform kexec reboot if kexec
kernel is loaded.</para>
<para>When the <varname>flags</varname> is 0 then these methods behave just like the versions without
flags. When <constant>SD_LOGIND_ROOT_CHECK_INHIBITORS</constant> (0x01) is set, active inhibitors are
honoured for privileged users too. When <constant>SD_LOGIND_KEXEC_REBOOT</constant> (0x02) is set, then
<function>RebootWithFlags()</function> performs a kexec reboot if kexec kernel is loaded. When
<constant>SD_LOGIND_SOFT_REBOOT</constant> (0x04) is set, then <function>RebootWithFlags()</function>
performs a userspace reboot only.</para>
<para><function>SetRebootParameter()</function> sets a parameter for a subsequent reboot operation.
See the description of <command>reboot</command> in

View File

@ -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 {
<variablelist class="dbus-method" generated="True" extra-ref="Reboot()"/>
<variablelist class="dbus-method" generated="True" extra-ref="SoftReboot()"/>
<variablelist class="dbus-method" generated="True" extra-ref="PowerOff()"/>
<variablelist class="dbus-method" generated="True" extra-ref="Halt()"/>
@ -1415,15 +1419,18 @@ node /org/freedesktop/systemd1 {
<para><function>Exit()</function> 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.</para>
<para><function>Reboot()</function>, <function>PowerOff()</function>, <function>Halt()</function>, or
<function>KExec()</function> 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
<function>Reboot()</function> or <function>PowerOff()</function> on the
<para><function>Reboot()</function>, <function>PowerOff()</function>, <function>Halt()</function>,
<function>KExec()</function> and <function>SoftReboot()</function> 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 <function>Reboot()</function>,
<function>RebootWithFlags()</function> or <function>PowerOff()</function> on the
<filename>systemd-logind</filename> manager object; see
<citerefentry><refentrytitle>org.freedesktop.login1</refentrytitle><manvolnum>5</manvolnum></citerefentry>
for more information.</para>
for more information. <function>SoftReboot()</function> 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
<filename>/run/nextroot/</filename> path is used if it exists.</para>
<para><function>SwitchRoot()</function> 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.

View File

@ -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',

View File

@ -1559,6 +1559,24 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
</listitem>
</varlistentry>
<varlistentry>
<term><command>soft-reboot</command></term>
<listitem>
<para>Shut down and reboot userspace. This is equivalent to <command>systemctl start
soft-reboot.target --job-mode=replace-irreversibly --no-block</command>. This command is
asynchronous; it will return after the reboot operation is enqueued, without waiting for it to
complete.</para>
<para>This command honors <option>--force</option> and <option>--when=</option> in a similar way
as <command>halt</command>.</para>
<para>This operation only reboots userspace, leaving the kernel running. See
<citerefentry><refentrytitle>systemd-soft-reboot.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
for details.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>exit</command> <optional><replaceable>EXIT_CODE</replaceable></optional></term>

View File

@ -68,6 +68,10 @@
<para>Note that <filename>systemd-poweroff.service</filename> (and the related units) should never be
executed directly. Instead, trigger system shutdown with a command such as <literal>systemctl
poweroff</literal>.</para>
<para>Another form of shutdown is provided by the
<citerefentry><refentrytitle>systemd-soft-reboot.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
functionality. It reboots only the OS userspace, leaving the kernel, firmware, and hardware as it is.</para>
</refsect1>
<refsect1>
@ -78,6 +82,7 @@
<citerefentry><refentrytitle>systemd.special</refentrytitle><manvolnum>7</manvolnum></citerefentry>,
<citerefentry><refentrytitle>reboot</refentrytitle><manvolnum>2</manvolnum></citerefentry>,
<citerefentry><refentrytitle>systemd-suspend.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
<citerefentry><refentrytitle>systemd-soft-reboot.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
<citerefentry><refentrytitle>bootup</refentrytitle><manvolnum>7</manvolnum></citerefentry>
</para>
</refsect1>

View File

@ -0,0 +1,158 @@
<?xml version='1.0'?> <!--*-nxml-*-->
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
<!-- SPDX-License-Identifier: LGPL-2.1-or-later -->
<refentry id="systemd-soft-reboot.service">
<refentryinfo>
<title>systemd-soft-reboot.service</title>
<productname>systemd</productname>
</refentryinfo>
<refmeta>
<refentrytitle>systemd-soft-reboot.service</refentrytitle>
<manvolnum>8</manvolnum>
</refmeta>
<refnamediv>
<refname>systemd-soft-reboot.service</refname>
<refpurpose>Userspace reboot operation</refpurpose>
</refnamediv>
<refsynopsisdiv>
<para><filename>systemd-soft-reboot.service</filename></para>
</refsynopsisdiv>
<refsect1>
<title>Description</title>
<para><filename>systemd-soft-reboot.service</filename> is a system service that is pulled in by
<filename>soft-reboot.target</filename> and is responsible for performing a userspace-only reboot
operation. When invoked, it will send the <constant>SIGTERM</constant> signal to any processes left
running (but does not follow up with <constant>SIGKILL</constant>, and does not wait for the processes to
exit). If the <filename>/run/nextroot/</filename> 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.</para>
<para>Such a userspace-only reboot operation permits updating or resetting the entirety of userspace with
minimal downtime, as the reboot operation does <emphasis>not</emphasis> transition through:</para>
<itemizedlist>
<listitem><para>The second phase of regular shutdown, as implemented by
<citerefentry><refentrytitle>systemd-shutdown</refentrytitle><manvolnum>8</manvolnum></citerefentry>.</para></listitem>
<listitem><para>The third phase of regular shutdown, i.e. the return to the initrd
context</para></listitem>
<listitem><para>The hardware reboot operation</para></listitem>
<listitem><para>The firmware initialization</para></listitem>
<listitem><para>The boot loader initialization</para></listitem>
<listitem><para>The kernel initialization</para></listitem>
<listitem><para>The initrd initialization</para></listitem>
</itemizedlist>
<para>However this form of reboot comes with drawbacks as well:</para>
<itemizedlist>
<listitem><para>The OS update remains incomplete, as the kernel is not reset and continues
running.</para></listitem>
<listitem><para>Kernel settings (such as <filename>/proc/sys/</filename> settings, a.k.a. "sysctl", or
<filename>/sys/</filename> settings) are not reset.</para></listitem>
</itemizedlist>
<para>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
<filename>/etc/sysctl.d/</filename> files.</para>
</refsect1>
<refsect1>
<title>Resource Pass-Through</title>
<para>Various runtime OS resources can passed from a system runtime to the next, through the userspace
reboot operation. Specificially:</para>
<itemizedlist>
<listitem><para>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 <varname>DefaultDependencies=no</varname> (and
avoid a manual <varname>Conflicts=shutdown.target</varname> or similar) to ensure they are not
terminated as usual during the system shutdown operation. Alternatively, use
<varname>FileDescriptorStorePreserve=</varname> to allow the file descriptor store to remain pinned
even when the unit is down. See
<citerefentry><refentrytitle>systemd.service</refentrytitle><manvolnum>5</manvolnum></citerefentry> for
details about the file descriptor store.</para></listitem>
<listitem><para>Similar to this, file descriptors associated with <filename>.socket</filename> units
remain open (and connectible) if the units are not stopped during the transition. (Achieved by
<varname>DefaultDependencies=no</varname>.)</para></listitem>
<listitem><para>The <filename>/run/</filename> file system remains mounted and populated and may be
used to pass state information between such userspace reboot cycles.</para></listitem>
<listitem><para>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
<varname>DefaultDependencies=no</varname>). They must also be set up to avoid being killed by the
aforementioned <constant>SIGTERM</constant> spree (as per <ulink
url="https://systemd.io/ROOT_STORAGE_DAEMONS">systemd and Storage Daemons for the Root File
System</ulink>).</para></listitem>
<listitem><para>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
<varname>DefaultDependencies=no</varname>, and by avoiding
<varname>Conflicts=umount.target</varname>)</para></listitem>
</itemizedlist>
<para>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 <emphasis>avoid</emphasis> 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 <ulink
url="https://systemd.io/PORTABLE_SERVICES">Portable Services</ulink>.</para>
<para>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
<varname>DefaultDependencies=no</varname> and then place
<varname>Conflicts=</varname>/<varname>Before=</varname> onto <filename>reboot.target</filename>,
<filename>kexec.target</filename>, <filename>poweroff.target</filename> and
<filename>halt.target</filename> (but <emphasis>not</emphasis> onto
<filename>soft-reboot.target</filename>).</para>
</refsect1>
<refsect1>
<title>Notes</title>
<para>Note that because
<citerefentry><refentrytitle>systemd-shutdown</refentrytitle><manvolnum>8</manvolnum></citerefentry> is
not executed, the executables in <filename>/usr/lib/systemd/system-shutdown/</filename> are not executed
either.</para>
<para>Note that <filename>systemd-soft-reboot.service</filename> (and related units) should never be
executed directly. Instead, trigger system shutdown with a command such as <literal>systemctl
soft-reboot</literal>.</para>
</refsect1>
<refsect1>
<title>See Also</title>
<para>
<citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
<citerefentry><refentrytitle>systemctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
<citerefentry><refentrytitle>systemd.special</refentrytitle><manvolnum>7</manvolnum></citerefentry>,
<citerefentry><refentrytitle>systemd-poweroff.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
<citerefentry><refentrytitle>systemd-suspend.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
<citerefentry><refentrytitle>bootup</refentrytitle><manvolnum>7</manvolnum></citerefentry>
</para>
</refsect1>
</refentry>

View File

@ -437,8 +437,12 @@
<listitem><para>An additional filesystem to be mounted in the initrd. See
<filename>initrd-fs.target</filename> description in
<citerefentry><refentrytitle>systemd.special</refentrytitle><manvolnum>7</manvolnum></citerefentry>.
</para></listitem>
<citerefentry><refentrytitle>systemd.special</refentrytitle><manvolnum>7</manvolnum></citerefentry>. 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.</para></listitem>
</varlistentry>
</variablelist>

View File

@ -81,6 +81,7 @@
<filename>slices.target</filename>,
<filename>smartcard.target</filename>,
<filename>sockets.target</filename>,
<filename>soft-reboot.target</filename>,
<filename>sound.target</filename>,
<filename>suspend.target</filename>,
<filename>swap.target</filename>,
@ -442,15 +443,18 @@
<varlistentry>
<term><filename>kexec.target</filename></term>
<listitem>
<para>A special target unit for shutting down and rebooting
the system via kexec.</para>
<para>A special target unit for shutting down and rebooting the system via kexec.</para>
<para>Applications wanting to reboot the system should not start this unit
directly, but should instead execute <command>systemctl kexec</command>
(possibly with the <option>--no-block</option> option) or call
<citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>'s
<command>org.freedesktop.systemd1.Manager.KExec</command> D-Bus method
<para>Applications wanting to reboot the system should not start this unit directly, but should
instead execute <command>systemctl kexec</command> (possibly with the
<option>--no-block</option> option) or call
<citerefentry><refentrytitle>systemd-logind</refentrytitle><manvolnum>8</manvolnum></citerefentry>'s
<function>org.freedesktop.login1.Manager.RebootWithFlags()</function> D-Bus method
directly.</para>
<para>See
<citerefentry><refentrytitle>systemd-kexec.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
for further details of the operation this target pulls in.</para>
</listitem>
</varlistentry>
<varlistentry>
@ -559,18 +563,20 @@
<varlistentry>
<term><filename>reboot.target</filename></term>
<listitem>
<para>A special target unit for shutting down and rebooting
the system.</para>
<para>A special target unit for shutting down and rebooting the system.</para>
<para>Applications wanting to reboot the system should not start this unit
directly, but should instead execute <command>systemctl reboot</command>
(possibly with the <option>--no-block</option> option) or call
<para>Applications wanting to reboot the system should not start this unit directly, but should
instead execute <command>systemctl reboot</command> (possibly with the
<option>--no-block</option> option) or call
<citerefentry><refentrytitle>systemd-logind</refentrytitle><manvolnum>8</manvolnum></citerefentry>'s
<command>org.freedesktop.login1.Manager.Reboot</command> D-Bus method
directly.</para>
<function>org.freedesktop.login1.Manager.Reboot()</function> D-Bus method directly.</para>
<para><filename>runlevel6.target</filename> is an alias for
this target unit, for compatibility with SysV.</para>
<para>See
<citerefentry><refentrytitle>systemd-reboot.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
for further details of the operation this target pulls in.</para>
<para><filename>runlevel6.target</filename> is an alias for this target unit, for compatibility
with SysV.</para>
</listitem>
</varlistentry>
<varlistentry>
@ -703,6 +709,24 @@
section.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><filename>soft-reboot.target</filename></term>
<listitem>
<para>A special target unit for shutting down and rebooting the userspace of the system (leaving
the kernel running).</para>
<para>Applications wanting to reboot the system should not start this unit directly, but should
instead execute <command>systemctl soft-reboot</command> (possibly with the
<option>--no-block</option> option) or call
<citerefentry><refentrytitle>systemd-logind</refentrytitle><manvolnum>8</manvolnum></citerefentry>'s
<function>org.freedesktop.login1.Manager.RebootWithFlags()</function> D-Bus method
directly.</para>
<para>See
<citerefentry><refentrytitle>systemd-soft-reboot.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
for further details of the operation this target pulls in.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><filename>suspend.target</filename></term>
<listitem>

View File

@ -999,27 +999,34 @@
<term><varname>FailureAction=</varname></term>
<term><varname>SuccessAction=</varname></term>
<listitem><para>Configure the action to take when the unit stops and enters a failed state or inactive state.
Takes one of <option>none</option>, <option>reboot</option>, <option>reboot-force</option>,
<option>reboot-immediate</option>, <option>poweroff</option>, <option>poweroff-force</option>,
<option>poweroff-immediate</option>, <option>exit</option>, and <option>exit-force</option>. In system mode,
all options are allowed. In user mode, only <option>none</option>, <option>exit</option>, and
<option>exit-force</option> are allowed. Both options default to <option>none</option>.</para>
<listitem><para>Configure the action to take when the unit stops and enters a failed state or
inactive state. Takes one of <option>none</option>, <option>reboot</option>,
<option>reboot-force</option>, <option>reboot-immediate</option>, <option>poweroff</option>,
<option>poweroff-force</option>, <option>poweroff-immediate</option>, <option>exit</option>,
<option>exit-force</option>, <option>soft-reboot</option> and <option>soft-reboot-force</option>. In
system mode, all options are allowed. In user mode, only <option>none</option>,
<option>exit</option>, <option>exit-force</option>, <option>soft-reboot</option> and
<option>soft-reboot-force</option> are allowed. Both options default to <option>none</option>.</para>
<para>If <option>none</option> is set, no action will be triggered. <option>reboot</option> causes a reboot
following the normal shutdown procedure (i.e. equivalent to <command>systemctl reboot</command>).
<option>reboot-force</option> causes a forced reboot which will terminate all processes forcibly but should
cause no dirty file systems on reboot (i.e. equivalent to <command>systemctl reboot -f</command>) and
<option>reboot-immediate</option> causes immediate execution of the
<citerefentry><refentrytitle>reboot</refentrytitle><manvolnum>2</manvolnum></citerefentry> system call, which
might result in data loss (i.e. equivalent to <command>systemctl reboot -ff</command>). Similarly,
<option>poweroff</option>, <option>poweroff-force</option>, <option>poweroff-immediate</option> have the effect
of powering down the system with similar semantics. <option>exit</option> causes the manager to exit following
the normal shutdown procedure, and <option>exit-force</option> causes it terminate without shutting down
services. When <option>exit</option> or <option>exit-force</option> 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 <varname>FailureActionExitStatus=</varname>/<varname>SuccessActionExitStatus=</varname>, see
below.</para></listitem>
<para>If <option>none</option> is set, no action will be triggered. <option>reboot</option> causes a
reboot following the normal shutdown procedure (i.e. equivalent to <command>systemctl
reboot</command>). <option>reboot-force</option> causes a forced reboot which will terminate all
processes forcibly but should cause no dirty file systems on reboot (i.e. equivalent to
<command>systemctl reboot -f</command>) and <option>reboot-immediate</option> causes immediate
execution of the
<citerefentry><refentrytitle>reboot</refentrytitle><manvolnum>2</manvolnum></citerefentry> system
call, which might result in data loss (i.e. equivalent to <command>systemctl reboot
-ff</command>). Similarly, <option>poweroff</option>, <option>poweroff-force</option>,
<option>poweroff-immediate</option> have the effect of powering down the system with similar
semantics. <option>exit</option> causes the manager to exit following the normal shutdown procedure,
and <option>exit-force</option> causes it terminate without shutting down services. When
<option>exit</option> or <option>exit-force</option> 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
<varname>FailureActionExitStatus=</varname>/<varname>SuccessActionExitStatus=</varname>, see
below. <option>soft-reboot</option> will trigger a userspace reboot
operation. <option>soft-reboot-force</option> does that too, but does not go through the shutdown
transaction beforehand.</para></listitem>
</varlistentry>
<varlistentry>

View File

@ -519,6 +519,15 @@
</listitem>
</varlistentry>
<varlistentry>
<term><constant>SIGRTMIN+7</constant></term>
<listitem><para>Reboots userspace, starts the <filename>soft-reboot.target</filename> unit. This is
mostly equivalent to <command>systemctl start soft-reboot.target
--job-mode=replace-irreversibly</command>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><constant>SIGRTMIN+13</constant></term>
@ -543,6 +552,12 @@
<listitem><para>Immediately reboots the machine with kexec.</para></listitem>
</varlistentry>
<varlistentry>
<term><constant>SIGRTMIN+17</constant></term>
<listitem><para>Immediately reboots the userspace.</para></listitem>
</varlistentry>
<varlistentry>
<term><constant>SIGRTMIN+20</constant></term>

View File

@ -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);

View File

@ -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"

View File

@ -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,

View File

@ -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)

View File

@ -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;

View File

@ -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,

View File

@ -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);
@ -2986,6 +2985,7 @@ static int manager_dispatch_signal_fd(sd_event_source *source, int fd, uint32_t
[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 &&

View File

@ -44,6 +44,7 @@ typedef enum ManagerObjective {
MANAGER_RELOAD,
MANAGER_REEXECUTE,
MANAGER_REBOOT,
MANAGER_SOFT_REBOOT,
MANAGER_POWEROFF,
MANAGER_HALT,
MANAGER_KEXEC,

View File

@ -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",

View File

@ -9,6 +9,7 @@ typedef enum HandleAction {
HANDLE_REBOOT,
HANDLE_HALT,
HANDLE_KEXEC,
HANDLE_SOFT_REBOOT,
HANDLE_SUSPEND,
HANDLE_HIBERNATE,
HANDLE_HYBRID_SLEEP,

View File

@ -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);

View File

@ -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 … */

View File

@ -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_;

View File

@ -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;

View File

@ -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,

View File

@ -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;
}
/* 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,6 +94,7 @@ 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. */
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
@ -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") {
/* 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;
r = chase(path, new_root, CHASE_PREFIX_ROOT|CHASE_NONEXISTENT, &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, path);
if (r > 0) {
/* Already exists. Let's see if it is a mount point already. */
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;
} 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);
r = mount_nofollow_verbose(LOG_ERR, transfer->path, chased, NULL, transfer->mount_flags, NULL);
if (r < 0)
return r;
}
/* 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);
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)

View File

@ -3,4 +3,9 @@
#include <stdbool.h>
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);

View File

@ -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:

View File

@ -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)

View File

@ -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)

View File

@ -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" },

View File

@ -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";

View File

@ -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 },

View File

@ -16,6 +16,7 @@ enum action {
ACTION_POWEROFF,
ACTION_REBOOT,
ACTION_KEXEC,
ACTION_SOFT_REBOOT,
ACTION_EXIT,
ACTION_SUSPEND,
ACTION_HIBERNATE,

View File

@ -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");

View File

@ -0,0 +1 @@
../TEST-01-BASIC/Makefile

21
test/TEST-82-SOFTREBOOT/test.sh Executable file
View File

@ -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" <<EOF
[Service]
ExecStart=
ExecStart=/bin/true
EOF
}
do_test "$@"

View File

@ -28,6 +28,7 @@ systemctl log-level info
# FIXME: systemd-run doesn't play well with daemon-reexec
# See: https://github.com/systemd/systemd/issues/27204
sed -i '/\[org.freedesktop.systemd1\]/aorg.freedesktop.systemd1.Manager:Reexecute FIXME' /etc/dfuzzer.conf
sed -i '/\[org.freedesktop.systemd1\]/aorg.freedesktop.systemd1.Manager:SoftReboot destructive' /etc/dfuzzer.conf
# TODO
# * check for possibly newly introduced buses?

View File

@ -0,0 +1,11 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=TEST-82-SOFTREBOOT
DefaultDependencies=no
After=basic.target
[Service]
Type=oneshot
ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
FileDescriptorStoreMax=3
NotifyAccess=all

147
test/units/testsuite-82.sh Executable file
View File

@ -0,0 +1,147 @@
#!/usr/bin/env bash
# SPDX-License-Identifier: LGPL-2.1-or-later
set -ex
set -o pipefail
systemd-analyze log-level debug
export SYSTEMD_LOG_LEVEL=debug
if [ -f /run/testsuite82.touch3 ]; then
echo "This is the fourth boot!"
systemd-notify --status="Fourth Boot"
rm /run/testsuite82.touch3
mount
rmdir /original-root /run/nextroot
# Check that the fdstore entry still exists
test "$LISTEN_FDS" -eq 3
read -r x <&5
test "$x" = "oinkoink"
# 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"
# Take out the big guns now, and kill the service via SIGKILL (SIGTERM is blocked after all, see below)
systemctl --signal=KILL kill testsuite-82-survive.service
systemctl stop testsuite-82-survive.service
# All succeeded, exit cleanly now
elif [ -f /run/testsuite82.touch2 ]; then
echo "This is the third boot!"
systemd-notify --status="Third Boot"
rm /run/testsuite82.touch2
# Check that the fdstore entry still exists
test "$LISTEN_FDS" -eq 2
read -r x <&4
test "$x" = "miaumiau"
# Upload another entry
T="/dev/shm/fdstore.$RANDOM"
echo "oinkoink" >"$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 </lower
test "$x" = "miep"
# Switch back to the original root, away from the overlayfs
mount --bind /original-root /run/nextroot
mount
# Now issue the soft reboot. We should be right back soon.
touch /run/testsuite82.touch3
systemctl --no-block soft-reboot
# Now block until the soft-boot killing spree kills us
exec sleep infinity
elif [ -f /run/testsuite82.touch ]; then
echo "This is the second boot!"
systemd-notify --status="Second Boot"
# Clean up what we created earlier
rm /run/testsuite82.touch
# Check that the fdstore entry still exists
test "$LISTEN_FDS" -eq 1
read -r x <&3
test "$x" = "wuffwuff"
# Upload another entry
T="/dev/shm/fdstore.$RANDOM"
echo "miaumiau" >"$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 <<EOF
#!/bin/bash
trap "" TERM
systemd-notify --ready
rm "$T"
exec sleep infinity
EOF
chmod +x "$T"
# This sets DefaultDependencies=no so that it remains running until the
# very end, and IgnoreOnIsolate=yes so that it isn't stopped via the
# "testsuite.target" isolation we do on next boot
systemd-run -p Type=notify -p DefaultDependencies=no -p IgnoreOnIsolate=yes --unit=testsuite-82-survive.service "$T"
systemd-run -p Type=exec -p DefaultDependencies=no -p IgnoreOnIsolate=yes --unit=testsuite-82-nosurvive.service sleep infinity
# Now issue the soft reboot. We should be right back soon.
touch /run/testsuite82.touch
systemctl --no-block soft-reboot
# Now block until the soft-boot killing spree kills us
exec sleep infinity
fi
systemd-analyze log-level info
echo OK >/testok
systemctl --no-block poweroff
exit 0

View File

@ -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'],

18
units/soft-reboot.target Normal file
View File

@ -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

View File

@ -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