| <?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.offline-updates"> |
| <refentryinfo> |
| <title>systemd.offline-updates</title> |
| <productname>systemd</productname> |
| </refentryinfo> |
| |
| <refmeta> |
| <refentrytitle>systemd.offline-updates</refentrytitle> |
| <manvolnum>7</manvolnum> |
| </refmeta> |
| |
| <refnamediv> |
| <refname>systemd.offline-updates</refname> |
| <refpurpose>Implementation of offline updates in systemd</refpurpose> |
| </refnamediv> |
| |
| <refsect1> |
| <title>Implementing Offline System Updates</title> |
| |
| <para>This man page describes how to implement "offline" system updates with systemd. By "offline" |
| OS updates we mean package installations and updates that are run with the system booted into a |
| special system update mode, in order to avoid problems related to conflicts of libraries and |
| services that are currently running with those on disk. This document is inspired by this |
| <ulink url="https://wiki.gnome.org/Design/OS/SoftwareUpdates">GNOME design whiteboard</ulink>. |
| </para> |
| |
| <para>The logic:</para> |
| |
| <orderedlist> |
| <listitem> |
| <para>The package manager prepares system updates by downloading all (.rpm or .deb or |
| whatever) packages to update off-line in a special directory |
| <filename index="false">/var/lib/system-update</filename> (or |
| another directory of the package/upgrade manager's choice).</para> |
| </listitem> |
| |
| <listitem> |
| <para>When the user OK'ed the update, the symlink <filename>/system-update</filename> is |
| created that points to <filename index="false">/var/lib/system-update</filename> (or |
| wherever the directory with the upgrade files is located) and the system is rebooted. This |
| symlink is in the root directory, since we need to check for it very early at boot, at a |
| time where <filename>/var/</filename> is not available yet.</para> |
| </listitem> |
| |
| <listitem> |
| <para>Very early in the new boot |
| <citerefentry><refentrytitle>systemd-system-update-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry> |
| checks whether <filename>/system-update</filename> exists. If so, it (temporarily and for |
| this boot only) redirects (i.e. symlinks) <filename>default.target</filename> to |
| <filename>system-update.target</filename>, a special target that pulls in the base system |
| (i.e. <filename>sysinit.target</filename>, so that all file systems are mounted but little |
| else) and the system update units.</para> |
| </listitem> |
| |
| <listitem> |
| <para>The system now continues to boot into <filename>default.target</filename>, and |
| thus into <filename>system-update.target</filename>. This target pulls in all system |
| update units. Only one service should perform an update (see the next point), and all |
| the other ones should exit cleanly with a "success" return code and without doing |
| anything. Update services should be ordered after <filename>sysinit.target</filename> |
| so that the update starts after all file systems have been mounted.</para> |
| </listitem> |
| |
| <listitem> |
| <para>As the first step, an update service should check if the |
| <filename>/system-update</filename> symlink points to the location used by that update |
| service. In case it does not exist or points to a different location, the service must exit |
| without error. It is possible for multiple update services to be installed, and for multiple |
| update services to be launched in parallel, and only the one that corresponds to the tool |
| that <emphasis>created</emphasis> the symlink before reboot should perform any actions. It |
| is unsafe to run multiple updates in parallel.</para> |
| </listitem> |
| |
| <listitem> |
| <para>The update service should now do its job. If applicable and possible, it should |
| create a file system snapshot, then install all packages. After completion (regardless |
| whether the update succeeded or failed) the machine must be rebooted, for example by |
| calling <command>systemctl reboot</command>. In addition, on failure the script should |
| revert to the old file system snapshot (without the symlink).</para> |
| </listitem> |
| |
| <listitem> |
| <para>The update scripts should exit only after the update is finished. It is expected |
| that the service which performs the update will cause the machine to reboot after it |
| is done. If the <filename>system-update.target</filename> is successfully reached, i.e. |
| all update services have run, and the <filename>/system-update</filename> symlink still |
| exists, it will be removed and the machine rebooted as a safety measure.</para> |
| </listitem> |
| |
| <listitem> |
| <para>After a reboot, now that the <filename>/system-update</filename> symlink is gone, |
| the generator won't redirect <filename>default.target</filename> anymore and the system |
| now boots into the default target again.</para> |
| </listitem> |
| </orderedlist> |
| </refsect1> |
| |
| <refsect1> |
| <title>Recommendations</title> |
| |
| <orderedlist> |
| <listitem> |
| <para>To make things a bit more robust we recommend hooking the update script into |
| <filename>system-update.target</filename> via a <filename index="false">.wants/</filename> |
| symlink in the distribution package, rather than depending on <command>systemctl |
| enable</command> in the postinst scriptlets of your package. More specifically, for your |
| update script create a .service file, without [Install] section, and then add a symlink like |
| <filename index="false">/usr/lib/systemd/system-update.target.wants/foobar.service</filename> |
| → <filename index="false">../foobar.service</filename> to your package.</para> |
| </listitem> |
| |
| <listitem> |
| <para>Make sure to remove the <filename>/system-update</filename> symlink as early as |
| possible in the update script to avoid reboot loops in case the update fails.</para> |
| </listitem> |
| |
| <listitem> |
| <para>Use <varname>FailureAction=reboot</varname> in the service file for your update script |
| to ensure that a reboot is automatically triggered if the update fails. |
| <varname>FailureAction=</varname> makes sure that the specified unit is activated if your |
| script exits uncleanly (by non-zero error code, or signal/coredump). If your script succeeds |
| you should trigger the reboot in your own code, for example by invoking logind's |
| <command>Reboot()</command> call or calling <command>systemctl reboot</command>. See |
| <citerefentry><refentrytitle>org.freedesktop.login1</refentrytitle><manvolnum>5</manvolnum></citerefentry> |
| for details about the logind D-Bus API.</para> |
| </listitem> |
| |
| <listitem> |
| <para>The update service should declare <varname>DefaultDependencies=no</varname>, |
| <varname>Requires=sysinit.target</varname>, <varname>After=sysinit.target</varname>, |
| <varname>After=system-update-pre.target</varname>, <varname>Before=system-update.target</varname> |
| and explicitly pull in any other services it requires.</para> |
| </listitem> |
| |
| <listitem> |
| <para>It may be desirable to always run an auxiliary unit when booting |
| into offline-updates mode, which itself does not install updates. To |
| do this create a .service file with |
| <varname>Wants=system-update-pre.target</varname> and |
| <varname>Before=system-update-pre.target</varname> and add a symlink |
| to that file under |
| <filename index="false">/usr/lib/systemd/system-update.target.wants</filename> |
| .</para> |
| </listitem> |
| </orderedlist> |
| </refsect1> |
| |
| <refsect1> |
| <title>See also</title> |
| |
| <para> |
| <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>, |
| <citerefentry><refentrytitle>systemd.generator</refentrytitle><manvolnum>7</manvolnum></citerefentry>, |
| <citerefentry><refentrytitle>systemd-system-update-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>, |
| <citerefentry project='mankier'><refentrytitle>dnf.plugin.system-upgrade</refentrytitle><manvolnum>8</manvolnum></citerefentry> |
| </para> |
| </refsect1> |
| </refentry> |