]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
Merge pull request #32389 from keszybz/bin-sbin-merge
authorLuca Boccassi <bluca@debian.org>
Mon, 22 Apr 2024 11:07:48 +0000 (13:07 +0200)
committerGitHub <noreply@github.com>
Mon, 22 Apr 2024 11:07:48 +0000 (13:07 +0200)
Try PATH without sbin even if compiled with split-bin=true

185 files changed:
.github/advanced-issue-labeler.yml
.github/labeler.yml
.github/workflows/mkosi.yml
NEWS
TODO
coccinelle/parsing_hacks.h
docs/ENVIRONMENT.md
man/custom-entities.ent.in
man/networkd.conf.xml
man/org.freedesktop.hostname1.xml
man/org.freedesktop.portable1.xml
man/org.freedesktop.systemd1.xml
man/pam_systemd_loadkey.xml
man/portablectl.xml
man/sd_event_add_inotify.xml
man/sd_event_add_io.xml
man/sd_id128_get_machine.xml
man/systemctl.xml
man/systemd-gpt-auto-generator.xml
man/systemd-measure.xml
man/systemd-pcrlock.xml
man/systemd-stub.xml
man/systemd-vmspawn.xml
man/systemd.network.xml
man/ukify.xml
meson.build
meson_options.txt
mkosi.conf
mkosi.images/system/mkosi.conf
mkosi.images/system/mkosi.conf.d/10-arch/mkosi.build.chroot
mkosi.images/system/mkosi.conf.d/10-arch/mkosi.conf
mkosi.images/system/mkosi.conf.d/10-arch/mkosi.conf.d/10-debug.conf [new file with mode: 0644]
mkosi.images/system/mkosi.conf.d/10-centos-fedora/mkosi.build.chroot
mkosi.images/system/mkosi.conf.d/10-centos-fedora/mkosi.conf
mkosi.images/system/mkosi.conf.d/10-centos-fedora/mkosi.conf.d/10-debug.conf [new file with mode: 0644]
mkosi.images/system/mkosi.conf.d/10-centos/mkosi.extra/usr/lib/sysusers.d/20-setup-groups.conf [new file with mode: 0644]
mkosi.images/system/mkosi.conf.d/10-centos/mkosi.extra/usr/lib/sysusers.d/20-setup-users.conf [new file with mode: 0644]
mkosi.images/system/mkosi.conf.d/10-debian-ubuntu/mkosi.build.chroot
mkosi.images/system/mkosi.conf.d/10-debian-ubuntu/mkosi.conf
mkosi.images/system/mkosi.conf.d/10-debian-ubuntu/mkosi.conf.d/10-debug.conf [new file with mode: 0644]
mkosi.images/system/mkosi.conf.d/10-opensuse/mkosi.build.chroot
mkosi.images/system/mkosi.conf.d/10-opensuse/mkosi.conf
mkosi.images/system/mkosi.conf.d/10-opensuse/mkosi.conf.d/10-debug.conf [new file with mode: 0644]
mkosi.images/system/mkosi.extra/usr/lib/systemd/system/user@.service.d/99-SYSTEMD_UNIT_PATH.conf [new file with mode: 0644]
mkosi.images/system/mkosi.postinst.chroot
pkg/arch
po/id.po
rules.d/60-persistent-media-controller.rules
shell-completion/bash/portablectl
shell-completion/bash/systemctl.in
src/analyze/analyze-security.c
src/analyze/analyze-srk.c
src/analyze/analyze-time-data.c
src/analyze/analyze-verify-util.c
src/basic/log.c
src/basic/log.h
src/basic/macro.h
src/basic/path-util.c
src/basic/path-util.h
src/basic/virt.c
src/boot/efi/boot.c
src/boot/efi/cpio.c
src/boot/efi/stub.c
src/boot/measure.c
src/core/cgroup.c
src/core/dbus-job.c
src/core/dbus.c
src/core/exec-invoke.c
src/core/job.c
src/core/kmod-setup.c
src/core/manager-serialize.c
src/core/manager.c
src/core/manager.h
src/core/transaction.c
src/core/unit.c
src/cryptenroll/cryptenroll-tpm2.c
src/cryptsetup/cryptsetup-tokens/luks2-tpm2.c
src/cryptsetup/cryptsetup.c
src/fundamental/uki.c
src/fundamental/uki.h
src/getty-generator/getty-generator.c
src/libsystemd-network/radv-internal.h
src/libsystemd-network/sd-radv.c
src/libsystemd-network/test-ndisc-ra.c
src/libsystemd/libsystemd.sym
src/libsystemd/sd-event/event-source.h
src/libsystemd/sd-event/sd-event.c
src/libsystemd/sd-event/test-event.c
src/libsystemd/sd-id128/sd-id128.c
src/libsystemd/sd-journal/journal-verify.c
src/libsystemd/sd-journal/sd-journal.c
src/libsystemd/sd-journal/test-journal-interleaving.c
src/login/logind-action.c
src/login/logind-user.c
src/network/meson.build
src/network/networkd-dhcp-common.c
src/network/networkd-dhcp-common.h
src/network/networkd-dhcp-server.c
src/network/networkd-dhcp4.c
src/network/networkd-dhcp6.c
src/network/networkd-dns.c [new file with mode: 0644]
src/network/networkd-dns.h [new file with mode: 0644]
src/network/networkd-gperf.gperf
src/network/networkd-json.c
src/network/networkd-manager.c
src/network/networkd-manager.h
src/network/networkd-ndisc.c
src/network/networkd-ndisc.h
src/network/networkd-network-gperf.gperf
src/network/networkd-network.c
src/network/networkd-network.h
src/network/networkd-ntp.c [new file with mode: 0644]
src/network/networkd-ntp.h [new file with mode: 0644]
src/network/networkd-radv.c
src/network/networkd-state-file.c
src/network/networkd.conf
src/network/test-network-tables.c
src/nspawn/nspawn-gperf.gperf
src/nspawn/nspawn-settings.c
src/nspawn/nspawn-settings.h
src/partition/repart.c
src/pcrextend/pcrextend.c
src/pcrlock/pcrlock.c
src/portable/portable.c
src/portable/portablectl.c
src/resolve/resolved-dnstls-openssl.c
src/shared/bus-wait-for-jobs.c
src/shared/bus-wait-for-units.c
src/shared/bus-wait-for-units.h
src/shared/clock-util.c
src/shared/compare-operator.c
src/shared/conf-parser.c
src/shared/conf-parser.h
src/shared/copy.c
src/shared/creds-util.c
src/shared/cryptsetup-tpm2.c
src/shared/dev-setup.c
src/shared/discover-image.c
src/shared/mount-setup.c
src/shared/openssl-util.c
src/shared/openssl-util.h
src/shared/sleep-config.c
src/shared/switch-root.c
src/shared/tests.c
src/shared/tests.h
src/shared/tpm2-util.c
src/shared/tpm2-util.h
src/shared/varlink.c
src/shared/vpick.c
src/shared/vpick.h
src/sysext/sysext.c
src/systemctl/systemctl-show.c
src/systemctl/systemctl-start-unit.c
src/systemctl/systemctl-util.c
src/systemd/sd-event.h
src/systemd/sd-id128.h
src/systemd/sd-radv.h
src/test/test-bpf-firewall.c
src/test/test-bpf-restrict-fs.c
src/test/test-creds.c
src/test/test-execute.c
src/test/test-fdset.c
src/test/test-id128.c
src/test/test-macro.c
src/test/test-mount-util.c
src/test/test-openssl.c
src/test/test-socket-util.c
src/test/test-stat-util.c
src/test/test-tpm2.c
src/test/test-vpick.c
src/timedate/timedated.c
src/tpm2-setup/tpm2-setup.c
src/ukify/ukify.py
src/update-utmp/update-utmp.c
src/userdb/userdbctl.c
src/vmspawn/vmspawn.c
src/vpick/vpick-tool.c
test/README.testsuite
test/TEST-02-UNITTESTS/meson.build [new file with mode: 0644]
test/integration_test_wrapper.py [new file with mode: 0755]
test/meson.build
test/test-network/systemd-networkd-tests.py
test/units/testsuite-29.sh
test/units/testsuite-50.sysext.sh
test/units/testsuite-70.pcrlock.sh

index bee39e8285222dbb5c55e447dc0d5aca5d6a9171..4d70058938aa1fec385e8655a2bccfe4f17690cc 100644 (file)
@@ -64,10 +64,10 @@ policy:
           - name: kernel-install
             keys: ['kernel-install']
 
-          - name: logind
+          - name: login
             keys: ['systemd-logind', 'loginctl', 'pam_systemd']
 
-          - name: machined
+          - name: machine
             keys: ['systemd-machined', 'machinectl']
 
           - name: modules-load
index e8952dc82a6dbd153e1f3aa7c854665442d1c8d5..ed4bf8292443ca9afabea0ac93370d449f20a970 100644 (file)
@@ -66,10 +66,7 @@ journal-remote:
     - any-glob-to-any-file: 'src/journal-remote/*'
 login:
   - changed-files:
-    - any-glob-to-any-file: '**/sd-login*/**'
-logind:
-  - changed-files:
-    - any-glob-to-any-file: 'src/login/*'
+    - any-glob-to-any-file: ['src/login/*', '**/sd-login*/**']
 meson:
   - changed-files:
     - any-glob-to-any-file: ['meson_options.txt', '**/meson.build']
index 2cffc3cf8264ed8691bdad6415fe35e560b9d6fa..8110395262ba5332dff50a5e28cc51fc1300114d 100644 (file)
@@ -74,7 +74,7 @@ jobs:
 
     steps:
     - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633
-    - uses: systemd/mkosi@a845d4108ac87ca443bd1ad78ab53520bffd2eda
+    - uses: systemd/mkosi@6ab7d9f09f8f2633f4b7c777a04e62e109486e2f
 
     # Freeing up disk space with rm -rf can take multiple minutes. Since we don't need the extra free space
     # immediately, we remove the files in the background. However, we first move them to a different location
diff --git a/NEWS b/NEWS
index f91e8e8914d0f06a4fa9d3209c61f204313a59fc..d990d34cd80b187d8315f8a7adb6f3b59f9c34a8 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -26,7 +26,7 @@ CHANGES WITH 256-rc1:
           or /efi/ hierarchies in /etc/fstab. This is to prevent the generator
           from interfering with systems where the ESP is explicitly configured
           to be mounted at some path, for example /boot/efi/ (this type of
-          setup is obsolete but still commonly found).
+          setup is obsolete, but still commonly found).
 
         * The behavior of systemd-sleep and systemd-homed has been updated to
           freeze user sessions when entering the various sleep modes or when
@@ -72,23 +72,23 @@ CHANGES WITH 256-rc1:
           embedded in the file name. The files are ordered by version and
           the newest one is selected.
 
-          systemd-nspawn --image=/--directory=, systemd-dissect, and the
-          RootDirectory=, RootImage=, ExtensionImages=, and
+          systemd-nspawn --image=/--directory=, systemd-dissect, systemd-portabled,
+          and the RootDirectory=, RootImage=, ExtensionImages=, and
           ExtensionDirectories= settings for units now support the vpick
           protocol and allow the latest version to be selected automatically if
           a "*.v/" directory is specified as the source.
 
-        * Encrypted service credentials may now be made accessible to
+        * Encrypted service credentials can now be made accessible to
           unprivileged users. systemd-creds gained new options --user/--uid=
           for encrypting/decrypting a credential for a specific user.
 
         * New command-line tool 'importctl' to download, import, and export
           disk images via systemd-importd is added with the following verbs:
           pull-tar, pull-raw, import-tar, import-raw, import-fs, export-tar,
-          export-raw, list-transfers, cancel-transfer. This functionality was
-          previously available in "machinectl", where it was exclusively for
-          machine image. The new "importctl" generalizes this for sysext,
-          confext, portable service images, too.
+          export-raw, list-transfers, and cancel-transfer. This functionality
+          was previously available in "machinectl", where it was used
+          exclusively for machine images. The new "importctl" generalizes this
+          for sysext, confext, and portable service images.
 
         Service Management:
 
@@ -97,9 +97,9 @@ CHANGES WITH 256-rc1:
           enabled by default in the initrd.
 
         * New unit setting WantsMountsFor= has been added. It is analogous to
-          RequiresMountsFor=, but with a Wants= dependency instead of
-          Requires=. This new logic is used in various places where mounts were
-          added as dependencies for other settings (WorkingDirectory=-…,
+          RequiresMountsFor=, but creates a Wants= dependency instead of
+          Requires=. This new logic is now used in various places where mounts
+          were added as dependencies for other settings (WorkingDirectory=-…,
           PrivateTmp=yes, cryptsetup lines with 'nofail').
 
         * New unit setting MemoryZSwapWriteback= can be used to control the new
@@ -107,9 +107,11 @@ CHANGES WITH 256-rc1:
 
         * The manager gained a org.freedesktop.systemd1.StartAuxiliaryScope()
           D-Bus method to devolve some processes from a service into a new
-          scope.  This new scope will remain even if the original service unit
-          is restarted. Control group properties of the new scope are copied
-          from the originating unit, so various limits are retained.
+          scope. This new scope will remain running, even when the original
+          service unit is restarted or stopped. This allows a service unit to
+          split out some worker processes which need to continue running.
+          Control group properties of the new scope are copied from the
+          originating unit, so various limits are retained.
 
         * Units now expose properties EffectiveMemoryMax=,
           EffectiveMemoryHigh=, and EffectiveTasksMax=, which report the
@@ -136,26 +138,27 @@ CHANGES WITH 256-rc1:
           system credential.
 
         * The systemd binary will no longer chainload sysvinit's "telinit"
-          binary when called under the init/telinit name on a system that
-          isn't booted with systemd. This previously has been supported to make
-          sure a distribution that has both init systems installed can be
-          reasonably switched from one to the other via a simple
-          reboot. Distributions apparently have lost interest in this, and the
-          functionality has not been supported on the primary distribution this
-          was still intended for for a longer time, and hence has been removed
-          now.
-
-        * A new concept called "capsules" has been introduced. "Capsules"
-          encapsulate additional per-user service managers, whose users are
-          transient and are only defined as long as the service manager
-          is running (implemented via DynamicUser=1). These service managers run
-          off home directories defined in /var/lib/capsules/<name>, where
-          <name> is a the capsule's name. These home directories can contain
-          regular per-user services and other units. A capsule is started via a
-          simple "systemctl start capsule@<name>.service". See the
-          capsule@.service(5) man page for further details. Various systemd
-          tools (including, and most importantly, systemctl and systemd-run)
-          have been updated to interact with capsules via the new
+          binary when called under the init/telinit name on a system that isn't
+          booted with systemd. This previously has been supported to make sure
+          a distribution that has both init systems installed can reasonably
+          switch from one to the other via a simple reboot. Distributions
+          apparently have lost interest in this, and the functionality has not
+          been supported on the primary distribution this was still intended
+          for for a long time, and hence has been removed now.
+
+        * A new concept called "capsules" has been introduced. "Capsules" wrap
+          additional per-user service managers, whose users are transient and
+          are only defined as long as the service manager is running. (This is
+          implemented via DynamicUser=1), allowing a user manager to be used to
+          manager a group of processes without needing to create an actual user
+          account. These service managers run with home directories of
+          /var/lib/capsules/<capsule-name> and can contain regular services and
+          other units. A capsule is started via a simple "systemctl start
+          capsule@<name>.service". See the capsule@.service(5) man page for
+          further details.
+
+          Various systemd tools (including, and most importantly, systemctl and
+          systemd-run) have been updated to interact with capsules via the new
           "--capsule="/"-C" switch.
 
         * .socket units gained a new setting PassFileDescriptorsToExec=, taking
@@ -163,7 +166,8 @@ CHANGES WITH 256-rc1:
           encapsulates are passed to the ExecStartPost=, ExecStopPre=,
           ExecStopPost= using the usual $LISTEN_FDS interface. This may be used
           for doing additional initializations on the sockets once they are
-          allocated (for example, install an additional eBPF program on them).
+          allocated. (For example, to install an additional eBPF program on
+          them).
 
         * The .socket setting MaxConnectionsPerSource= (which so far put a
           limit on concurrent connections per IP in Accept=yes socket units),
@@ -173,13 +177,13 @@ CHANGES WITH 256-rc1:
           services in a simple Accept=yes mode.
 
         * The service manager will now maintain a counter of soft reboot cycles
-          the system went through so far. It may be queried via the D-Bus APIs.
+          the system went through. It may be queried via the D-Bus APIs.
 
         * systemd's execution logic now supports the new pidfd_spawn() API
           introduced by glibc 2.39, which allows us to invoke a subprocess in a
           target cgroup and get a pidfd back in a single operation.
 
-        * systemd/PID 1 will now send an additional sd_notify() message to its
+        * systemd/PID 1 will now send an additional sd_notify() message to its
           supervising VMM or container manager reporting the selected hostname
           ("X_SYSTEMD_HOSTNAME=") and machine ID ("X_SYSTEMD_MACHINE_ID=") at
           boot. Moreover, the service manager will send additional sd_notify()
@@ -189,9 +193,10 @@ CHANGES WITH 256-rc1:
           reports "ssh-access.target" being reached a VMM/container manager
           knows it can now connect to the system via SSH. Finally, a new
           sd_notify() message ("X_SYSTEMD_SIGNALS_LEVEL=2") is sent the moment
-          PID 1 successfully completed installation of its various UNIX process
-          signal handlers (i.e. the moment where SIGRTMIN+4 sent to PID 1 will
-          start to have the effect of shutting down the system cleanly).
+          PID 1 has successfully completed installation of its various UNIX
+          process signal handlers (i.e. the moment where SIGRTMIN+4 sent to
+          PID 1 will start to have the effect of shutting down the system
+          cleanly).
 
         Journal:
 
@@ -461,20 +466,8 @@ CHANGES WITH 256-rc1:
 
         * confexts are loaded by systemd-stub from the ESP as well.
 
-        * The pcrlock policy is saved in an unencrypted credential file
-          "pcrlock.<entry-token>.cred" under XBOOTLDR/ESP in the
-          /loader/credentials/ directory. It will be picked up at boot by
-          systemd-stub and passed to the initrd, where it can be used to unlock
-          the root file system.
-
         * kernel-install gained support for --root= for the 'list' verb.
 
-        * systemd-pcrlock gained an --entry-token= option to configure the
-          entry-token.
-
-        * systemd-pcrlock now provides a basic Varlink interface and can be run
-          as a daemon via a template unit.
-
         * bootctl now provides a basic Varlink interface and can be run as a
           daemon via a template unit.
 
@@ -498,6 +491,30 @@ CHANGES WITH 256-rc1:
           for enrolling "dbx" too (Previously, only db/KEK/PK enrollment was
           supported). It also now supports UEFI "Custom" mode.
 
+        * The pcrlock policy is saved in an unencrypted credential file
+          "pcrlock.<entry-token>.cred" under XBOOTLDR/ESP in the
+          /loader/credentials/ directory. It will be picked up at boot by
+          systemd-stub and passed to the initrd, where it can be used to unlock
+          the root file system.
+
+        * systemd-pcrlock gained an --entry-token= option to configure the
+          entry-token.
+
+        * systemd-pcrlock now provides a basic Varlink interface and can be run
+          as a daemon via a template unit.
+
+        * systemd-pcrlock's TPM nvindex access policy has been modified, this
+          means that previous pcrlock policies stored in nvindexes are
+          invalidated. They must be removed (systemd-pcrlock remove-policy) and
+          recreated (systemd-pcrlock make-policy). For the time being
+          systemd-pcrlock remains an experimental feature, but it is expected
+          to become stable in the next release, i.e. v257.
+
+        * systemd-pcrlock's --recovery-pin= switch now takes three values:
+          "hide", "show", "query". If "show" is selected the automatically
+          generated recovery PIN is shown to the user. If "query" is selected
+          then the PIN is queried from the user.
+
         systemd-run/run0:
 
         * systemd-run is now a multi-call binary. When invoked as 'run0', it
@@ -591,6 +608,12 @@ CHANGES WITH 256-rc1:
           --ssh-key-type= to optionally set up transient SSH keys to pass to the
           invoked VMs in order to be able to SSH into them once booted.
 
+        * systemd-vmspawn will now enable various "HyperV enlightenments" and
+          the "VM Generation ID" on the VMs.
+
+        * A new environment variable $SYSTEMD_VMSPAWN_QEMU_EXTRA may carry
+          additional qemu command line options to pass to qemu.
+
         systemd-repart:
 
         * systemd-repart gained new options --generate-fstab= and
@@ -626,6 +649,10 @@ CHANGES WITH 256-rc1:
           sd_journal_stream_fd() but creates a log stream targeted at a
           specific log namespace.
 
+        * The sd-id128 API gained a new API call
+          sd_id128_get_invocation_app_specific() for acquiring an app-specific
+          ID that is derived from the service invocation ID.
+
         systemd-cryptsetup/systemd-cryptenroll:
 
         * systemd-cryptenroll can now enroll directly with a PKCS11 public key
diff --git a/TODO b/TODO
index 1c77f9ffc5c1db32df0d783fb8a2a8e312a1330c..548a7c31fc5292a05f5ae7eb79726088d1d755c8 100644 (file)
--- a/TODO
+++ b/TODO
@@ -130,6 +130,10 @@ Deprecations and removals:
 
 Features:
 
+* systemd-repart should probably enable btrfs' "temp_fsid" feature for all file
+  systems it creates, as we have no interest in RAID for repart, and it should
+  make sure that we can mount them trivially everywhere.
+
 * systemd-nspawn should get the same SSH key support that vmspawn now has.
 
 * insert the new pidfs inode number as a third field into PidRef, so that
@@ -325,10 +329,7 @@ Features:
     PCRs.
 
 * vmspawn:
-  - enable hyperv extension by default (https://www.qemu.org/docs/master/system/i386/hyperv.html)
-  - register with machined
   - run in scope unit when invoked from command line, and machined registration is off
-  - support --directory= via virtiofs
   - sd_notify support
   - --ephemeral support
   - --read-only support
index 9e77e514dd034629df8d6857298b3c5d9a1c8103..f88dae0c86b65c8f5ffc51ac006bed12ce370030 100644 (file)
@@ -68,6 +68,7 @@
 /* Mark a couple of iterator explicitly as iterators, otherwise Coccinelle gets a bit confused. Coccinelle
  * can usually infer this information automagically, but in these specific cases it needs a bit of help. */
 #define FOREACH_ARRAY(i, array, num) YACFE_ITERATOR
+#define FOREACH_ELEMENT(i, array) YACFE_ITERATOR
 #define FOREACH_DIRENT_ALL(de, d, on_error) YACFE_ITERATOR
 #define FOREACH_STRING(x, y, ...) YACFE_ITERATOR
 #define HASHMAP_FOREACH(e, h) YACFE_ITERATOR
index 961601c72e0dc76f3215d489667a65ff02d00a1d..8068d0d33cf214a1803c688aabbb9a347fcb24d3 100644 (file)
@@ -191,6 +191,9 @@ All tools:
   expected format is six groups of two hexadecimal digits separated by colons,
   e.g. `SYSTEMD_VMSPAWN_NETWORK_MAC=12:34:56:78:90:AB`
 
+* `$SYSTEMD_VMSPAWN_QEMU_EXTRA=…` – may contain additional command line
+  arguments to append the qemu command line.
+
 `systemd-logind`:
 
 * `$SYSTEMD_BYPASS_HIBERNATION_MEMORY_CHECK=1` — if set, report that
index c82e2b8d8f6ece720cb3534b0fbc6420bce21689..8dfc1eb98c7c49403d03d19012b4c9256ca78477 100644 (file)
@@ -19,3 +19,4 @@
 <!ENTITY DEFAULT_USER_TIMEOUT "{{DEFAULT_USER_TIMEOUT_SEC}} s">
 <!ENTITY DEFAULT_KEYMAP "{{SYSTEMD_DEFAULT_KEYMAP}}">
 <!ENTITY fedora_latest_version "40">
+<!ENTITY fedora_cloud_release "1.10">
index 8820fcc507da1d02d0d708056a0d09b5ec700ab2..64f83bb5f91d634252da552de9f1eb8d141c4de0 100644 (file)
         <xi:include href="version-info.xml" xpointer="v254"/>
         </listitem>
       </varlistentry>
+
+      <varlistentry>
+        <term><varname>UseDomains=</varname></term>
+        <listitem>
+          <para>Specifies the network- and protocol-independent default value for the same settings in
+          [IPv6AcceptRA], [DHCPv4], and [DHCPv6] sections below. Takes a boolean, or the special value
+          <option>route</option>. See the same setting in
+          <citerefentry><refentrytitle>systemd.network</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
+          Defaults to <literal>no</literal>.</para>
+
+          <xi:include href="version-info.xml" xpointer="v256"/>
+        </listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+
+  <refsect1>
+    <title>[IPv6AcceptRA] Section Options</title>
+
+    <para>This section configures the default setting of the Neighbor Discovery. The following options are
+    available in the [IPv6AcceptRA] section:</para>
+
+    <variablelist class='network-directives'>
+      <varlistentry>
+        <term><varname>UseDomains=</varname></term>
+        <listitem>
+          <para>Specifies the network-independent default value for the same setting in the [IPv6AcceptRA]
+          section in
+          <citerefentry><refentrytitle>systemd.network</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
+          Takes a boolean, or the special value <option>route</option>. When unspecified, the value specified
+          in the [Network] section in
+          <citerefentry><refentrytitle>networkd.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
+          which defaults to <literal>no</literal>, will be used.</para>
+
+          <xi:include href="version-info.xml" xpointer="v256"/>
+        </listitem>
+      </varlistentry>
     </variablelist>
   </refsect1>
 
@@ -260,11 +297,9 @@ DUIDRawData=00:00:ab:11:f9:2a:c2:77:29:f9:5c:00</programlisting>
 
       <varlistentry>
         <term><varname>UseDomains=</varname></term>
-          <listitem><para>Specifies the default value for per-network <varname>UseDomains=</varname>.
-          Takes a boolean. See for details in
-          <citerefentry><refentrytitle>systemd.network</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
-          Defaults to <literal>no</literal>.</para>
-          
+        <listitem>
+          <para>Same as the one in the [IPv6AcceptRA] section, but applied for DHCPv4 protocol.</para>
+
           <xi:include href="version-info.xml" xpointer="v256"/>
         </listitem>
       </varlistentry>
@@ -310,12 +345,9 @@ DUIDRawData=00:00:ab:11:f9:2a:c2:77:29:f9:5c:00</programlisting>
 
     <variablelist class='network-directives'>
       <varlistentry>
-        <term><varname>PersistLeases=</varname></term>
+        <term><varname>UseDomains=</varname></term>
         <listitem>
-          <para>Specifies the default value for per-network <varname>PersistLeases=</varname>.
-          Takes a boolean. See for details in
-          <citerefentry><refentrytitle>systemd.network</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
-          Defaults to <literal>yes</literal>.</para>
+          <para>Same as the one in the [IPv6AcceptRA] section, but applied for DHCPv4 protocol.</para>
 
           <xi:include href="version-info.xml" xpointer="v256"/>
         </listitem>
index 61d9831ca6b8a5aaaccaa8a6c7b5fe4165aa3149..d16ae9ab1c276fc458f09f611f8ce717f27fc942 100644 (file)
@@ -108,20 +108,6 @@ node /org/freedesktop/hostname1 {
 };
     </programlisting>
 
-    <!--method GetHardwareSerial is not documented!-->
-
-    <!--property OperatingSystemSupportEnd is not documented!-->
-
-    <!--property HardwareVendor is not documented!-->
-
-    <!--property HardwareModel is not documented!-->
-
-    <!--property FirmwareVersion is not documented!-->
-
-    <!--property FirmwareVendor is not documented!-->
-
-    <!--property FirmwareDate is not documented!-->
-
     <!--Autogenerated cross-references for systemd.directives, do not edit-->
 
     <variablelist class="dbus-interface" generated="True" extra-ref="org.freedesktop.hostname1"/>
@@ -299,6 +285,22 @@ node /org/freedesktop/hostname1 {
     <constant>UINT32_MAX</constant> otherwise. See <citerefentry project="man-pages"><refentrytitle>vsock</refentrytitle><manvolnum>7</manvolnum></citerefentry> for
     details.</para>
 
+    <para><varname>OperatingSystemSupportEnd</varname> exposes when the OS' vendor support ends, if this
+    information is known. It's an unsigned 64bit value, in µs since the UNIX epoch, UTC. If this information
+    is not known carries the value 2^64-1, i.e. <constant>UINT64_MAX</constant>.</para>
+
+    <para><varname>HardwareVendor</varname> and <varname>HardwareModel</varname> expose information about the
+    vendor of the hardware of the system. If no such information can be determined these properties are set
+    to empty strings.</para>
+
+    <para><varname>FirmwareVersion</varname> and <varname>FirmwareVendor</varname> expose information about
+    the system's firmware, i.e. a version string and a vendor name. If no such information can be determined
+    these properties are set to empty strings.</para>
+
+    <para><varname>FirmwareDate</varname> exposes the firmware build date, if that information is known. It's
+    an unsigned 64bit value, in µs since the UNIX epoch, UTC. If not known
+    <constant>UNIT64_MAX</constant>.</para>
+
     <refsect2>
       <title>Methods</title>
 
@@ -333,6 +335,10 @@ node /org/freedesktop/hostname1 {
       requires root privileges, and this method allows access to unprivileged clients through the polkit
       framework.</para>
 
+      <para><function>GetHardwareSerial()</function> returns the "hardware serial" as exposed by the kernel
+      based on DMI information. Reading the file directly requires root privileges, and this method allows
+      access to unprivileged clients through the polkit framework.</para>
+
       <para><function>Describe()</function> returns a JSON representation of all properties in one.</para>
     </refsect2>
 
index c68995d95248f797e108e98071fc1d527bafb532..4de3da2905dfe9567338dc46c46fb184b273c2aa 100644 (file)
@@ -259,6 +259,9 @@ node /org/freedesktop/portable1 {
       on the system. Note that this method returns only after all the listed operations are completed,
       and due to the I/O involved it might take some time.</para>
 
+      <xi:include href="vpick.xml" xpointer="image"/>
+      <xi:include href="vpick.xml" xpointer="directory"/>
+
       <para><function>AttachImageWithExtensions()</function> attaches a portable image to the system.
       This method is a superset of <function>AttachImage()</function> with the addition of
       a list of extensions as input parameter, which will be overlaid on top of the main
index 6e90662fa9ee416ea7063c59b9079846b9b54826..f60b3ba702852ddd63e6dc7fc1c87c1aac3acc8a 100644 (file)
@@ -575,8 +575,6 @@ node /org/freedesktop/systemd1 {
 
     <!--method EnqueueUnitJob is not documented!-->
 
-    <!--method CleanUnit is not documented!-->
-
     <!--method FreezeUnit is not documented!-->
 
     <!--method ThawUnit is not documented!-->
@@ -1585,6 +1583,12 @@ node /org/freedesktop/systemd1 {
       shouldn't be bound to a lifecycle of the service, e.g. they should continue running after the restart
       of the service. Note that the main PID of the service can not be migrated to an auxiliary scope.
       Also, <varname>flags</varname> argument must be 0 and is reserved for future extensions.</para>
+
+      <para><function>CleanUnit()</function> deletes the configuration, state, logs, cache and runtime data
+      directories and clear out the file descriptors store for the unit, as specified in the mask
+      parameters. The possible values are <literal>configuration</literal>, <literal>state</literal>,
+      <literal>logs</literal>, <literal>cache</literal>, <literal>runtime</literal>,
+      <literal>fdstore</literal>, and <literal>all</literal>.</para>
     </refsect2>
 
     <refsect2>
index becb32adcd028e74a6da05f117500e5e7be3d6b1..13d1686bd1e6e4340fa504058f69e30fbabb509a 100644 (file)
         <term><varname>keyname=</varname></term>
 
         <listitem><para>Takes a string argument which sets the keyname to read.
-        The default is <literal>cryptsetup</literal>, which is used by
+        The default is <literal>cryptsetup</literal>.
+        During boot,
         <citerefentry><refentrytitle>systemd-cryptsetup@.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
-        to store LUKS passphrase during boot.</para>
+        stores a passphrase or PIN in the keyring.
+        The LUKS2 volume key can also be used, via the <option>link-volume-key</option> option in
+        <citerefentry><refentrytitle>crypttab</refentrytitle><manvolnum>5</manvolnum></citerefentry>.</para>
+
+        <table>
+          <title>
+            Possible values for <varname>keyname</varname>.
+          </title>
+
+          <tgroup cols='2'>
+            <colspec colname='value' />
+            <colspec colname='description' />
+            <thead>
+              <row>
+                <entry>Value</entry>
+                <entry>Description</entry>
+              </row>
+            </thead>
+            <tbody>
+              <row>
+                <entry>cryptsetup</entry>
+                <entry>Passphrase or recovery key</entry>
+              </row>
+              <row>
+                <entry>fido2-pin</entry>
+                <entry>Security token PIN</entry>
+              </row>
+              <row>
+                <entry>luks2-pin</entry>
+                <entry>LUKS2 token PIN</entry>
+              </row>
+              <row>
+                <entry>tpm2-pin</entry>
+                <entry>TPM2 PIN</entry>
+              </row>
+            </tbody>
+          </tgroup>
+        </table>
 
         <xi:include href="version-info.xml" xpointer="v255"/></listitem>
       </varlistentry>
 
     <programlisting>
 -auth       optional    pam_systemd_loadkey.so
+-auth       optional    pam_gnome_keyring.so
 -session    optional    pam_gnome_keyring.so auto_start
 -session    optional    pam_kwallet5.so auto_start
     </programlisting>
index bcfaf7ced15ca310f0e51011357363ec651635c5..92d8ff03aa76fc7c82b84295677b399c36fe8b31 100644 (file)
         immediately started (blocking operation unless <option>--no-block</option> is passed) and/or enabled after
         attaching the image.</para>
 
+        <xi:include href="vpick.xml" xpointer="image"/>
+        <xi:include href="vpick.xml" xpointer="directory"/>
         <xi:include href="version-info.xml" xpointer="v239"/>
         </listitem>
       </varlistentry>
         <xi:include href="version-info.xml" xpointer="v245"/></listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><option>--clean</option></term>
+
+        <listitem><para>When detaching ensure the configuration, state, logs, cache, and runtime data
+        directories of the portable service are removed from the host system.</para>
+
+        <xi:include href="version-info.xml" xpointer="v256"/></listitem>
+      </varlistentry>
+
       <varlistentry>
         <term><option>--extension=<replaceable>PATH</replaceable></option></term>
 
         <para>Note that the same extensions have to be specified, in the same order, when attaching
         and detaching.</para>
 
+        <xi:include href="vpick.xml" xpointer="image"/>
+        <xi:include href="vpick.xml" xpointer="directory"/>
         <xi:include href="version-info.xml" xpointer="v249"/></listitem>
       </varlistentry>
 
index 69c51948992ad138f73ecbe19a00096774e65a1f..ed26c8ac96ecf1deab7afe7fd69dab1cb0a168f9 100644 (file)
@@ -19,6 +19,7 @@
     <refname>sd_event_add_inotify</refname>
     <refname>sd_event_add_inotify_fd</refname>
     <refname>sd_event_source_get_inotify_mask</refname>
+    <refname>sd_event_source_get_inotify_path</refname>
     <refname>sd_event_inotify_handler_t</refname>
 
     <refpurpose>Add an "inotify" file system inode event source to an event loop</refpurpose>
       <funcprototype>
         <funcdef>int <function>sd_event_source_get_inotify_mask</function></funcdef>
         <paramdef>sd_event_source *<parameter>source</parameter></paramdef>
-        <paramdef>uint32_t *<parameter>mask</parameter></paramdef>
+        <paramdef>uint32_t *<parameter>ret</parameter></paramdef>
+      </funcprototype>
+
+      <funcprototype>
+        <funcdef>int <function>sd_event_source_get_inotify_path</function></funcdef>
+        <paramdef>sd_event_source *<parameter>source</parameter></paramdef>
+        <paramdef>const char **<parameter>ret</parameter></paramdef>
       </funcprototype>
 
     </funcsynopsis>
     event source created previously with <function>sd_event_add_inotify()</function>. It takes the event source object
     as the <parameter>source</parameter> parameter and a pointer to a <type>uint32_t</type> variable to return the mask
     in.</para>
+
+    <para><function>sd_event_source_get_inotify_path()</function> retrieves the target path of the configured
+    inotify watch of an event source created previously with <function>sd_event_add_inotify()</function>. It
+    takes the event source object as the <parameter>source</parameter> parameter and a pointer to a
+    <type>const char **</type> variable to return the path in. The caller must not free the returned path.
+    </para>
   </refsect1>
 
   <refsect1>
         <varlistentry>
           <term><constant>-ESTALE</constant></term>
 
-          <listitem><para>The event loop is already terminated.</para></listitem>
+          <listitem>
+            <para>Returned by <function>sd_event_source_add_inotify()</function> or
+            <function>sd_event_source_add_inotify_fd()</function> when the event loop is already terminated.
+            Returned by <function>sd_event_source_get_inotify_path()</function> when no active inode data is
+            assigned to the event source, e.g. when the event source is disabled.</para>
+          </listitem>
 
         </varlistentry>
 
         <varlistentry>
           <term><constant>-EBADF</constant></term>
 
-          <listitem><para>The passed file descriptor is not valid.</para>
+          <listitem><para>The passed file descriptor is not valid.</para></listitem>
 
-          <xi:include href="version-info.xml" xpointer="v250"/></listitem>
         </varlistentry>
 
         <varlistentry>
           <term><constant>-ENOSYS</constant></term>
 
-          <listitem><para><function>sd_event_add_inotify_fd()</function> was called without
-          <filename>/proc/</filename> mounted.</para>
+          <listitem>
+            <para><function>sd_event_add_inotify_fd()</function> or
+            <function>sd_event_source_get_inotify_path()</function> was called without
+            <filename>/proc/</filename> mounted.</para>
+          </listitem>
 
-          <xi:include href="version-info.xml" xpointer="v250"/></listitem>
         </varlistentry>
 
       </variablelist>
     <function>sd_event_add_inotify()</function>, and
     <function>sd_event_source_get_inotify_mask()</function> were added in version 239.</para>
     <para><function>sd_event_add_inotify_fd()</function> was added in version 250.</para>
+    <para><function>sd_event_source_get_inotify_path()</function> was added in version 256.</para>
   </refsect1>
 
   <refsect1>
index 3935141fc0501c44bba9baa09869b4dea332a76c..09a5b11dfffe93046f279db2dd178f223d988c86 100644 (file)
     source object and returns the non-negative file descriptor
     or a negative error number on error (see below).</para>
 
-    <para><function>sd_event_source_set_io_fd()</function>
-    changes the UNIX file descriptor of an I/O event source created
-    previously with <function>sd_event_add_io()</function>. It takes
-    the event source object and the new file descriptor.</para>
-
-    <para><function>sd_event_source_set_io_fd_own()</function> controls whether the file descriptor of the event source
-    shall be closed automatically when the event source is freed, i.e. whether it shall be considered 'owned' by the
-    event source object. By default it is not closed automatically, and the application has to do this on its own. The
-    <parameter>b</parameter> parameter is a boolean parameter: if zero, the file descriptor is not closed automatically
-    when the event source is freed, otherwise it is closed.</para>
+    <para><function>sd_event_source_set_io_fd()</function> changes the UNIX file descriptor of an I/O event
+    source created previously with <function>sd_event_add_io()</function>. It takes the event source object
+    and the new file descriptor. If the event source takes the ownership of the previous file descriptor,
+    that is, <function>sd_event_source_set_io_fd_own()</function> was called for the event source with a
+    non-zero value, then the previous file descriptor will be closed and the event source will also take the
+    ownership of the new file descriptor on success.</para>
+
+    <para><function>sd_event_source_set_io_fd_own()</function> controls whether the file descriptor of the
+    event source shall be closed automatically when the event source is freed (or when the file descriptor
+    assigned to the event source is replaced by <function>sd_event_source_set_io_fd()</function>), i.e.
+    whether it shall be considered 'owned' by the event source object. By default it is not closed
+    automatically, and the application has to do this on its own. The <parameter>b</parameter> parameter is a
+    boolean parameter: if zero, the file descriptor is not closed automatically when the event source is
+    freed, otherwise it is closed.</para>
 
     <para><function>sd_event_source_get_io_fd_own()</function> may be used to query the current setting of the file
     descriptor ownership boolean flag as set with <function>sd_event_source_set_io_fd_own()</function>. It returns
index 6904f2953c6709692a8bf1edb823a60009cf5fae..59f3266e6feedbb929c679b371b08836f44734b5 100644 (file)
         <paramdef>sd_id128_t *<parameter>ret</parameter></paramdef>
       </funcprototype>
 
+      <funcprototype>
+        <funcdef>int <function>sd_id128_get_invocation_app_specific</function></funcdef>
+        <paramdef>sd_id128_t <parameter>app_id</parameter></paramdef>
+        <paramdef>sd_id128_t *<parameter>ret</parameter></paramdef>
+      </funcprototype>
+
     </funcsynopsis>
   </refsynopsisdiv>
 
     for details. The ID is cached internally. In future a different mechanism to determine the invocation ID
     may be added.</para>
 
+    <para><function>sd_id128_get_invocation_app_specific()</function> derives an application-specific ID from
+    the invocation ID.</para>
+
     <para>Note that <function>sd_id128_get_machine_app_specific()</function>,
-    <function>sd_id128_get_boot()</function>, <function>sd_id128_get_boot_app_specific()</function>, and
-    <function>sd_id128_get_invocation()</function> always return UUID Variant 1 Version 4 compatible IDs.
-    <function>sd_id128_get_machine()</function> will also return a UUID Variant 1 Version 4 compatible ID on
-    new installations but might not on older. It is possible to convert the machine ID non-reversibly into a
-    UUID Variant 1 Version 4 compatible one. For more information, see
+    <function>sd_id128_get_boot()</function>, <function>sd_id128_get_boot_app_specific()</function>,
+    <function>sd_id128_get_invocation()</function> and
+    <function>sd_id128_get_invocation_app_specific</function> always return UUID Variant 1 Version 4
+    compatible IDs. <function>sd_id128_get_machine()</function> will also return a UUID Variant 1 Version 4
+    compatible ID on new installations but might not on older. It is possible to convert the machine ID
+    non-reversibly into a UUID Variant 1 Version 4 compatible one. For more information, see
     <citerefentry><refentrytitle>machine-id</refentrytitle><manvolnum>5</manvolnum></citerefentry>. It is
     hence guaranteed that these functions will never return the ID consisting of all zero or all one bits
     (<constant>SD_ID128_NULL</constant>, <constant>SD_ID128_ALLF</constant>) — with the possible exception of
@@ -262,6 +272,7 @@ As man:sd-id128(3) macro:
     <para><function>sd_id128_get_machine_app_specific()</function> was added in version 233.</para>
     <para><function>sd_id128_get_boot_app_specific()</function> was added in version 240.</para>
     <para><function>sd_id128_get_app_specific()</function> was added in version 255.</para>
+    <para><function>sd_id128_get_invocation_app_specific()</function> was added in version 256.</para>
   </refsect1>
 
   <refsect1>
index ca101d5b75cd937cd1f73a055812d4aae47eccfe..a8bc85707b6600547df5484f2ebd19f1c99c5310 100644 (file)
@@ -569,6 +569,51 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
             these three types of resources are generally redundant and reproducible on the next invocation of
             the unit). Note that the specified units must be stopped to invoke this operation.</para>
 
+            <table>
+              <title>
+                Possible values for <option>--what=</option>
+              </title>
+
+              <tgroup cols='2'>
+                <thead>
+                  <row>
+                    <entry>Value</entry>
+                    <entry>Unit Setting</entry>
+                  </row>
+                </thead>
+                <tbody>
+                  <row>
+                    <entry><literal>runtime</literal></entry>
+                    <entry><varname>RuntimeDirectory=</varname></entry>
+                  </row>
+                  <row>
+                    <entry><literal>state</literal></entry>
+                    <entry><varname>StateDirectory=</varname></entry>
+                  </row>
+                  <row>
+                    <entry><literal>cache</literal></entry>
+                    <entry><varname>CacheDirectory=</varname></entry>
+                  </row>
+                  <row>
+                    <entry><literal>logs</literal></entry>
+                    <entry><varname>LogsDirectory=</varname></entry>
+                  </row>
+                  <row>
+                    <entry><literal>configuration</literal></entry>
+                    <entry><varname>ConfigurationDirectory=</varname></entry>
+                  </row>
+                  <row>
+                    <entry><literal>fdstore</literal></entry>
+                    <entry><varname>FileDescriptorStorePreserve=</varname></entry>
+                  </row>
+                  <row>
+                    <entry><literal>all</literal></entry>
+                    <entry>All of the above</entry>
+                  </row>
+                </tbody>
+              </tgroup>
+            </table>
+
             <xi:include href="version-info.xml" xpointer="v243"/>
           </listitem>
         </varlistentry>
index c8cf12a005993e67b2b1bff42a12aa02e50f27c5..0893b3f4e8216b5b5a9626f07d84608fbcc1d330 100644 (file)
 
     <table>
       <title>Partition Type GUIDs</title>
-      <tgroup cols='3' align='left' colsep='1' rowsep='1'>
+      <tgroup cols='5' align='left' colsep='1' rowsep='1'>
+        <colspec colname="type" />
         <colspec colname="guid" />
         <colspec colname="name" />
         <colspec colname="where" />
         <colspec colname="explanation" />
         <thead>
           <row>
-            <entry>Partition Type GUID</entry>
+            <entry>Partition Type</entry>
+            <entry>GUID</entry>
             <entry>Name</entry>
             <entry>Mount Point</entry>
             <entry>Explanation</entry>
         </thead>
         <tbody>
           <row>
-            <entry><constant>SD_GPT_ROOT_X86_64</constant> <constant>4f68bce3-e8cd-4db1-96e7-fbcaf984b709</constant></entry>
+            <entry><constant>SD_GPT_ROOT_X86_64</constant></entry>
+            <entry><constant>4f68bce3-e8cd-4db1-96e7-fbcaf984b709</constant></entry>
             <entry><filename>Root Partition (x86-64)</filename></entry>
             <entry><filename>/</filename></entry>
-            <entry>The first partition with this type UUID, located on the same disk as the ESP, is used as the root file system <filename>/</filename> on AMD64 / 64-bit x86 systems.</entry>
+            <entry>The first partition with this type UUID, located on the same disk as the ESP used for booting, is used as the root file system <filename>/</filename> on AMD64 / 64-bit x86 systems.</entry>
           </row>
           <row>
-            <entry><constant>SD_GPT_ROOT_ARM64</constant> <constant>b921b045-1df0-41c3-af44-4c6f280d3fae</constant></entry>
+            <entry><constant>SD_GPT_ROOT_ARM64</constant></entry>
+            <entry><constant>b921b045-1df0-41c3-af44-4c6f280d3fae</constant></entry>
             <entry><filename>Root Partition (64-bit ARM)</filename></entry>
             <entry><filename>/</filename></entry>
-            <entry>The first partition with this type UUID, located on the same disk as the ESP, is used as the root file system <filename>/</filename> on AArch64 / 64-bit ARM systems.</entry>
+            <entry>The first partition with this type UUID, located on the same disk as the ESP used for booting, is used as the root file system <filename>/</filename> on AArch64 / 64-bit ARM systems.</entry>
           </row>
           <row>
-            <entry>
-              <constant>SD_GPT_ROOT_ALPHA</constant> <constant>SD_GPT_ROOT_ARC</constant> <constant>SD_GPT_ROOT_ARM</constant> <constant>SD_GPT_ROOT_ARM64</constant> <constant>SD_GPT_ROOT_IA64</constant> <constant>SD_GPT_ROOT_LOONGARCH64</constant> <constant>SD_GPT_ROOT_MIPS</constant> <constant>SD_GPT_ROOT_MIPS64</constant> <constant>SD_GPT_ROOT_MIPS_LE</constant> <constant>SD_GPT_ROOT_MIPS64_LE</constant> <constant>SD_GPT_ROOT_PARISC</constant> <constant>SD_GPT_ROOT_PPC</constant> <constant>SD_GPT_ROOT_PPC64</constant> <constant>SD_GPT_ROOT_PPC64_LE</constant> <constant>SD_GPT_ROOT_RISCV32</constant> <constant>SD_GPT_ROOT_RISCV64</constant> <constant>SD_GPT_ROOT_S390</constant> <constant>SD_GPT_ROOT_S390X</constant> <constant>SD_GPT_ROOT_TILEGX</constant> <constant>SD_GPT_ROOT_X86</constant> <constant>SD_GPT_ROOT_X86_64</constant> <constant>SD_GPT_USR_ALPHA</constant> <constant>SD_GPT_USR_ARC</constant> <constant>SD_GPT_USR_ARM</constant> <constant>SD_GPT_USR_IA64</constant> <constant>SD_GPT_USR_LOONGARCH64</constant> <constant>SD_GPT_USR_MIPS_LE</constant> <constant>SD_GPT_USR_MIPS64_LE</constant> <constant>SD_GPT_USR_PARISC</constant> <constant>SD_GPT_USR_PPC</constant> <constant>SD_GPT_USR_PPC64</constant> <constant>SD_GPT_USR_PPC64_LE</constant> <constant>SD_GPT_USR_RISCV32</constant> <constant>SD_GPT_USR_RISCV64</constant> <constant>SD_GPT_USR_S390</constant> <constant>SD_GPT_USR_S390X</constant> <constant>SD_GPT_USR_TILEGX</constant> <constant>SD_GPT_USR_X86</constant>
-            </entry>
-            <entry>root partitions for other architectures</entry>
+            <entry><constant>SD_GPT_ROOT_ALPHA</constant> <constant>SD_GPT_ROOT_ARC</constant> <constant>SD_GPT_ROOT_ARM</constant> <constant>SD_GPT_ROOT_ARM64</constant> <constant>SD_GPT_ROOT_IA64</constant> <constant>SD_GPT_ROOT_LOONGARCH64</constant> <constant>SD_GPT_ROOT_MIPS</constant> <constant>SD_GPT_ROOT_MIPS64</constant> <constant>SD_GPT_ROOT_MIPS_LE</constant> <constant>SD_GPT_ROOT_MIPS64_LE</constant> <constant>SD_GPT_ROOT_PARISC</constant> <constant>SD_GPT_ROOT_PPC</constant> <constant>SD_GPT_ROOT_PPC64</constant> <constant>SD_GPT_ROOT_PPC64_LE</constant> <constant>SD_GPT_ROOT_RISCV32</constant> <constant>SD_GPT_ROOT_RISCV64</constant> <constant>SD_GPT_ROOT_S390</constant> <constant>SD_GPT_ROOT_S390X</constant> <constant>SD_GPT_ROOT_TILEGX</constant> <constant>SD_GPT_ROOT_X86</constant> <constant>SD_GPT_ROOT_X86_64</constant> <constant>SD_GPT_USR_ALPHA</constant> <constant>SD_GPT_USR_ARC</constant> <constant>SD_GPT_USR_ARM</constant> <constant>SD_GPT_USR_IA64</constant> <constant>SD_GPT_USR_LOONGARCH64</constant> <constant>SD_GPT_USR_MIPS_LE</constant> <constant>SD_GPT_USR_MIPS64_LE</constant> <constant>SD_GPT_USR_PARISC</constant> <constant>SD_GPT_USR_PPC</constant> <constant>SD_GPT_USR_PPC64</constant> <constant>SD_GPT_USR_PPC64_LE</constant> <constant>SD_GPT_USR_RISCV32</constant> <constant>SD_GPT_USR_RISCV64</constant> <constant>SD_GPT_USR_S390</constant> <constant>SD_GPT_USR_S390X</constant> <constant>SD_GPT_USR_TILEGX</constant> <constant>SD_GPT_USR_X86</constant></entry>
+            <entry>…</entry>
+            <entry>Root partitions for other architectures</entry>
             <entry><filename>/</filename></entry>
-            <entry>The first partition with the type UUID matching the architecture, located on the same disk as the ESP, is used as the root file system <filename>/</filename>. For the full list and constant values, see <ulink url="https://uapi-group.org/specifications/specs/discoverable_partitions_specification">Discoverable Partitions Specification</ulink>.</entry>
+            <entry>The first partition with the type UUID matching the architecture, located on the same disk as the ESP used for booting, is used as the root file system <filename>/</filename>. For the full list and constant values, see <ulink url="https://uapi-group.org/specifications/specs/discoverable_partitions_specification">Discoverable Partitions Specification</ulink>.</entry>
           </row>
           <row>
-            <entry><constant>SD_GPT_HOME</constant> <constant>933ac7e1-2eb4-4f13-b844-0e14e2aef915</constant></entry>
+            <entry><constant>SD_GPT_HOME</constant></entry>
+            <entry><constant>933ac7e1-2eb4-4f13-b844-0e14e2aef915</constant></entry>
             <entry>Home Partition</entry>
             <entry><filename>/home/</filename></entry>
-            <entry>The first partition with this type UUID on the same disk as the ESP is mounted to <filename>/home/</filename>.</entry>
+            <entry>The first partition with this type UUID on the same disk as the root partition is mounted to <filename>/home/</filename>.</entry>
           </row>
           <row>
-            <entry><constant>SD_GPT_SRV</constant> <constant>3b8f8425-20e0-4f3b-907f-1a25a76f98e8</constant></entry>
+            <entry><constant>SD_GPT_SRV</constant></entry>
+            <entry><constant>3b8f8425-20e0-4f3b-907f-1a25a76f98e8</constant></entry>
             <entry>Server Data Partition</entry>
             <entry><filename>/srv/</filename></entry>
-            <entry>The first partition with this type UUID on the same disk as the ESP is mounted to <filename>/srv/</filename>.</entry>
+            <entry>The first partition with this type UUID on the same disk as the root partition is mounted to <filename>/srv/</filename>.</entry>
           </row>
           <row>
-            <entry><constant>SD_GPT_VAR</constant> <constant>4d21b016-b534-45c2-a9fb-5c16e091fd2d</constant></entry>
+            <entry><constant>SD_GPT_VAR</constant></entry>
+            <entry><constant>4d21b016-b534-45c2-a9fb-5c16e091fd2d</constant></entry>
             <entry>Variable Data Partition</entry>
             <entry><filename>/var/</filename></entry>
-            <entry>The first partition with this type UUID on the same disk as the ESP is mounted to <filename>/var/</filename> — under the condition its partition UUID matches the first 128 bit of the HMAC-SHA256 of the GPT type uuid of this partition keyed by the machine ID of the installation stored in <citerefentry><refentrytitle>machine-id</refentrytitle><manvolnum>5</manvolnum></citerefentry>.</entry>
+            <entry>The first partition with this type UUID on the same disk as the root partition is mounted to <filename>/var/</filename> — under the condition its partition UUID matches the first 128 bit of the HMAC-SHA256 of the GPT type uuid of this partition keyed by the machine ID of the installation stored in <citerefentry><refentrytitle>machine-id</refentrytitle><manvolnum>5</manvolnum></citerefentry>.</entry>
           </row>
           <row>
-            <entry><constant>SD_GPT_TMP</constant> <constant>7ec6f557-3bc5-4aca-b293-16ef5df639d1</constant></entry>
+            <entry><constant>SD_GPT_TMP</constant></entry>
+            <entry><constant>7ec6f557-3bc5-4aca-b293-16ef5df639d1</constant></entry>
             <entry>Temporary Data Partition</entry>
             <entry><filename>/var/tmp/</filename></entry>
-            <entry>The first partition with this type UUID on the same disk as the ESP is mounted to <filename>/var/tmp/</filename>.</entry>
+            <entry>The first partition with this type UUID on the same disk as the root partition is mounted to <filename>/var/tmp/</filename>.</entry>
           </row>
           <row>
-            <entry><constant>SD_GPT_SWAP</constant> <constant>0657fd6d-a4ab-43c4-84e5-0933c84b4f4f</constant></entry>
+            <entry><constant>SD_GPT_SWAP</constant></entry>
+            <entry><constant>0657fd6d-a4ab-43c4-84e5-0933c84b4f4f</constant></entry>
             <entry>Swap</entry>
             <entry>n/a</entry>
-            <entry>All partitions with this type UUID on the same disk as the ESP are used as swap.</entry>
+            <entry>All partitions with this type UUID on the same disk as the root partition are used as swap.</entry>
           </row>
           <row>
-            <entry><constant>SD_GPT_ESP</constant> <constant>c12a7328-f81f-11d2-ba4b-00a0c93ec93b</constant></entry>
+            <entry><constant>SD_GPT_ESP</constant></entry>
+            <entry><constant>c12a7328-f81f-11d2-ba4b-00a0c93ec93b</constant></entry>
             <entry>EFI System Partition (ESP)</entry>
             <entry><filename>/efi/</filename> or <filename>/boot/</filename></entry>
             <entry>The first partition with this type UUID located on the same disk as the root partition is mounted to <filename>/boot/</filename> or <filename>/efi/</filename>, see below.</entry>
           </row>
           <row>
-            <entry><constant>SD_GPT_XBOOTLDR</constant> <constant>bc13c2ff-59e6-4262-a352-b275fd6f7172</constant></entry>
+            <entry><constant>SD_GPT_XBOOTLDR</constant></entry>
+            <entry><constant>bc13c2ff-59e6-4262-a352-b275fd6f7172</constant></entry>
             <entry>Extended Boot Loader Partition</entry>
             <entry><filename>/boot/</filename></entry>
             <entry>The first partition with this type UUID located on the same disk as the root partition is mounted to <filename>/boot/</filename>, see below.</entry>
 
     <table>
       <title>Partition Attribute Flags</title>
-      <tgroup cols='3' align='left' colsep='1' rowsep='1'>
+      <tgroup cols='4' align='left' colsep='1' rowsep='1'>
         <colspec colname="flag" />
+        <colspec colname="value" />
         <colspec colname="where" />
         <colspec colname="explanation" />
         <thead>
           <row>
             <entry>Flag</entry>
+            <entry>Value</entry>
             <entry>Applicable to</entry>
             <entry>Explanation</entry>
           </row>
         </thead>
         <tbody>
           <row>
-            <entry><constant>SD_GPT_FLAG_READ_ONLY</constant> <constant>0x1000000000000000</constant></entry>
+            <entry><constant>SD_GPT_FLAG_READ_ONLY</constant></entry>
+            <entry><constant>0x1000000000000000</constant></entry>
             <entry><filename>/</filename>, <filename>/home/</filename>, <filename>/srv/</filename>, <filename>/var/</filename>, <filename>/var/tmp/</filename>, Extended Boot Loader Partition</entry>
             <entry>Partition is mounted read-only</entry>
           </row>
 
           <row>
-            <entry><constant>SD_GPT_FLAG_NO_AUTO</constant> <constant>0x8000000000000000</constant></entry>
+            <entry><constant>SD_GPT_FLAG_NO_AUTO</constant></entry>
+            <entry><constant>0x8000000000000000</constant></entry>
             <entry><filename>/</filename>, <filename>/home/</filename>, <filename>/srv/</filename>, <filename>/var/</filename>, <filename>/var/tmp/</filename>, Extended Boot Loader Partition</entry>
             <entry>Partition is not mounted automatically</entry>
           </row>
 
           <row>
-            <entry><constant>SD_GPT_FLAG_NO_BLOCK_IO_PROTOCOL</constant> <constant>0x0000000000000002</constant></entry>
+            <entry><constant>SD_GPT_FLAG_NO_BLOCK_IO_PROTOCOL</constant></entry>
+            <entry><constant>0x0000000000000002</constant></entry>
             <entry>EFI System Partition (ESP)</entry>
             <entry>Partition is not mounted automatically</entry>
           </row>
index 1707e0c7f3b3449cf9862c880e056151cd2653bf..8ea667426edb8a982e5153a7d96319b405355661 100644 (file)
@@ -75,9 +75,9 @@
         <listitem><para>Pre-calculate the expected values seen in PCR register 11 after boot-up of a unified
         kernel image consisting of the components specified with <option>--linux=</option>,
         <option>--osrel=</option>, <option>--cmdline=</option>, <option>--initrd=</option>,
-        <option>--splash=</option>, <option>--dtb=</option>, <option>--uname=</option>,
-        <option>--sbat=</option>, <option>--pcrpkey=</option> see below. Only <option>--linux=</option> is
-        mandatory. (Alternatively, specify <option>--current</option> to use the current values of PCR
+        <option>--ucode=</option>, <option>--splash=</option>, <option>--dtb=</option>,
+        <option>--uname=</option>, <option>--sbat=</option>, <option>--pcrpkey=</option> see below.
+        Only <option>--linux=</option> is mandatory. (Alternatively, specify <option>--current</option> to use the current values of PCR
         register 11 instead.)</para>
 
         <xi:include href="version-info.xml" xpointer="v252"/>
         <term><option>--osrel=<replaceable>PATH</replaceable></option></term>
         <term><option>--cmdline=<replaceable>PATH</replaceable></option></term>
         <term><option>--initrd=<replaceable>PATH</replaceable></option></term>
+        <term><option>--ucode=<replaceable>PATH</replaceable></option></term>
         <term><option>--splash=<replaceable>PATH</replaceable></option></term>
         <term><option>--dtb=<replaceable>PATH</replaceable></option></term>
         <term><option>--uname=<replaceable>PATH</replaceable></option></term>
index 2c674a34b4c0a94c4d8fe54853e33c5d794a510c..e2e861b246796e58c6a5d92a68bad3a9726531c2 100644 (file)
       <varlistentry>
         <term><option>--recovery-pin=</option></term>
 
-        <listitem><para>Takes a boolean. Defaults to false. Honoured by <command>make-policy</command>. If
-        true, will query the user for a PIN to unlock the TPM2 NV index with. If no policy was created before
-        this PIN is used to protect the newly allocated NV index. If a policy has been created before the PIN
-        is used to unlock write access to the NV index. If this option is not used a PIN is automatically
-        generated. Regardless if user supplied or automatically generated, it is stored in encrypted form in
-        the policy metadata file. The recovery PIN may be used to regain write access to an NV index in case
-        the access policy became out of date.</para>
+        <listitem><para>Takes one of <literal>hide</literal>, <literal>show</literal> or
+        <literal>query</literal>. Defaults to <literal>hide</literal>. Honoured by
+        <command>make-policy</command>. If <literal>query</literal>, will query the user for a PIN to unlock
+        the TPM2 NV index with. If no policy was created before, this PIN is used to protect the newly
+        allocated NV index. If a policy has been created before, the PIN is used to unlock write access to
+        the NV index. If either <literal>hide</literal> or <literal>show</literal> is used, a PIN is
+        automatically generated, and — only in case of <literal>show</literal> — displayed on
+        screen. Regardless if user supplied or automatically generated, it is stored in encrypted form in the
+        policy metadata file. The recovery PIN may be used to regain write access to an NV index in case the
+        access policy became out of date.</para>
 
         <xi:include href="version-info.xml" xpointer="v255"/></listitem>
       </varlistentry>
index 756654854ee92f7737ca0d7c9ab1e8faf8a23133..e7e8a42a6d336bfae02ca99326e34c568fd27168 100644 (file)
@@ -70,6 +70,9 @@
 
       <listitem><para>An <literal>.initrd</literal> section with the initrd.</para></listitem>
 
+      <listitem><para>A <literal>.ucode</literal> section with an initrd containing microcode, to be handed
+      to the kernel before any other initrd. This initrd must not be compressed.</para></listitem>
+
       <listitem><para>A <literal>.splash</literal> section with an image (in the Windows
       <filename>.BMP</filename> format) to show on screen before invoking the kernel.</para></listitem>
 
       configuration.</para>
 
       <para>In case Secure Boot is enabled, these files will be validated using keys in UEFI DB, Shim's DB or
-      Shim's MOK, and will be rejected otherwise. Additionally, if the both the addon and the UKI contain a a
+      Shim's MOK, and will be rejected otherwise. Additionally, if both the addon and the UKI contain a
       <literal>.uname</literal> section, the addon will be rejected if they do not match exactly. It is
       recommended to always add a <literal>.sbat</literal> section to all signed addons, so that they may be
       revoked with a SBAT policy update, without requiring blocklisting via DBX/MOKX. The
     core kernel, the embedded initrd and kernel command line (see above for a full list).</para>
 
     <para>Also note that the Linux kernel will measure all initrds it receives into TPM PCR 9. This means
-    every type of initrd will be measured two or three times: the initrd embedded in the kernel image will be
+    every type of initrd will be measured two or three times: the initrds embedded in the kernel image will be
     measured to PCR 4, PCR 9 and PCR 11; the initrd synthesized from credentials (and the one synthesized
     from configuration extensions) will be measured to both PCR 9 and PCR 12; the initrd synthesized from
     system extensions will be measured to both PCR 4 and PCR 9. Let's summarize the OS resources and the PCRs
             <entry>4 + 9 + 11</entry>
           </row>
 
+          <row>
+            <entry>Microcode initrd (embedded in unified PE binary)</entry>
+            <entry>4 + 9 + 11</entry>
+          </row>
+
           <row>
             <entry>Default kernel command line (embedded in unified PE binary)</entry>
             <entry>4 + 11</entry>
index f9649b2f30a829ef1278308f0dbc0c2f9e56e6ab..8f196dd3525f720351c2c9918b0bc9bce94099ab 100644 (file)
@@ -1,6 +1,9 @@
 <?xml version='1.0'?>
 <!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
-  "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd">
+  "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [
+<!ENTITY % entities SYSTEM "custom-entities.ent" >
+%entities;
+]>
 <!-- SPDX-License-Identifier: LGPL-2.1-or-later -->
 
 <refentry id="systemd-vmspawn" conditional="ENABLE_VMSPAWN"
 
         <listitem><para>Directory to use as file system root for the virtual machine.</para>
 
-        <para>One of either <option>--directory=</option> or <option>--image=</option> must be specified.</para>
+        <para>One of either <option>--directory=</option> or <option>--image=</option> must be specified.
+        If neither are specified <option>--directory=.</option> is assumed.</para>
 
         <para>Note: If mounting a non-root owned directory you may require <option>--private-users=</option>
-              to map into the user's subuid namespace.</para>
+        to map into the user's subuid namespace. An example of how to use <constant>/etc/subuid</constant>
+        for this is given later.</para>
 
         <xi:include href="version-info.xml" xpointer="v256"/>
         </listitem>
         <varlistentry>
           <term><option>--cpus=<replaceable>CPUS</replaceable></option></term>
 
-          <listitem><para>Configures the number of CPUs to start the virtual machine with.
+          <listitem><para>The number of CPUs to start the virtual machine with.
           Defaults to 1.</para>
 
           <xi:include href="version-info.xml" xpointer="v255"/>
         <varlistentry>
           <term><option>--ram=<replaceable>BYTES</replaceable></option></term>
 
-          <listitem><para>Configures the amount of memory to start the virtual machine with.
+          <listitem><para>The amount of memory to start the virtual machine with.
           Defaults to 2G.</para>
 
           <xi:include href="version-info.xml" xpointer="v255"/>
         <varlistentry>
           <term><option>--kvm=<replaceable>BOOL</replaceable></option></term>
 
-          <listitem><para>Configures whether to use KVM. If the option is not specified KVM support will be
+          <listitem><para>If <option>--kvm=</option> is not specified KVM support will be
           detected automatically. If true, KVM is always used, and if false, KVM is never used.</para>
 
           <xi:include href="version-info.xml" xpointer="v255"/></listitem>
         <varlistentry>
           <term><option>--vsock=<replaceable>BOOL</replaceable></option></term>
 
-          <listitem>
-            <para>Configure whether to use VSOCK networking.</para>
+          <listitem><para>If <option>--vsock=</option> is not specified VSOCK networking support will be
+          detected automatically. If true, VSOCK networking is always used, and if false, VSOCK networking is never used.</para>
 
-            <para>If the option is not specified VSOCK support will be detected automatically.  If yes is
-            specified VSOCK is always used, and vice versa if no is set VSOCK are never used.</para>
-            <xi:include href="version-info.xml" xpointer="v255"/>
-          </listitem>
+          <xi:include href="version-info.xml" xpointer="v255"/></listitem>
         </varlistentry>
 
         <varlistentry>
           <term><option>--vsock-cid=<replaceable>CID</replaceable></option></term>
 
           <listitem>
-            <para>Configure vmspawn to use a specific CID for the guest.</para>
-
-            <para>If the option is not specified or an empty argument is supplied the guest will be assigned a random CID.</para>
-
-            <para>Valid CIDs are in the range <constant>3</constant> to <constant>4294967294</constant> (<constant>0xFFFF_FFFE</constant>).
-            CIDs outside of this range are reserved.</para>
+            <para>Sets the specific CID to use for the guest.
+            Valid CIDs are in the range <constant>3</constant> to <constant>4294967294</constant> (<constant>0xFFFF_FFFE</constant>).
+            CIDs outside of this range are reserved. By default vmspawn will attempt to derive a CID for the guest derived from the machine name,
+            falling back to a random CID if this CID is taken.</para>
 
             <xi:include href="version-info.xml" xpointer="v255"/>
           </listitem>
           <term><option>--tpm=<replaceable>BOOL</replaceable></option></term>
 
           <listitem>
-            <para>Configure whether to use VM with a virtual TPM or not.</para>
-
-            <para>If the option is not specified vmspawn will detect the presence of <citerefentry project='debian'>
+            <para>If <option>--tpm=</option> is not specified vmspawn will detect the presence of <citerefentry project='debian'>
             <refentrytitle>swtpm</refentrytitle><manvolnum>8</manvolnum></citerefentry> and use it if available.
             If yes is specified <citerefentry project='debian'><refentrytitle>swtpm</refentrytitle><manvolnum>8</manvolnum></citerefentry>
-            is always used, and vice versa if no is set <citerefentry project='debian'><refentrytitle>swtpm</refentrytitle>
+            is always used, and if no is set <citerefentry project='debian'><refentrytitle>swtpm</refentrytitle>
             <manvolnum>8</manvolnum></citerefentry> is never used.</para>
 
             <para>Note: the virtual TPM used may change in future.</para>
           <term><option>--linux=<replaceable>PATH</replaceable></option></term>
 
           <listitem>
-            <para>Set the linux kernel image to use for direct kernel boot.</para>
-
-            <para>If no kernel was installed into the image then the image will fail to boot.</para>
+            <para>Set the linux kernel image to use for direct kernel boot.
+            If a directory type image is used and <option>--linux=</option> was omitted, vmspawn will search for boot loader entries
+            according to the
+            <ulink url="https://uapi-group.org/specifications/specs/boot_loader_specification">Boot Loader Specification</ulink> assuming
+            XBOOTLDR to be located at /boot and ESP to be /efi respectively.
+            If no kernel was installed into the image then the image will fail to boot.</para>
 
             <xi:include href="version-info.xml" xpointer="v256"/>
           </listitem>
           <term><option>--initrd=<replaceable>PATH</replaceable></option></term>
 
           <listitem>
-            <para>Set the initrd to use for direct kernel boot.</para>
+            <para>Set the initrd to use for direct kernel boot.
+            If the <option>--linux=</option> supplied is a
+            <ulink url="https://uapi-group.org/specifications/specs/boot_loader_specification">Boot Loader Specification</ulink>
+            Type #2 entry, then this argument is not required.
+            If no initrd was installed into the image then the image will fail to boot.</para>
 
-            <para>If the linux kernel supplied is a UKI then this argument is not required.</para>
-
-            <para>If the option is specified multiple times vmspawn will merge the initrds together.</para>
-
-            <para>If no initrd was installed into the image then the image will fail to boot.</para>
+            <para><option>--initrd=</option> can be specified multiple times and vmspawn will merge them together.</para>
 
             <xi:include href="version-info.xml" xpointer="v256"/>
           </listitem>
           <term><option>--private-users=<replaceable>UID_SHIFT[:UID_RANGE]</replaceable></option></term>
 
           <listitem><para>Controls user namespacing under <option>--directory=</option>.
-          If enabled, <command>virtiofsd</command> is instructed to map user and group ids (UIDs and GIDs).
+          If enabled,
+          <citerefentry><refentrytitle>virtiofsd</refentrytitle><manvolnum>1</manvolnum></citerefentry>
+          is instructed to map user and group ids (UIDs and GIDs).
           This involves mapping the private UIDs/GIDs used in the virtual machine (starting with the virtual machine's
           root user 0 and up) to a range of UIDs/GIDs on the host that are not used for other purposes (usually in the
           range beyond the host's UID/GID 65536).</para>
           for more information.</para>
 
           <para>By default <literal>ed25519</literal> keys are generated, however <literal>rsa</literal> keys
-          may also be useful if the VM has a particularly old version of <command>sshd</command></para>.
+          may also be useful if the VM has a particularly old version of <command>sshd</command>.</para>
 
           <xi:include href="version-info.xml" xpointer="v256"/>
           </listitem>
@@ -507,6 +510,53 @@ $ mkosi -d arch -p systemd -p linux --autologin -o image.raw -f build
 $ systemd-vmspawn --image=image.raw
       </programlisting>
     </example>
+
+    <example>
+      <title>Import and run a Fedora 39 Cloud image using machinectl</title>
+
+      <programlisting>
+$ curl -L \
+       -O https://download.fedoraproject.org/pub/fedora/linux/releases/&fedora_latest_version;/Cloud/x86_64/images/Fedora-Cloud-Base-&fedora_latest_version;-&fedora_cloud_release;.x86_64.raw.xz \
+       -O https://download.fedoraproject.org/pub/fedora/linux/releases/&fedora_latest_version;/Cloud/x86_64/images/Fedora-Cloud-&fedora_latest_version;-&fedora_cloud_release;-x86_64-CHECKSUM \
+       -O https://fedoraproject.org/fedora.gpg
+$ gpgv --keyring ./fedora.gpg Fedora-Cloud-&fedora_latest_version;-&fedora_cloud_release;-x86_64-CHECKSUM
+$ sha256sum -c Fedora-Cloud-&fedora_latest_version;-&fedora_cloud_release;-x86_64-CHECKSUM
+# machinectl import-raw Fedora-Cloud-Base-&fedora_latest_version;-&fedora_cloud_release;.x86_64.raw.xz fedora-&fedora_latest_version;-cloud
+# systemd-vmspawn -M fedora-&fedora_latest_version;-cloud
+      </programlisting>
+    </example>
+
+    <example>
+      <title>Build and run systemd's system image and forward the VM's journal to a local file</title>
+
+      <programlisting>
+$ mkosi build
+$ systemd-vmspawn \
+    -D mkosi.output/system \
+    --private-users $(grep $(whoami) /etc/subuid | cut -d: -f2) \
+    --linux mkosi.output/system.efi \
+    --forward-journal=vm.journal \
+    enforcing=0
+      </programlisting>
+
+      <para>Note: this example also uses a kernel command line argument to ensure SELinux isn't started in enforcing mode.</para>
+    </example>
+
+    <example>
+      <title>SSH into a running VM using <command>systemd-ssh-proxy</command></title>
+
+      <programlisting>
+$ mkosi build
+$ my_vsock_cid=3735928559
+$ systemd-vmspawn \
+    -D mkosi.output/system \
+    --private-users $(grep $(whoami) /etc/subuid | cut -d: -f2) \
+    --linux mkosi.output/system.efi \
+    --vsock-cid $my_vsock_cid \
+    enforcing=0
+$ ssh root@vsock/$my_vsock_cid -i /run/user/$UID/systemd/vmspawn/machine-*-system-ed25519
+      </programlisting>
+    </example>
   </refsect1>
 
   <refsect1>
@@ -524,6 +574,7 @@ $ systemd-vmspawn --image=image.raw
       <member><citerefentry project='debian'><refentrytitle>mkosi</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
       <member><citerefentry><refentrytitle>machinectl</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
       <member><citerefentry><refentrytitle>importctl</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
+      <member><ulink url="https://uapi-group.org/specifications/specs/boot_loader_specification">Boot Loader Specification</ulink></member>
     </simplelist></para>
   </refsect1>
 </refentry>
index 38ab30fb4d4a41f21bd23d7b83eb9cac8d87c91e..bc3174b3916e62447e1cdcaf8da8890597259317 100644 (file)
@@ -747,6 +747,17 @@ Table=1234</programlisting></para>
         </listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><varname>UseDomains=</varname></term>
+        <listitem>
+          <para>Specifies the protocol-independent default value for the same settings in
+          [IPv6AcceptRA], [DHCPv4], and [DHCPv6] sections below. Takes a boolean, or the special value
+          <option>route</option>. See also the same setting in [DHCPv4] below. Defaults to unset.</para>
+
+          <xi:include href="version-info.xml" xpointer="v256"/>
+        </listitem>
+      </varlistentry>
+
       <varlistentry>
         <term><varname>Domains=</varname></term>
         <listitem>
@@ -888,12 +899,13 @@ Table=1234</programlisting></para>
       <varlistentry>
         <term><varname>IPv6AcceptRA=</varname></term>
         <listitem>
-          <para>Takes a boolean. Controls IPv6 Router Advertisement (RA) reception support for the
-          interface. If true, RAs are accepted; if false, RAs are ignored. When RAs are accepted, they
-          may trigger the start of the DHCPv6 client if the relevant flags are set in the RA data, or
-          if no routers are found on the link. The default is to disable RA reception for bridge
-          devices or when IP forwarding is enabled, and to enable it otherwise. Cannot be enabled on
-          devices aggregated in a bond device or when link-local addressing is disabled.</para>
+          <para>Takes a boolean. Controls IPv6 Router Advertisement (RA) reception support for the interface.
+          If true, RAs are accepted; if false, RAs are ignored. When RAs are accepted, they may trigger the
+          start of the DHCPv6 client if the relevant flags are set in the RA data, or if no routers are found
+          on the link. Defaults to false for bridge devices, when IP forwarding is enabled,
+          <varname>IPv6SendRA=</varname> or <varname>KeepMaster=</varname> is enabled. Otherwise, enabled by
+          default. Cannot be enabled on devices aggregated in a bond device or when link-local addressing is
+          disabled.</para>
 
           <para>Further settings for the IPv6 RA support may be configured in the [IPv6AcceptRA]
           section, see below.</para>
@@ -2570,9 +2582,15 @@ NFTSet=prefix:netdev:filter:eth_ipv4_prefix</programlisting>
           effect of the <option>Domains=</option> setting. If set to <option>route</option>, the domain name
           received from the DHCP server will be used for routing DNS queries only, but not for searching,
           similarly to the effect of the <option>Domains=</option> setting when the argument is prefixed with
-          <literal>~</literal>. When unspecified, the value specified in the same setting in
-          <citerefentry><refentrytitle>networkd.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
-          which defaults to <literal>no</literal>, will be used.</para>
+          <literal>~</literal>.</para>
+
+          <para>When unspecified, the value specified in the same setting in the [Network] section will be
+          used. When it is unspecified, the value specified in the same setting in the [DHCPv4] section in
+          <citerefentry><refentrytitle>networkd.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+          will be used. When it is unspecified, the value specified in the same setting in the [Network]
+          section in
+          <citerefentry><refentrytitle>networkd.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+          will be used. When none of them are specified, defaults to <literal>no</literal>.</para>
 
           <para>It is recommended to enable this option only on trusted networks, as setting this
           affects resolution of all hostnames, in particular of single-label names. It is generally
index a81cbcb47b5df358bb1b0b2e8eb41fe7ac445c7d..a9034bc51114a5581fbb3102327a18522ff26bea 100644 (file)
@@ -67,6 +67,7 @@
 
       <para>Additional sections will be inserted into the UKI, either automatically or only if a specific
       option is provided. See the discussions of
+      <varname>Microcode=</varname>/<option>--microcode=</option>,
       <varname>Cmdline=</varname>/<option>--cmdline=</option>,
       <varname>OSRelease=</varname>/<option>--os-release=</option>,
       <varname>DeviceTree=</varname>/<option>--devicetree=</option>,
           <xi:include href="version-info.xml" xpointer="v254"/></listitem>
         </varlistentry>
 
+        <varlistentry>
+          <term><varname>Microcode=<replaceable>UCODE</replaceable></varname></term>
+          <term><option>--microcode=<replaceable>UCODE</replaceable></option></term>
+
+          <listitem><para>Path to initrd containing microcode updates. If not specified, the section
+          will not be present.</para>
+
+          <xi:include href="version-info.xml" xpointer="v256"/></listitem>
+        </varlistentry>
+
         <varlistentry>
           <term><varname>Cmdline=<replaceable>TEXT</replaceable>|<replaceable>@PATH</replaceable></varname></term>
           <term><option>--cmdline=<replaceable>TEXT</replaceable>|<replaceable>@PATH</replaceable></option></term>
index 1a160960cc6f4d2c5d1cb00908fc0bbd7a7bcc0f..e2de14809521a4f6db30ce3b5669d6e0b1754f51 100644 (file)
@@ -2573,6 +2573,18 @@ endif
 
 #####################################################################
 
+if get_option('integration-tests') != false
+        system_mkosi = custom_target('system_mkosi',
+                build_always_stale : true,
+                output : 'system',
+                console : true,
+                command : ['mkosi', '-C', meson.project_source_root(), '--image=system', '--format=disk', '--output-dir', meson.project_build_root() / '@OUTPUT@', '--without-tests', '-fi', 'build'],
+                depends : [executables_by_name['bootctl'], executables_by_name['systemd-measure'], executables_by_name['systemd-repart'], ukify],
+        )
+endif
+
+############################################################
+
 subdir('rules.d')
 subdir('test')
 
index 41a524b0dcb5fd2552d0e555fb93a6ee6eed07b4..d52ca4e4b5629c617eff89d98dcc2196cc4d58fb 100644 (file)
@@ -498,6 +498,8 @@ option('install-tests', type : 'boolean', value : false,
        description : 'install test executables')
 option('log-message-verification', type : 'feature', deprecated : { 'true' : 'enabled', 'false' : 'disabled' },
        description : 'do fake printf() calls to verify format strings')
+option('integration-tests', type : 'boolean', value : false,
+       description : 'run the integration tests')
 
 option('ok-color', type : 'combo',
        choices : ['black', 'red', 'green', 'yellow', 'blue', 'magenta', 'cyan',
index ee7fa6e2a48d1b65319bc8e8b7415b2ee5a6d30d..02f6a90b6f3010af903f18d3f1d6b168f8acc7df 100644 (file)
@@ -1,7 +1,7 @@
 # SPDX-License-Identifier: LGPL-2.1-or-later
 
 [Config]
-Images=system
+@Images=system
 MinimumVersion=23~devel
 
 [Output]
@@ -17,28 +17,30 @@ Environment=ASAN_OPTIONS=verify_asan_link_order=false
 @SELinuxRelabel=no
 BuildSourcesEphemeral=yes
 
+KernelCommandLine=systemd.crash_shell
+                  systemd.log_level=debug,console:info
+                  systemd.log_ratelimit_kmsg=0
+                  systemd.journald.forward_to_console
+                  systemd.journald.max_level_console=warning
+                  # Disable the kernel's ratelimiting on userspace logging to kmsg.
+                  printk.devkmsg=on
+                  # Make sure /sysroot is mounted rw in the initrd.
+                  rw
+                  # Lower the default device timeout so we get a shell earlier if the root device does
+                  # not appear for some reason.
+                  systemd.default_device_timeout_sec=20
+                  # Make sure no LSMs are enabled by default.
+                  apparmor=0
+                  selinux=0
+                  enforcing=0
+                  systemd.early_core_pattern=/core
+                  systemd.firstboot=no
+                  systemd.setenv=SYSTEMD_ENABLE_LOG_CONTEXT=yes
+                  SYSTEMD_ENABLE_LOG_CONTEXT=yes
+
 [Host]
 @Incremental=yes
 @RuntimeSize=8G
 @RuntimeBuildSources=yes
+@QemuSmp=2
 ToolsTreePackages=virtiofsd
-KernelCommandLineExtra=systemd.crash_shell
-                       systemd.log_level=debug,console:info
-                       systemd.log_ratelimit_kmsg=0
-                       systemd.journald.forward_to_console
-                       systemd.journald.max_level_console=warning
-                       # Disable the kernel's ratelimiting on userspace logging to kmsg.
-                       printk.devkmsg=on
-                       # Make sure /sysroot is mounted rw in the initrd.
-                       rw
-                       # Lower the default device timeout so we get a shell earlier if the root device does
-                       # not appear for some reason.
-                       systemd.default_device_timeout_sec=10
-                       # Make sure no LSMs are enabled by default.
-                       apparmor=0
-                       selinux=0
-                       enforcing=0
-                       systemd.early_core_pattern=/core
-                       systemd.firstboot=no
-                       systemd.setenv=SYSTEMD_ENABLE_LOG_CONTEXT=yes
-                       SYSTEMD_ENABLE_LOG_CONTEXT=yes
index ed09d841b843bf4af049571d6a3b9dee24c35253..90f302e44d0e4375db7c63972d8b0800d87ad31c 100644 (file)
@@ -5,6 +5,9 @@
 
 [Content]
 Autologin=yes
+ExtraTrees=
+        %D/mkosi.crt:/usr/lib/verity.d/mkosi.crt # sysext verification key
+
 Packages=
         acl
         bash-completion
index 8eebd62b047badf0887d99aed42217ef8287aaa3..04eeaf1e37c406a7c7e2014d3a80adf047d6fad9 100755 (executable)
@@ -1,6 +1,6 @@
 #!/bin/bash
 # SPDX-License-Identifier: LGPL-2.1-or-later
-set -ex
+set -e
 
 # shellcheck source=/dev/null
 . /usr/lib/os-release
@@ -25,7 +25,7 @@ mount --mkdir --rbind "$PWD/pkg/$ID" "pkg/$ID/src/"
 # on the makepkg command line so we append to /etc/makepkg.conf instead. The rootfs is overlaid with a
 # writable tmpfs during the build script so these changes don't end up in the image itself.
 tee --append /etc/makepkg.conf >/dev/null <<EOF
-CFLAGS="$CFLAGS -O0 -Wp,-D_FORTIFY_SOURCE=0"
+CFLAGS="$CFLAGS -O${OPTIMIZATION:-0} -Wp,-U_FORTIFY_SOURCE"
 OPTIONS=(
     docs
     !libtool
index 92d58292f4c7919a390484f720652de1d23825fd..cf5d4f3103f05aa7b1337e71b4090e95a8397dda 100644 (file)
@@ -6,10 +6,11 @@ Distribution=arch
 [Content]
 VolatilePackages=
         systemd
-        systemd-ukify
-        systemd-sysvcompat
+        systemd-libs
         systemd-resolvconf
+        systemd-sysvcompat
         systemd-tests
+        systemd-ukify
 
 Packages=
         bpf
@@ -25,6 +26,7 @@ Packages=
         git
         gnutls
         iproute
+        iputils
         linux
         man-db
         openbsd-netcat
@@ -33,6 +35,7 @@ Packages=
         pacman
         pkgconf
         polkit
+        procps-ng
         quota-tools
         sbsigntools
         shadow
@@ -45,4 +48,5 @@ InitrdPackages=
 
 InitrdVolatilePackages=
         systemd
+        systemd-libs
         systemd-sysvcompat
diff --git a/mkosi.images/system/mkosi.conf.d/10-arch/mkosi.conf.d/10-debug.conf b/mkosi.images/system/mkosi.conf.d/10-arch/mkosi.conf.d/10-debug.conf
new file mode 100644 (file)
index 0000000..4a6d2e9
--- /dev/null
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+[Match]
+Environment=WITH_DEBUG=1
+
+[Content]
+VolatilePackages=systemd-debug
index 0cba71bf7a7231ac60c5f6b654a8015f7825385e..79770c0d4f85b40ad6488396adcd7c1bec9a5466 100755 (executable)
@@ -31,17 +31,17 @@ DIST="$(rpm --eval %dist)"
 ARCH="$(rpm --eval %_arch)"
 SRCDEST="/usr/src/debug/systemd-$VERSION-${RELEASE}${DIST}.$ARCH"
 
-# TODO: Drop -D_FORTIFY_SOURCE when we switch to CentOS Stream 10.
-EXTRA_CFLAGS="-O0 -Wp,-D_FORTIFY_SOURCE=0"
+# TODO: Drop -U_FORTIFY_SOURCE when we switch to CentOS Stream 10.
+CFLAGS="$(rpm --define "_fortify_level 0" --undefine _lto_cflags --eval %build_cflags) -O${OPTIMIZATION:-0} -Wp,-U_FORTIFY_SOURCE"
 if ((WITH_DEBUG)); then
-    EXTRA_CFLAGS="$EXTRA_CFLAGS -ffile-prefix-map=../src=$SRCDEST"
+    CFLAGS="$CFLAGS -ffile-prefix-map=../src=$SRCDEST"
 fi
 
 IFS=
 # TODO: Replace meson_build and meson_install overrides with "--undefine __meson_verbose" once
 # https://github.com/mesonbuild/meson/pull/12835 is available.
 # shellcheck disable=SC2046
-rpmbuild \
+ANNOBIN="no-active-checks" rpmbuild \
     -bb \
     --build-in-place \
     --with upstream \
@@ -58,7 +58,7 @@ rpmbuild \
     $( ((WITH_DEBUG)) || echo "debug_package %{nil}") \
     --define "version_override $VERSION" \
     --define "release_override $RELEASE" \
-    --define "build_cflags $(rpm --eval %build_cflags) $EXTRA_CFLAGS" \
+    --define "build_cflags $CFLAGS" \
     --define "meson_build %{shrink:%{__meson} compile -C %{_vpath_builddir} -j %{_smp_build_ncpus} %{nil}}" \
     --define "meson_install %{shrink:DESTDIR=%{buildroot} %{__meson} install -C %{_vpath_builddir} --no-rebuild --quiet %{nil}}" \
     --define "meson_extra_configure_options -D mode=developer -D b_sanitize=${SANITIZERS:-none}" \
@@ -74,6 +74,7 @@ rpmbuild \
     --define "_find_debuginfo_dwz_opts %{nil}" \
     --define "_fortify_level 0" \
     --undefine _lto_cflags \
+    --undefine _package_note_flags \
     --noclean \
     "pkg/$ID/systemd.spec"
 
index cc9f3e9d11ccba64d7ae0670d473b63c6704efc0..294e4cdf2897d4933ce0b60302f2c8a3735edeb5 100644 (file)
@@ -7,18 +7,18 @@ Distribution=|fedora
 [Content]
 VolatilePackages=
         systemd
-        systemd-udev
+        systemd-boot
         systemd-container
-        systemd-repart
-        systemd-resolved
+        systemd-devel
+        systemd-journal-remote
         systemd-networkd
-        systemd-boot
+        systemd-networkd-defaults
+        systemd-oomd-defaults
+        systemd-pam
+        systemd-resolved
         systemd-tests
+        systemd-udev
         systemd-ukify
-        systemd-pam
-        systemd-oomd-defaults
-        systemd-journal-remote
-        systemd-networkd-defaults
 
 Packages=
         bpftool
@@ -30,6 +30,7 @@ Packages=
         integritysetup
         iproute
         iproute-tc
+        iputils
         kernel-core
         libasan
         libcap-ng-utils
@@ -41,6 +42,7 @@ Packages=
         p11-kit
         pam
         passwd
+        policycoreutils
         polkit
         procps-ng
         quota
@@ -50,7 +52,6 @@ Packages=
         selinux-policy
         selinux-policy-targeted
         setools-console
-        policycoreutils
         util-linux
         vim-common
 
diff --git a/mkosi.images/system/mkosi.conf.d/10-centos-fedora/mkosi.conf.d/10-debug.conf b/mkosi.images/system/mkosi.conf.d/10-centos-fedora/mkosi.conf.d/10-debug.conf
new file mode 100644 (file)
index 0000000..0c3707b
--- /dev/null
@@ -0,0 +1,17 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+[Match]
+Environment=WITH_DEBUG=1
+
+[Content]
+VolatilePackages=
+        systemd-container-debuginfo
+        systemd-debuginfo
+        systemd-debugsource
+        systemd-journal-remote-debuginfo
+        systemd-libs-debuginfo
+        systemd-networkd-debuginfo
+        systemd-pam-debuginfo
+        systemd-resolved-debuginfo
+        systemd-tests-debuginfo
+        systemd-udev-debuginfo
diff --git a/mkosi.images/system/mkosi.conf.d/10-centos/mkosi.extra/usr/lib/sysusers.d/20-setup-groups.conf b/mkosi.images/system/mkosi.conf.d/10-centos/mkosi.extra/usr/lib/sysusers.d/20-setup-groups.conf
new file mode 100644 (file)
index 0000000..d69ff91
--- /dev/null
@@ -0,0 +1,24 @@
+g root 0
+g bin 1
+g daemon 2
+g sys 3
+g adm 4
+g tty 5
+g disk 6
+g lp 7
+g mem 8
+g kmem 9
+g wheel 10
+g cdrom 11
+g mail 12
+g man 15
+g dialout 18
+g floppy 19
+g games 20
+g tape 33
+g video 39
+g ftp 50
+g lock 54
+g audio 63
+g users 100
+g nobody 65534
diff --git a/mkosi.images/system/mkosi.conf.d/10-centos/mkosi.extra/usr/lib/sysusers.d/20-setup-users.conf b/mkosi.images/system/mkosi.conf.d/10-centos/mkosi.extra/usr/lib/sysusers.d/20-setup-users.conf
new file mode 100644 (file)
index 0000000..bea0ab3
--- /dev/null
@@ -0,0 +1,13 @@
+u root 0:0 "Super User" /root /bin/bash
+u bin 1:1 "bin" /bin -
+u daemon 2:2 "daemon" /sbin -
+u adm 3:4 "adm" /var/adm -
+u lp 4:7 "lp" /var/spool/lpd -
+u sync 5:0 "sync" /sbin /bin/sync
+u shutdown 6:0 "shutdown" /sbin /sbin/shutdown
+u halt 7:0 "halt" /sbin /sbin/halt
+u mail 8:12 "mail" /var/spool/mail -
+u operator 11:0 "operator" /root -
+u games 12:100 "games" /usr/games -
+u ftp 14:50 "FTP User" /var/ftp -
+u nobody 65534:65534 "Kernel Overflow User" - -
index a8775d05f14df6e2d0c56d1068daa2ac428f399d..061ed5b5a767154d51ea533be872f86d635a50b3 100755 (executable)
@@ -42,19 +42,20 @@ cat debian/changelog >>debian/changelog.new
 mv debian/changelog.new debian/changelog
 
 build() {
-    DEB_BUILD_OPTIONS="\
+    DEB_BUILD_OPTIONS=$(awk '$1=$1' <<<"\
         $( ((WITH_TESTS)) || echo nocheck) \
         $( ((WITH_DOCS)) || echo nodoc) \
         $( ((WITH_DEBUG)) || echo nostrip) \
         terse \
         optimize=-lto \
-        noopt \
-    " \
-    DEB_BUILD_PROFILES="\
+        hardening=-fortify \
+    ") \
+    DEB_BUILD_PROFILES=$(awk '$1=$1' <<<"\
         $( ((WITH_TESTS)) || echo nocheck) \
         $( ((WITH_DOCS)) || echo nodoc) \
         pkg.systemd.upstream \
-    " \
+    ") \
+    DEB_CFLAGS_APPEND="-O${OPTIMIZATION:-0}" \
     DPKG_FORCE="unsafe-io" \
     DPKG_DEB_COMPRESSOR_TYPE="none" \
     DH_MISSING="--fail-missing" \
index 7ece67705a9ad0bd03f7c352bb71ac33e71e0ce5..ce80e5513f649907e2791561282d64c8e4f371b2 100644 (file)
@@ -6,19 +6,28 @@ Distribution=|ubuntu
 
 [Content]
 VolatilePackages=
+        libnss-myhostname
+        libnss-mymachines
+        libnss-resolve
+        libnss-systemd
+        libpam-systemd
+        libsystemd-dev
+        libudev-dev
         systemd
-        systemd-userdbd
+        systemd-boot
+        systemd-boot-efi
+        systemd-container
+        systemd-coredump
+        systemd-dev
+        systemd-homed
+        systemd-journal-remote
         systemd-oomd
+        systemd-resolved
         systemd-sysv
         systemd-tests
         systemd-timesyncd
-        systemd-resolved
-        systemd-homed
-        systemd-coredump
-        systemd-journal-remote
-        systemd-container
-        systemd-boot
         systemd-ukify
+        systemd-userdbd
         udev
 
 Packages=
@@ -37,6 +46,7 @@ Packages=
         fdisk
         git-core
         iproute2
+        iputils-ping
         isc-dhcp-server
         libcap-ng-utils
         libtss2-rc0
diff --git a/mkosi.images/system/mkosi.conf.d/10-debian-ubuntu/mkosi.conf.d/10-debug.conf b/mkosi.images/system/mkosi.conf.d/10-debian-ubuntu/mkosi.conf.d/10-debug.conf
new file mode 100644 (file)
index 0000000..b53b3dc
--- /dev/null
@@ -0,0 +1,27 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+[Match]
+Environment=WITH_DEBUG=1
+
+[Content]
+VolatilePackages=
+        libnss-myhostname-dbgsym
+        libnss-mymachines-dbgsym
+        libnss-resolve-dbgsym
+        libnss-systemd-dbgsym
+        libpam-systemd-dbgsym
+        libsystemd-shared-dbgsym
+        libsystemd0-dbgsym
+        libudev1-dbgsym
+        systemd-boot-dbgsym
+        systemd-container-dbgsym
+        systemd-coredump-dbgsym
+        systemd-dbgsym
+        systemd-homed-dbgsym
+        systemd-journal-remote-dbgsym
+        systemd-oomd-dbgsym
+        systemd-resolved-dbgsym
+        systemd-tests-dbgsym
+        systemd-timesyncd-dbgsym
+        systemd-userdbd-dbgsym
+        udev-dbgsym
index fe89611e36ed90ac13da0fbe1ba0b32ad357d82f..6d097791644cd2a93a1fe84eff308eabcd078900 100755 (executable)
@@ -29,6 +29,20 @@ tee --append /usr/lib/rpm/suse/macros <<'EOF'
 %{nil}
 EOF
 
+VERSION="$(cat meson.version)"
+RELEASE="$(date "+%Y%m%d%H%M%S" --date "@$TS")"
+
+DIST="$(rpm --eval %dist)"
+ARCH="$(rpm --eval %_arch)"
+SRCDEST="/usr/src/debug/systemd-$VERSION-${RELEASE}${DIST}.$ARCH"
+
+# TODO: Enable this when the opensuse spec stops unconditionally setting FORTIFY_SOURCE=2.
+# EXTRA_CFLAGS="-O${OPTIMIZATION:-0} -Wp,-U_FORTIFY_SOURCE"
+EXTRA_CFLAGS=""
+if ((WITH_DEBUG)); then
+    EXTRA_CFLAGS="$EXTRA_CFLAGS -ffile-prefix-map=../src=$SRCDEST"
+fi
+
 build() {
     IFS=
     # TODO: Replace meson_build and meson_install overrides with "--undefine __meson_verbose" once
@@ -49,15 +63,17 @@ build() {
         $( ((WITH_DEBUG)) || echo --define) \
         $( ((WITH_DEBUG)) || echo "debug_package %{nil}") \
         --define "vendor openSUSE" \
-        --define "version_override $(cat meson.version)" \
-        --define "release_override $(date "+%Y%m%d%H%M%S" --date "@$TS")" \
+        --define "version_override $VERSION" \
+        --define "release_override $RELEASE" \
         --define "__check_files sh -c '$(rpm --eval %__check_files) | tee /tmp/unpackaged-files'" \
+        --define "build_cflags $(rpm --eval %build_cflags) $EXTRA_CFLAGS" \
         --define "meson_build %{shrink:%{__meson} compile -C %{_vpath_builddir} -j %{_smp_build_ncpus} %{nil}}" \
         --define "meson_install %{shrink:DESTDIR=%{buildroot} %{__meson} install -C %{_vpath_builddir} --no-rebuild --quiet %{nil}}" \
         --define "meson_extra_configure_options -D mode=developer -D b_sanitize=${SANITIZERS:-none}" \
         --define "__os_install_post /usr/lib/rpm/brp-suse %{nil}" \
         --define "__elf_exclude_path ^/usr/lib/systemd/tests/unit-tests/.*$" \
         --define "__script_requires %{nil}" \
+        --define "_find_debuginfo_dwz_opts %{nil}" \
         --noclean \
         "$@" \
         "pkg/$ID/systemd.spec"
index 24d69ccc277f46d8998d8331128f37dd484eae3f..5bcf7b8efd557bf055d7bba90db9ca1a8ed7213d 100644 (file)
@@ -9,15 +9,18 @@ InitrdInclude=initrd/
 [Content]
 VolatilePackages=
         systemd
-        udev
-        systemd-experimental
         systemd-boot
         systemd-container
+        systemd-devel
+        systemd-doc
+        systemd-experimental
         systemd-homed
+        systemd-lang
         systemd-network
         systemd-portable
         systemd-sysvcompat
         systemd-testsuite
+        udev
 
 # We install gawk, gzip, grep, xz, sed, rsync and docbook-xsl-stylesheets here explicitly so that the busybox
 # versions don't get installed instead.
@@ -33,7 +36,13 @@ Packages=
         git-core
         glibc-locale-base
         grep
+        group(bin)
+        group(daemon)
+        group(games)
+        group(nobody)
+        group(root)
         gzip
+        iputils
         kernel-kvmsmall
         kmod
         libasan8
@@ -43,6 +52,7 @@ Packages=
         openssh-server
         pam
         patterns-base-minimal_base
+        procps4
         python3-pefile
         quota
         rpm-build
@@ -51,6 +61,11 @@ Packages=
         sed
         shadow
         timezone
+        user(bin)
+        user(daemon)
+        user(games)
+        user(nobody)
+        user(root)
         vim
         xz
 
diff --git a/mkosi.images/system/mkosi.conf.d/10-opensuse/mkosi.conf.d/10-debug.conf b/mkosi.images/system/mkosi.conf.d/10-opensuse/mkosi.conf.d/10-debug.conf
new file mode 100644 (file)
index 0000000..2262eae
--- /dev/null
@@ -0,0 +1,22 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+[Match]
+Environment=WITH_DEBUG=1
+
+[Content]
+VolatilePackages=
+        libsystemd0-debuginfo
+        libudev1-debuginfo
+        systemd-boot-debuginfo
+        systemd-container-debuginfo
+        systemd-coredump-debuginfo
+        systemd-debuginfo
+        systemd-debugsource
+        systemd-experimental-debuginfo
+        systemd-homed-debuginfo
+        systemd-journal-remote-debuginfo
+        systemd-network-debuginfo
+        systemd-portable-debuginfo
+        systemd-sysvcompat-debuginfo
+        systemd-testsuite-debuginfo
+        udev-debuginfo
diff --git a/mkosi.images/system/mkosi.extra/usr/lib/systemd/system/user@.service.d/99-SYSTEMD_UNIT_PATH.conf b/mkosi.images/system/mkosi.extra/usr/lib/systemd/system/user@.service.d/99-SYSTEMD_UNIT_PATH.conf
new file mode 100644 (file)
index 0000000..fa63493
--- /dev/null
@@ -0,0 +1,2 @@
+[Service]
+PassEnvironment=SYSTEMD_UNIT_PATH
index d1052694aa711012d58f703c8fb16f3904300d4b..8a00ca9f6884106e2d7d8901fa607863e876c6e1 100755 (executable)
@@ -2,6 +2,10 @@
 # SPDX-License-Identifier: LGPL-2.1-or-later
 set -e
 
+# Make sure serial console line wrapping is re-enabled as qemu's seabios firmware disables serial console
+# line-wrapping on boot.
+echo "tput smam || :" >>/etc/profile
+
 if [ -n "$SANITIZERS" ]; then
     LD_PRELOAD=$(ldd /usr/lib/systemd/systemd | grep libasan.so | awk '{print $3}')
 
index ccc32ea10164a9b6ca3098765e63f653cddc6817..124b1da79088d0f0ab33dc2d840d4733074dbf64 160000 (submodule)
--- a/pkg/arch
+++ b/pkg/arch
@@ -1 +1 @@
-Subproject commit ccc32ea10164a9b6ca3098765e63f653cddc6817
+Subproject commit 124b1da79088d0f0ab33dc2d840d4733074dbf64
index d941484d986ed963eb717dc7455bb2c567574781..9a904d3947a634465ea297f0b3c5f29318e569b9 100644 (file)
--- a/po/id.po
+++ b/po/id.po
@@ -6,16 +6,16 @@ msgid ""
 msgstr ""
 "Report-Msgid-Bugs-To: \n"
 "POT-Creation-Date: 2024-03-04 10:09+0100\n"
-"PO-Revision-Date: 2024-01-21 10:36+0000\n"
+"PO-Revision-Date: 2024-04-20 07:36+0000\n"
 "Last-Translator: Andika Triwidada <andika@gmail.com>\n"
 "Language-Team: Indonesian <https://translate.fedoraproject.org/projects/"
-"systemd/master/id/>\n"
+"systemd/main/id/>\n"
 "Language: id\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
 "Plural-Forms: nplurals=1; plural=0;\n"
-"X-Generator: Weblate 5.3.1\n"
+"X-Generator: Weblate 5.4\n"
 
 #: src/core/org.freedesktop.systemd1.policy.in:22
 msgid "Send passphrase back to system"
@@ -132,23 +132,22 @@ msgstr ""
 
 #: src/home/org.freedesktop.home1.policy:73
 msgid "Inhibit automatic lock of a home area"
-msgstr ""
+msgstr "Cegah penguncian otomatis dari area home"
 
 #: src/home/org.freedesktop.home1.policy:74
-#, fuzzy
 msgid ""
 "Authentication is required to inhibit automatic lock of a user's home area."
-msgstr "Otentikasi diperlukan untuk memperbarui suatu area rumah pengguna."
+msgstr ""
+"Otentikasi diperlukan untuk mencegah penguncian otomatis dari area home "
+"pengguna."
 
 #: src/home/org.freedesktop.home1.policy:83
-#, fuzzy
 msgid "Activate a home area"
-msgstr "Menciptakan suatu area rumah"
+msgstr "Aktifkan suatu area home"
 
 #: src/home/org.freedesktop.home1.policy:84
-#, fuzzy
 msgid "Authentication is required to activate a user's home area."
-msgstr "Otentikasi diperlukan untuk mencipta suatu area rumah pengguna."
+msgstr "Otentikasi diperlukan untuk mengaktifkan suatu area home pengguna."
 
 #: src/home/pam_systemd_home.c:293
 #, c-format
@@ -370,46 +369,39 @@ msgid "Authentication is required to get system description."
 msgstr "Otentikasi diperlukan untuk mendapatkan deskripsi sistem."
 
 #: src/import/org.freedesktop.import1.policy:22
-#, fuzzy
 msgid "Import a disk image"
-msgstr "Impor sebuah image kontainer atau VM"
+msgstr "Impor sebuah image disk"
 
 #: src/import/org.freedesktop.import1.policy:23
-#, fuzzy
 msgid "Authentication is required to import an image"
-msgstr "Otentikasi diperlukan untuk mengimpor suatu image kontainer atau VM"
+msgstr "Otentikasi diperlukan untuk mengimpor suatu image"
 
 #: src/import/org.freedesktop.import1.policy:32
-#, fuzzy
 msgid "Export a disk image"
-msgstr "Ekspor sebuah image kontainer atau VM"
+msgstr "Ekspor sebuah image disk"
 
 #: src/import/org.freedesktop.import1.policy:33
-#, fuzzy
 msgid "Authentication is required to export disk image"
-msgstr "Otentikasi diperlukan untuk mengekspor suatu image kontainer atau VM"
+msgstr "Otentikasi diperlukan untuk mengekspor suatu image disk"
 
 #: src/import/org.freedesktop.import1.policy:42
-#, fuzzy
 msgid "Download a disk image"
-msgstr "Unduh sebuah image kontainer atau VM"
+msgstr "Unduh sebuah image disk"
 
 #: src/import/org.freedesktop.import1.policy:43
-#, fuzzy
 msgid "Authentication is required to download a disk image"
-msgstr "Otentikasi diperlukan untuk mengunduh suatu image kontainer atau VM"
+msgstr "Otentikasi diperlukan untuk mengunduh suatu image disk"
 
 #: src/import/org.freedesktop.import1.policy:52
 msgid "Cancel transfer of a disk image"
-msgstr ""
+msgstr "Batalkan transfer suatu image disk"
 
 #: src/import/org.freedesktop.import1.policy:53
-#, fuzzy
 msgid ""
 "Authentication is required to cancel the ongoing transfer of a disk image"
 msgstr ""
-"Otentikasi diperlukan untuk mengubah kata sandi dari suatu area rumah "
-"pengguna."
+"Otentikasi diperlukan untuk membatalkan transfer suatu image disk yang "
+"sedang berjalan"
 
 #: src/locale/org.freedesktop.locale1.policy:22
 msgid "Set system locale"
index bf614d978374678334713b2629f4f6771264c44d..8c2175c5bacd2758fabdc62661ba2167a344f54a 100644 (file)
@@ -7,6 +7,7 @@ SUBSYSTEM!="media", GOTO="persistent_media_ctl_end"
 ENV{MAJOR}=="", GOTO="persistent_media_ctl_end"
 
 IMPORT{builtin}="path_id"
-ENV{ID_PATH}=="?*", KERNEL=="media*", SYMLINK+="media/by-path/$env{ID_PATH}-media-controller"
+KERNEL=="media*", ENV{ID_PATH_WITH_USB_REVISION}=="?*", SYMLINK+="media/by-path/$env{ID_PATH_WITH_USB_REVISION}-media-controller"
+KERNEL=="media*", ENV{ID_PATH_WITH_USB_REVISION}=="", ENV{ID_PATH}=="?*", SYMLINK+="media/by-path/$env{ID_PATH}-media-controller"
 
 LABEL="persistent_media_ctl_end"
index 61a9610aa3afdba4ff5a8a4e55d358c9343c6e4e..8d88ab46bc18005190fa8d3fe4b00f844c689161 100644 (file)
@@ -36,7 +36,8 @@ _portablectl() {
     local cur=${COMP_WORDS[COMP_CWORD]} prev=${COMP_WORDS[COMP_CWORD-1]}
     local -A OPTS=(
         [STANDALONE]='-q --quiet --runtime --no-reload --cat --no-pager --no-legend
-                              --no-ask-password --enable --now -h --help --version'
+                              --no-ask-password --enable --now -h --help --version
+                              --clean --no-block --force'
         [ARG]='-p --profile --copy -H --host -M --machine --extension'
     )
 
index 7f85e70d40fc56e2cb50cee6c858310e4bbc588c..f4576c4355b7c4138e872dc7df1d8e4b2e411a45 100644 (file)
@@ -193,7 +193,7 @@ _systemctl () {
                 comps='auto yes no'
                 ;;
             --what)
-                comps='configuration state cache logs runtime all'
+                comps='configuration state cache logs runtime fdstore all'
                 ;;
             --image)
                 comps=$(compgen -A file -- "$cur")
index 486143b7f0620dff04342940da9d2c06971f08c4..668475d1f573836ec529eb2f66917acd635fb5c0 100644 (file)
@@ -1756,7 +1756,7 @@ static int assess(const SecurityInfo *info,
                         (void) table_set_display(details_table, (size_t) 0, (size_t) 1, (size_t) 2, (size_t) 3, (size_t) 7);
         }
 
-        FOREACH_ARRAY(a, security_assessor_table, ELEMENTSOF(security_assessor_table)) {
+        FOREACH_ELEMENT(a, security_assessor_table) {
                 _cleanup_free_ char *d = NULL;
                 uint64_t badness;
                 void *data;
index 0e24b416bb4efa77dae6550de00d630e64534a53..6faf2c29a34f283f208d8e3e55e3cbb2fd560a65 100644 (file)
@@ -11,9 +11,9 @@ int verb_srk(int argc, char *argv[], void *userdata) {
         _cleanup_(Esys_Freep) TPM2B_PUBLIC *public = NULL;
         int r;
 
-        r = tpm2_context_new(/* device= */ NULL, &c);
+        r = tpm2_context_new_or_warn(/* device= */ NULL, &c);
         if (r < 0)
-                return log_error_errno(r, "Failed to create TPM2 context: %m");
+                return r;
 
         r = tpm2_get_srk(
                         c,
index 62e055dda4de7708701b2c6f4e2fe56cab3a0288..1a2699166f37225ec3bdc8125a650ad83c883923 100644 (file)
@@ -249,7 +249,7 @@ void unit_times_clear(UnitTimes *t) {
         if (!t)
                 return;
 
-        FOREACH_ARRAY(d, t->deps, ELEMENTSOF(t->deps))
+        FOREACH_ELEMENT(d, t->deps)
                 *d = strv_free(*d);
 
         t->name = mfree(t->name);
index 3e5adcab2ef0ad62b768cae65b53d0fb67e244a3..8e83c9a589b1707013a337e466bcd692686e323f 100644 (file)
@@ -202,12 +202,12 @@ static int verify_executables(Unit *u, const char *root) {
         assert(u);
 
         if (u->type == UNIT_SERVICE)
-                FOREACH_ARRAY(i, SERVICE(u)->exec_command, ELEMENTSOF(SERVICE(u)->exec_command))
+                FOREACH_ELEMENT(i, SERVICE(u)->exec_command)
                         LIST_FOREACH(command, j, *i)
                                 RET_GATHER(r, verify_executable(u, j, root));
 
         if (u->type == UNIT_SOCKET)
-                FOREACH_ARRAY(i, SOCKET(u)->exec_command, ELEMENTSOF(SOCKET(u)->exec_command))
+                FOREACH_ELEMENT(i, SOCKET(u)->exec_command)
                         LIST_FOREACH(command, j, *i)
                                 RET_GATHER(r, verify_executable(u, j, root));
 
index 6faa6ad4fcef8de0e3f28211a388ded74f594d46..0d740c675ecf8e8d48961659dca71bdc90c11c15 100644 (file)
@@ -256,7 +256,7 @@ fail:
         return r;
 }
 
-static bool stderr_is_journal(void) {
+bool stderr_is_journal(void) {
         _cleanup_free_ char *w = NULL;
         const char *e;
         uint64_t dev, ino;
index 6df164e06f785a9db0890aedd06b18f8e7997812..726f035cfca3ec09fe70573038568ee52a0cf5ac 100644 (file)
@@ -85,6 +85,7 @@ int log_show_tid_from_string(const char *e);
 assert_cc(STRLEN(__FILE__) > STRLEN(RELATIVE_SOURCE_PATH) + 1);
 #define PROJECT_FILE (&__FILE__[STRLEN(RELATIVE_SOURCE_PATH) + 1])
 
+bool stderr_is_journal(void);
 int log_open(void);
 void log_close(void);
 void log_forget_fds(void);
index d43c3cdd5ddde4cfd610773624003fc96a0704b1..19d5039fd3bf451dc2020eec27b2ba3ce03a4ea0 100644 (file)
@@ -275,6 +275,9 @@ static inline int __coverity_check_and_return__(int condition) {
 #define FOREACH_ARRAY(i, array, num)                                    \
         _FOREACH_ARRAY(i, array, num, UNIQ_T(m, UNIQ), UNIQ_T(end, UNIQ))
 
+#define FOREACH_ELEMENT(i, array)                                 \
+        FOREACH_ARRAY(i, array, ELEMENTSOF(array))
+
 #define _DEFINE_TRIVIAL_REF_FUNC(type, name, scope)             \
         scope type *name##_ref(type *p) {                       \
                 if (!p)                                         \
index 5272477c2ac8deef4b4ecf0a2d8901a6e16dbae7..ad8d6d5b30d4d636da5d55472e47e14eca994ff4 100644 (file)
@@ -525,6 +525,18 @@ int path_compare_filename(const char *a, const char *b) {
         return strcmp(fa, fb);
 }
 
+int path_equal_or_inode_same_full(const char *a, const char *b, int flags) {
+        /* Returns true if paths are of the same entry, false if not, <0 on error. */
+
+        if (path_equal(a, b))
+                return 1;
+
+        if (!a || !b)
+                return 0;
+
+        return inode_same(a, b, flags);
+}
+
 char* path_extend_internal(char **x, ...) {
         size_t sz, old_sz;
         char *q, *nx;
index 62a012d39f02ed968a55cd7f1e522a5c388b4266..fcb3aa93999a53bc87fc2a46cd946e3dad12948d 100644 (file)
@@ -67,8 +67,9 @@ static inline bool path_equal_filename(const char *a, const char *b) {
         return path_compare_filename(a, b) == 0;
 }
 
+int path_equal_or_inode_same_full(const char *a, const char *b, int flags);
 static inline bool path_equal_or_inode_same(const char *a, const char *b, int flags) {
-        return path_equal(a, b) || inode_same(a, b, flags) > 0;
+        return path_equal_or_inode_same_full(a, b, flags) > 0;
 }
 
 char* path_extend_internal(char **x, ...);
index 7e2c0781fe12870bcd9e4bc36488c90ce5930c13..09703506f93367125bcd8a0c98952f7d0033056a 100644 (file)
@@ -447,7 +447,7 @@ static Virtualization detect_vm_zvm(void) {
 /* Returns a short identifier for the various VM implementations */
 Virtualization detect_vm(void) {
         static thread_local Virtualization cached_found = _VIRTUALIZATION_INVALID;
-        bool other = false;
+        bool other = false, hyperv = false;
         int xen_dom0 = 0;
         Virtualization v, dmi;
 
@@ -504,7 +504,12 @@ Virtualization detect_vm(void) {
         v = detect_vm_cpuid();
         if (v < 0)
                 return v;
-        if (v == VIRTUALIZATION_VM_OTHER)
+        if (v == VIRTUALIZATION_MICROSOFT)
+                /* QEMU sets the CPUID string to hyperv's, in case it provides hyperv enlightenments. Let's
+                 * hence not return Microsoft here but just use the other mechanisms first to make a better
+                 * decision. */
+                hyperv = true;
+        else if (v == VIRTUALIZATION_VM_OTHER)
                 other = true;
         else if (v != VIRTUALIZATION_NONE)
                 goto finish;
@@ -545,8 +550,15 @@ Virtualization detect_vm(void) {
                 return v;
 
 finish:
-        if (v == VIRTUALIZATION_NONE && other)
-                v = VIRTUALIZATION_VM_OTHER;
+        /* None of the checks above gave us a clear answer, hence let's now use fallback logic: if hyperv
+         * enlightenments are available but the VMM wasn't recognized as anything yet, it's probably
+         * Microsoft. */
+        if (v == VIRTUALIZATION_NONE) {
+                if (hyperv)
+                        v = VIRTUALIZATION_MICROSOFT;
+                else if (other)
+                        v = VIRTUALIZATION_VM_OTHER;
+        }
 
         cached_found = v;
         log_debug("Found VM virtualization %s", virtualization_to_string(v));
index 6c0f956c32f17cc052f1216818770e21dc04ea4c..b4decc152d0369451ed1c7e7759abdab7de115c8 100644 (file)
@@ -2255,9 +2255,9 @@ static EFI_STATUS initrd_prepare(
         assert(ret_initrd_size);
 
         if (entry->type != LOADER_LINUX || !entry->initrd) {
-                ret_options = NULL;
-                ret_initrd = NULL;
-                ret_initrd_size = 0;
+                *ret_options = NULL;
+                *ret_initrd = NULL;
+                *ret_initrd_size = 0;
                 return EFI_SUCCESS;
         }
 
index 1408d74bb60b0bb03be0c8b30294bc9addf648ea..bd1118a58a5a3f2e85cfc97100844601d9805214 100644 (file)
@@ -65,7 +65,7 @@ static EFI_STATUS pack_cpio_one(
         char *a;
 
         assert(fname);
-        assert(contents_size || contents_size == 0);
+        assert(contents || contents_size == 0);
         assert(target_dir_prefix);
         assert(inode_counter);
         assert(cpio_buffer);
index 58586f942faa4ccb6bee87450be72e260944af76..9aa605b7563e06c7b4db37bbaf73782b075fa399 100644 (file)
@@ -26,28 +26,27 @@ DECLARE_NOALLOC_SECTION(".sdmagic", "#### LoaderInfo: systemd-stub " GIT_VERSION
 
 DECLARE_SBAT(SBAT_STUB_SECTION_TEXT);
 
-static EFI_STATUS combine_initrd(
-                EFI_PHYSICAL_ADDRESS initrd_base, size_t initrd_size,
-                const void * const extra_initrds[], const size_t extra_initrd_sizes[], size_t n_extra_initrds,
+/* Combine initrds by concatenation in memory */
+static EFI_STATUS combine_initrds(
+                const void * const initrds[], const size_t initrd_sizes[], size_t n_initrds,
                 Pages *ret_initr_pages, size_t *ret_initrd_size) {
 
-        size_t n;
+        size_t n = 0;
 
         assert(ret_initr_pages);
         assert(ret_initrd_size);
 
-        /* Combines four initrds into one, by simple concatenation in memory */
-
-        n = ALIGN4(initrd_size); /* main initrd might not be padded yet */
-
-        for (size_t i = 0; i < n_extra_initrds; i++) {
-                if (!extra_initrds[i])
+        for (size_t i = 0; i < n_initrds; i++) {
+                if (!initrds[i])
                         continue;
 
-                if (n > SIZE_MAX - extra_initrd_sizes[i])
+                /* some initrds (the ones from UKI sections) need padding,
+                 * pad all to be safe */
+                size_t initrd_size = ALIGN4(initrd_sizes[i]);
+                if (n > SIZE_MAX - initrd_size)
                         return EFI_OUT_OF_RESOURCES;
 
-                n += extra_initrd_sizes[i];
+                n += initrd_size;
         }
 
         _cleanup_pages_ Pages pages = xmalloc_pages(
@@ -56,27 +55,21 @@ static EFI_STATUS combine_initrd(
                         EFI_SIZE_TO_PAGES(n),
                         UINT32_MAX /* Below 4G boundary. */);
         uint8_t *p = PHYSICAL_ADDRESS_TO_POINTER(pages.addr);
-        if (initrd_base != 0) {
+        for (size_t i = 0; i < n_initrds; i++) {
+                if (!initrds[i])
+                        continue;
+
                 size_t pad;
 
-                /* Order matters, the real initrd must come first, since it might include microcode updates
-                 * which the kernel only looks for in the first cpio archive */
-                p = mempcpy(p, PHYSICAL_ADDRESS_TO_POINTER(initrd_base), initrd_size);
+                p = mempcpy(p, initrds[i], initrd_sizes[i]);
 
-                pad = ALIGN4(initrd_size) - initrd_size;
+                pad = ALIGN4(initrd_sizes[i]) - initrd_sizes[i];
                 if (pad > 0)  {
                         memzero(p, pad);
                         p += pad;
                 }
         }
 
-        for (size_t i = 0; i < n_extra_initrds; i++) {
-                if (!extra_initrds[i])
-                        continue;
-
-                p = mempcpy(p, extra_initrds[i], extra_initrd_sizes[i]);
-        }
-
         assert(PHYSICAL_ADDRESS_TO_POINTER(pages.addr + n) == p);
 
         *ret_initr_pages = pages;
@@ -503,8 +496,8 @@ static EFI_STATUS run(EFI_HANDLE image) {
         void **dt_bases_addons_global = NULL, **dt_bases_addons_uki = NULL;
         char16_t **dt_filenames_addons_global = NULL, **dt_filenames_addons_uki = NULL;
         _cleanup_free_ size_t *dt_sizes_addons_global = NULL, *dt_sizes_addons_uki = NULL;
-        size_t linux_size, initrd_size, dt_size, n_dts_addons_global = 0, n_dts_addons_uki = 0;
-        EFI_PHYSICAL_ADDRESS linux_base, initrd_base, dt_base;
+        size_t linux_size, initrd_size, ucode_size, dt_size, n_dts_addons_global = 0, n_dts_addons_uki = 0;
+        EFI_PHYSICAL_ADDRESS linux_base, initrd_base, ucode_base, dt_base;
         _cleanup_(devicetree_cleanup) struct devicetree_state dt_state = {};
         EFI_LOADED_IMAGE_PROTOCOL *loaded_image;
         size_t addrs[_UNIFIED_SECTION_MAX] = {}, szs[_UNIFIED_SECTION_MAX] = {};
@@ -541,6 +534,10 @@ static EFI_STATUS run(EFI_HANDLE image) {
         CLEANUP_ARRAY(dt_filenames_addons_global, n_dts_addons_global, dt_filenames_free);
         CLEANUP_ARRAY(dt_filenames_addons_uki, n_dts_addons_uki, dt_filenames_free);
 
+        if (szs[UNIFIED_SECTION_UNAME] > 0)
+                uname = xstrndup8((char *)loaded_image->ImageBase + addrs[UNIFIED_SECTION_UNAME],
+                                  szs[UNIFIED_SECTION_UNAME]);
+
         /* Now that we have the UKI sections loaded, also load global first and then local (per-UKI)
          * addons. The data is loaded at once, and then used later. */
         err = load_addons(
@@ -615,10 +612,6 @@ static EFI_STATUS run(EFI_HANDLE image) {
         /* Show splash screen as early as possible */
         graphics_splash((const uint8_t*) loaded_image->ImageBase + addrs[UNIFIED_SECTION_SPLASH], szs[UNIFIED_SECTION_SPLASH]);
 
-        if (szs[UNIFIED_SECTION_UNAME] > 0)
-                uname = xstrndup8((char *)loaded_image->ImageBase + addrs[UNIFIED_SECTION_UNAME],
-                                  szs[UNIFIED_SECTION_UNAME]);
-
         if (use_load_options(image, loaded_image, szs[UNIFIED_SECTION_CMDLINE] > 0, &cmdline)) {
                 /* Let's measure the passed kernel command line into the TPM. Note that this possibly
                  * duplicates what we already did in the boot menu, if that was already used. However, since
@@ -792,12 +785,18 @@ static EFI_STATUS run(EFI_HANDLE image) {
         initrd_size = szs[UNIFIED_SECTION_INITRD];
         initrd_base = initrd_size != 0 ? POINTER_TO_PHYSICAL_ADDRESS(loaded_image->ImageBase) + addrs[UNIFIED_SECTION_INITRD] : 0;
 
+        ucode_size = szs[UNIFIED_SECTION_UCODE];
+        ucode_base = ucode_size != 0 ? POINTER_TO_PHYSICAL_ADDRESS(loaded_image->ImageBase) + addrs[UNIFIED_SECTION_UCODE] : 0;
+
         _cleanup_pages_ Pages initrd_pages = {};
-        if (credential_initrd || global_credential_initrd || sysext_initrd || confext_initrd || pcrsig_initrd || pcrpkey_initrd) {
-                /* If we have generated initrds dynamically, let's combine them with the built-in initrd. */
-                err = combine_initrd(
-                                initrd_base, initrd_size,
+        if (ucode_base || credential_initrd || global_credential_initrd || sysext_initrd || confext_initrd || pcrsig_initrd || pcrpkey_initrd) {
+                /* If we have generated initrds dynamically or there is a microcode initrd, combine them with the built-in initrd. */
+                err = combine_initrds(
                                 (const void*const[]) {
+                                        /* Microcode must always be first as kernel only scans uncompressed cpios
+                                         * and later initrds might be compressed. */
+                                        PHYSICAL_ADDRESS_TO_POINTER(ucode_base),
+                                        PHYSICAL_ADDRESS_TO_POINTER(initrd_base),
                                         credential_initrd,
                                         global_credential_initrd,
                                         sysext_initrd,
@@ -806,6 +805,8 @@ static EFI_STATUS run(EFI_HANDLE image) {
                                         pcrpkey_initrd,
                                 },
                                 (const size_t[]) {
+                                        ucode_size,
+                                        initrd_size,
                                         credential_initrd_size,
                                         global_credential_initrd_size,
                                         sysext_initrd_size,
@@ -813,7 +814,7 @@ static EFI_STATUS run(EFI_HANDLE image) {
                                         pcrsig_initrd_size,
                                         pcrpkey_initrd_size,
                                 },
-                                6,
+                                8,
                                 &initrd_pages, &initrd_size);
                 if (err != EFI_SUCCESS)
                         return err;
index 789a3deb8ce276aee21cdb3c9fa65c2de45e9fc5..41fcd5a91ad1eafc39893afd9b5d059034609cd1 100644 (file)
@@ -92,6 +92,7 @@ static int help(int argc, char *argv[], void *userdata) {
                "     --osrel=PATH        Path to os-release file                %7$s .osrel\n"
                "     --cmdline=PATH      Path to file with kernel command line  %7$s .cmdline\n"
                "     --initrd=PATH       Path to initrd image file              %7$s .initrd\n"
+               "     --ucode=PATH        Path to microcode image file           %7$s .ucode\n"
                "     --splash=PATH       Path to splash bitmap file             %7$s .splash\n"
                "     --dtb=PATH          Path to Devicetree file                %7$s .dtb\n"
                "     --uname=PATH        Path to 'uname -r' file                %7$s .uname\n"
@@ -133,6 +134,7 @@ static int parse_argv(int argc, char *argv[]) {
                 ARG_OSREL,
                 ARG_CMDLINE,
                 ARG_INITRD,
+                ARG_UCODE,
                 ARG_SPLASH,
                 ARG_DTB,
                 ARG_UNAME,
@@ -159,6 +161,7 @@ static int parse_argv(int argc, char *argv[]) {
                 { "osrel",              required_argument, NULL, ARG_OSREL              },
                 { "cmdline",            required_argument, NULL, ARG_CMDLINE            },
                 { "initrd",             required_argument, NULL, ARG_INITRD             },
+                { "ucode",              required_argument, NULL, ARG_UCODE              },
                 { "splash",             required_argument, NULL, ARG_SPLASH             },
                 { "dtb",                required_argument, NULL, ARG_DTB                },
                 { "uname",              required_argument, NULL, ARG_UNAME              },
index 852a16622f1108e689e014755c62de4dd44ec6a7..47f028fbe0711403cea34a3204fafc1c3badc4e3 100644 (file)
@@ -4918,7 +4918,7 @@ void unit_reset_memory_accounting_last(Unit *u) {
         if (!crt || !crt->cgroup_path)
                 return;
 
-        FOREACH_ARRAY(i, crt->memory_accounting_last, ELEMENTSOF(crt->memory_accounting_last))
+        FOREACH_ELEMENT(i, crt->memory_accounting_last)
                 *i = UINT64_MAX;
 }
 
@@ -5226,13 +5226,13 @@ CGroupRuntime *cgroup_runtime_new(void) {
                 .cgroup_invalidated_mask = _CGROUP_MASK_ALL,
         };
 
-        FOREACH_ARRAY(i, crt->memory_accounting_last, ELEMENTSOF(crt->memory_accounting_last))
+        FOREACH_ELEMENT(i, crt->memory_accounting_last)
                 *i = UINT64_MAX;
-        FOREACH_ARRAY(i, crt->io_accounting_base, ELEMENTSOF(crt->io_accounting_base))
+        FOREACH_ELEMENT(i, crt->io_accounting_base)
                 *i = UINT64_MAX;
-        FOREACH_ARRAY(i, crt->io_accounting_last, ELEMENTSOF(crt->io_accounting_last))
+        FOREACH_ELEMENT(i, crt->io_accounting_last)
                 *i = UINT64_MAX;
-        FOREACH_ARRAY(i, crt->ip_accounting_extra, ELEMENTSOF(crt->ip_accounting_extra))
+        FOREACH_ELEMENT(i, crt->ip_accounting_extra)
                 *i = UINT64_MAX;
 
         return TAKE_PTR(crt);
index d5b50e8518360ddf4c570a127e0f1100fb40cc69..693efbbcbaff0b05009bf361eb2b4fbfbcd4a85b 100644 (file)
@@ -54,7 +54,7 @@ int bus_job_method_cancel(sd_bus_message *message, void *userdata, sd_bus_error
         if (!sd_bus_track_contains(j->bus_track, sd_bus_message_get_sender(message))) {
 
                 /* And for everybody else consult polkit */
-                r = bus_verify_manage_units_async(j->unit->manager, message, error);
+                r = bus_verify_manage_units_async(j->manager, message, error);
                 if (r < 0)
                         return r;
                 if (r == 0)
@@ -263,7 +263,7 @@ void bus_job_send_pending_change_signal(Job *j, bool including_new) {
         if (!j->sent_dbus_new_signal && !including_new)
                 return;
 
-        if (MANAGER_IS_RELOADING(j->unit->manager))
+        if (MANAGER_IS_RELOADING(j->manager))
                 return;
 
         bus_job_send_change_signal(j);
@@ -332,12 +332,12 @@ static int bus_job_allocate_bus_track(Job *j) {
         if (j->bus_track)
                 return 0;
 
-        return sd_bus_track_new(j->unit->manager->api_bus, &j->bus_track, bus_job_track_handler, j);
+        return sd_bus_track_new(j->manager->api_bus, &j->bus_track, bus_job_track_handler, j);
 }
 
 int bus_job_coldplug_bus_track(Job *j) {
-        int r;
         _cleanup_strv_free_ char **deserialized_clients = NULL;
+        int r;
 
         assert(j);
 
@@ -362,7 +362,7 @@ int bus_job_track_sender(Job *j, sd_bus_message *m) {
         assert(j);
         assert(m);
 
-        if (sd_bus_message_get_bus(m) != j->unit->manager->api_bus) {
+        if (sd_bus_message_get_bus(m) != j->manager->api_bus) {
                 j->ref_by_private_bus = true;
                 return 0;
         }
index a336b04daf377c95e725d0f9276b37561f0465fd..1c6f6fc8b3838151e2da5ae92be4d8d15aa973d2 100644 (file)
@@ -1113,31 +1113,29 @@ int bus_foreach_bus(
                 int (*send_message)(sd_bus *bus, void *userdata),
                 void *userdata) {
 
-        sd_bus *b;
-        int r, ret = 0;
+        int r = 0;
+
+        assert(m);
+        assert(send_message);
 
         /* Send to all direct buses, unconditionally */
+        sd_bus *b;
         SET_FOREACH(b, m->private_buses) {
 
                 /* Don't bother with enqueuing these messages to clients that haven't started yet */
                 if (sd_bus_is_ready(b) <= 0)
                         continue;
 
-                r = send_message(b, userdata);
-                if (r < 0)
-                        ret = r;
+                RET_GATHER(r, send_message(b, userdata));
         }
 
         /* Send to API bus, but only if somebody is subscribed */
         if (m->api_bus &&
             (sd_bus_track_count(m->subscribed) > 0 ||
-             sd_bus_track_count(subscribed2) > 0)) {
-                r = send_message(m->api_bus, userdata);
-                if (r < 0)
-                        ret = r;
-        }
+             sd_bus_track_count(subscribed2) > 0))
+                RET_GATHER(r, send_message(m->api_bus, userdata));
 
-        return ret;
+        return r;
 }
 
 void bus_track_serialize(sd_bus_track *t, FILE *f, const char *prefix) {
index 90e11d0d1e9c1fc419e53940b6c807013c677648..32b48ececc0d1f267fb260889e1e7d13f972f28d 100644 (file)
@@ -3717,7 +3717,7 @@ static int connect_unix_harder(const ExecContext *c, const ExecParameters *p, co
                 return log_exec_error_errno(c, p, r, "Failed to set sockaddr for '%s': %m", of->path);
         sa_len = r;
 
-        FOREACH_ARRAY(i, socket_types, ELEMENTSOF(socket_types)) {
+        FOREACH_ELEMENT(i, socket_types) {
                 _cleanup_close_ int fd = -EBADF;
 
                 fd = socket(AF_UNIX, *i|SOCK_CLOEXEC, 0);
index c6b48035a79155b252c84996bbedc9df3454be9b..2f19468a9544f8cc7cfa4e4dcd3197f3099c223b 100644 (file)
@@ -133,6 +133,7 @@ Job* job_free(Job *j) {
 
 static void job_set_state(Job *j, JobState state) {
         assert(j);
+        assert(j->manager);
         assert(state >= 0);
         assert(state < _JOB_STATE_MAX);
 
@@ -145,15 +146,15 @@ static void job_set_state(Job *j, JobState state) {
                 return;
 
         if (j->state == JOB_RUNNING)
-                j->unit->manager->n_running_jobs++;
+                j->manager->n_running_jobs++;
         else {
                 assert(j->state == JOB_WAITING);
-                assert(j->unit->manager->n_running_jobs > 0);
+                assert(j->manager->n_running_jobs > 0);
 
-                j->unit->manager->n_running_jobs--;
+                j->manager->n_running_jobs--;
 
-                if (j->unit->manager->n_running_jobs <= 0)
-                        j->unit->manager->jobs_in_progress_event_source = sd_event_source_disable_unref(j->unit->manager->jobs_in_progress_event_source);
+                if (j->manager->n_running_jobs <= 0)
+                        j->manager->jobs_in_progress_event_source = sd_event_source_disable_unref(j->manager->jobs_in_progress_event_source);
         }
 }
 
@@ -281,6 +282,8 @@ int job_install_deserialized(Job *j) {
         Job **pj;
         int r;
 
+        assert(j);
+        assert(j->manager);
         assert(!j->installed);
 
         if (j->type < 0 || j->type >= _JOB_TYPE_MAX_IN_TRANSACTION)
@@ -307,7 +310,7 @@ int job_install_deserialized(Job *j) {
         j->installed = true;
 
         if (j->state == JOB_RUNNING)
-                j->unit->manager->n_running_jobs++;
+                j->manager->n_running_jobs++;
 
         log_unit_debug(j->unit,
                        "Reinstalled deserialized job %s/%s as %u",
@@ -1375,6 +1378,7 @@ int job_coldplug(Job *j) {
 
 void job_shutdown_magic(Job *j) {
         assert(j);
+        assert(j->manager);
 
         /* The shutdown target gets some special treatment here: we
          * tell the kernel to begin with flushing its disk caches, to
@@ -1391,15 +1395,15 @@ void job_shutdown_magic(Job *j) {
                 return;
 
         /* This is the very beginning of the shutdown phase, so take the timestamp here */
-        dual_timestamp_now(ASSERT_PTR(j->manager)->timestamps + MANAGER_TIMESTAMP_SHUTDOWN_START);
+        dual_timestamp_now(j->manager->timestamps + MANAGER_TIMESTAMP_SHUTDOWN_START);
 
         if (!MANAGER_IS_SYSTEM(j->manager))
                 return;
 
         /* In case messages on console has been disabled on boot */
-        j->unit->manager->no_console_output = false;
+        j->manager->no_console_output = false;
 
-        manager_invalidate_startup_units(j->unit->manager);
+        manager_invalidate_startup_units(j->manager);
 
         if (detect_container() > 0)
                 return;
@@ -1439,6 +1443,7 @@ bool job_may_gc(Job *j) {
         Unit *other;
 
         assert(j);
+        assert(j->manager);
 
         /* Checks whether this job should be GC'ed away. We only do this for jobs of units that have no effect on their
          * own and just track external state. For now the only unit type that qualifies for this are .device units.
@@ -1459,7 +1464,7 @@ bool job_may_gc(Job *j) {
          * referenced by one, and reset this whenever we notice that no private bus connections are around. This means
          * the GC is a bit too conservative when it comes to jobs created by private bus connections. */
         if (j->ref_by_private_bus) {
-                if (set_isempty(j->unit->manager->private_buses))
+                if (set_isempty(j->manager->private_buses))
                         j->ref_by_private_bus = false;
                 else
                         return false;
@@ -1482,6 +1487,7 @@ bool job_may_gc(Job *j) {
 
 void job_add_to_gc_queue(Job *j) {
         assert(j);
+        assert(j->manager);
 
         if (j->in_gc_queue)
                 return;
@@ -1489,7 +1495,7 @@ void job_add_to_gc_queue(Job *j) {
         if (!job_may_gc(j))
                 return;
 
-        LIST_PREPEND(gc_queue, j->unit->manager->gc_job_queue, j);
+        LIST_PREPEND(gc_queue, j->manager->gc_job_queue, j);
         j->in_gc_queue = true;
 }
 
index 3f8f7e3bca581af6b5500dc5c08cf55068a86215..c39b136e046076d7633fd8cd1312cc59c941366e 100644 (file)
@@ -157,7 +157,7 @@ int kmod_setup(void) {
                 return 0;
 
         _cleanup_(sym_kmod_unrefp) struct kmod_ctx *ctx = NULL;
-        FOREACH_ARRAY(kmod, kmod_table, ELEMENTSOF(kmod_table)) {
+        FOREACH_ELEMENT(kmod, kmod_table) {
                 if (kmod->path && access(kmod->path, F_OK) >= 0)
                         continue;
 
index 6008e5655e800c9cf3cbf1638128ade431af9831..3c6475574337bab11b50aeec5726b7aa81ad5b2c 100644 (file)
@@ -109,6 +109,7 @@ int manager_serialize(
         (void) serialize_usec(f, "pretimeout-watchdog-overridden", m->watchdog_overridden[WATCHDOG_PRETIMEOUT]);
         (void) serialize_item(f, "pretimeout-watchdog-governor-overridden", m->watchdog_pretimeout_governor_overridden);
 
+        (void) serialize_item(f, "previous-objective", manager_objective_to_string(m->objective));
         (void) serialize_item_format(f, "soft-reboots-count", "%u", m->soft_reboots_count);
 
         for (ManagerTimestamp q = 0; q < _MANAGER_TIMESTAMP_MAX; q++) {
@@ -158,9 +159,6 @@ int manager_serialize(
         (void) serialize_ratelimit(f, "dump-ratelimit", &m->dump_ratelimit);
         (void) serialize_ratelimit(f, "reload-reexec-ratelimit", &m->reload_reexec_ratelimit);
 
-        if (m->objective >= 0 && m->objective < _MANAGER_OBJECTIVE_MAX)
-                (void) serialize_item_format(f, "previous-objective", "%u", (unsigned) m->objective);
-
         bus_track_serialize(m->subscribed, f, "subscribed");
 
         r = dynamic_user_serialize(m, f, fds);
@@ -533,12 +531,13 @@ int manager_deserialize(Manager *m, FILE *f, FDSet *fds) {
                         else
                                 m->soft_reboots_count = n;
                 } else if ((val = startswith(l, "previous-objective="))) {
-                        unsigned n;
+                        ManagerObjective objective;
 
-                        if (safe_atou(val, &n) < 0 || n >= _MANAGER_OBJECTIVE_MAX)
-                                log_notice("Failed to parse objective '%s', ignoring.", val);
+                        objective = manager_objective_from_string(val);
+                        if (objective < 0)
+                                log_notice("Failed to parse previous objective '%s', ignoring.", val);
                         else
-                                m->previous_objective = n;
+                                m->previous_objective = objective;
 
                 } else {
                         ManagerTimestamp q;
index b627e7abc088c7f5f36288d0fcfbf5a1cf24ee15..564aa1022484a8c55dd7bc74a2feb6cbe47ac3f0 100644 (file)
@@ -1978,6 +1978,15 @@ int manager_startup(Manager *m, FILE *serialization, FDSet *fds, const char *roo
                                 return log_error_errno(r, "Deserialization failed: %m");
                 }
 
+                if (m->previous_objective >= 0) {
+                        if (IN_SET(m->previous_objective, MANAGER_REEXECUTE, MANAGER_SOFT_REBOOT, MANAGER_SWITCH_ROOT))
+                                log_debug("Launching as effect of a '%s' operation.",
+                                          manager_objective_to_string(m->previous_objective));
+                        else
+                                log_warning("Got unexpected previous objective '%s', ignoring.",
+                                            manager_objective_to_string(m->previous_objective));
+                }
+
                 /* If we are in a new soft-reboot iteration bump the counter now before starting units, so
                  * that they can reliably read it. We get the previous objective from serialized state. */
                 if (m->previous_objective == MANAGER_SOFT_REBOOT)
@@ -3702,13 +3711,15 @@ static void manager_notify_finished(Manager *m) {
         if (MANAGER_IS_SYSTEM(m) && m->soft_reboots_count > 0) {
                 /* The soft-reboot case, where we only report data for the last reboot */
                 firmware_usec = loader_usec = initrd_usec = kernel_usec = 0;
-                total_usec = userspace_usec = usec_sub_unsigned(m->timestamps[MANAGER_TIMESTAMP_FINISH].monotonic, m->timestamps[MANAGER_TIMESTAMP_SHUTDOWN_START].monotonic);
+                total_usec = userspace_usec = usec_sub_unsigned(m->timestamps[MANAGER_TIMESTAMP_FINISH].monotonic,
+                                                                m->timestamps[MANAGER_TIMESTAMP_SHUTDOWN_START].monotonic);
 
                 log_struct(LOG_INFO,
                            "MESSAGE_ID=" SD_MESSAGE_STARTUP_FINISHED_STR,
                            "USERSPACE_USEC="USEC_FMT, userspace_usec,
-                           LOG_MESSAGE("Soft-reboot finished in %s.",
-                                       FORMAT_TIMESPAN(total_usec, USEC_PER_MSEC)));
+                           LOG_MESSAGE("Soft-reboot finished in %s, counter is now at %u.",
+                                       FORMAT_TIMESPAN(total_usec, USEC_PER_MSEC),
+                                       m->soft_reboots_count));
         } else if (MANAGER_IS_SYSTEM(m) && detect_container() <= 0) {
                 char buf[FORMAT_TIMESPAN_MAX + STRLEN(" (firmware) + ") + FORMAT_TIMESPAN_MAX + STRLEN(" (loader) + ")]
                         = {};
@@ -4415,7 +4426,7 @@ void manager_override_show_status(Manager *m, ShowStatus mode, const char *reaso
         set_show_status_marker(show_status_on(mode));
 }
 
-const char *manager_get_confirm_spawn(Manager *m) {
+const charmanager_get_confirm_spawn(Manager *m) {
         static int last_errno = 0;
         struct stat st;
         int r;
@@ -5038,7 +5049,7 @@ LogTarget manager_get_executor_log_target(Manager *m) {
         return LOG_TARGET_KMSG;
 }
 
-static const char *const manager_state_table[_MANAGER_STATE_MAX] = {
+static const charconst manager_state_table[_MANAGER_STATE_MAX] = {
         [MANAGER_INITIALIZING] = "initializing",
         [MANAGER_STARTING]     = "starting",
         [MANAGER_RUNNING]      = "running",
@@ -5049,7 +5060,22 @@ static const char *const manager_state_table[_MANAGER_STATE_MAX] = {
 
 DEFINE_STRING_TABLE_LOOKUP(manager_state, ManagerState);
 
-static const char *const manager_timestamp_table[_MANAGER_TIMESTAMP_MAX] = {
+static const char* const manager_objective_table[_MANAGER_OBJECTIVE_MAX] = {
+        [MANAGER_OK]          = "ok",
+        [MANAGER_EXIT]        = "exit",
+        [MANAGER_RELOAD]      = "reload",
+        [MANAGER_REEXECUTE]   = "reexecute",
+        [MANAGER_REBOOT]      = "reboot",
+        [MANAGER_SOFT_REBOOT] = "soft-reboot",
+        [MANAGER_POWEROFF]    = "poweroff",
+        [MANAGER_HALT]        = "halt",
+        [MANAGER_KEXEC]       = "kexec",
+        [MANAGER_SWITCH_ROOT] = "switch-root",
+};
+
+DEFINE_STRING_TABLE_LOOKUP(manager_objective, ManagerObjective);
+
+static const char* const manager_timestamp_table[_MANAGER_TIMESTAMP_MAX] = {
         [MANAGER_TIMESTAMP_FIRMWARE]                 = "firmware",
         [MANAGER_TIMESTAMP_LOADER]                   = "loader",
         [MANAGER_TIMESTAMP_KERNEL]                   = "kernel",
index 70ae78aa8497f8f929ef51be5fdf49d198310a1a..c4ad42293e5dbfa4006d13fc5b1a29d6ef7c2274 100644 (file)
@@ -627,13 +627,16 @@ void manager_restore_original_log_level(Manager *m);
 void manager_override_log_target(Manager *m, LogTarget target);
 void manager_restore_original_log_target(Manager *m);
 
-const char *manager_state_to_string(ManagerState m) _const_;
+const char* manager_get_confirm_spawn(Manager *m);
+void manager_disable_confirm_spawn(void);
+
+const char* manager_state_to_string(ManagerState m) _const_;
 ManagerState manager_state_from_string(const char *s) _pure_;
 
-const char *manager_get_confirm_spawn(Manager *m);
-void manager_disable_confirm_spawn(void);
+const char* manager_objective_to_string(ManagerObjective m) _const_;
+ManagerObjective manager_objective_from_string(const char *s) _pure_;
 
-const char *manager_timestamp_to_string(ManagerTimestamp m) _const_;
+const charmanager_timestamp_to_string(ManagerTimestamp m) _const_;
 ManagerTimestamp manager_timestamp_from_string(const char *s) _pure_;
 ManagerTimestamp manager_timestamp_initrd_mangle(ManagerTimestamp s);
 
index b5c6348b6fcbc7bef2e7f5a6f94c05f788759174..d70a44b23c853a02132f87507a8d58b80b181a9a 100644 (file)
@@ -446,7 +446,7 @@ static int transaction_verify_order_one(Transaction *tr, Job *j, Job *from, unsi
          * the graph over 'before' edges in the actual job execution order. We traverse over both unit
          * ordering dependencies and we test with job_compare() whether it is the 'before' edge in the job
          * execution ordering. */
-        FOREACH_ARRAY(d, directions, ELEMENTSOF(directions)) {
+        FOREACH_ELEMENT(d, directions) {
                 Unit *u;
 
                 UNIT_FOREACH_DEPENDENCY(u, j->unit, *d) {
index f26c1c248eed0dcd8623481b71760948fdd386f5..15285a3ac4cdc1f9356f07dde87936cd7b7d726e 100644 (file)
@@ -467,7 +467,7 @@ bool unit_may_gc(Unit *u) {
                 break;
 
         case COLLECT_INACTIVE_OR_FAILED:
-                if (!IN_SET(state, UNIT_INACTIVE, UNIT_FAILED))
+                if (!UNIT_IS_INACTIVE_OR_FAILED(state))
                         return false;
 
                 break;
index 3ded815fb0778cc706497a1f13ba9b6262d183f1..83a2f4420f5380edbcc472013ca6b001c8daa5aa 100644 (file)
@@ -351,9 +351,9 @@ int enroll_tpm2(struct crypt_device *cd,
                         return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
                                                "Must provide all PCR values when using TPM2 device key.");
         } else {
-                r = tpm2_context_new(device, &tpm2_context);
+                r = tpm2_context_new_or_warn(device, &tpm2_context);
                 if (r < 0)
-                        return log_error_errno(r, "Failed to create TPM2 context: %m");
+                        return r;
 
                 if (!tpm2_pcr_values_has_all_values(hash_pcr_values, n_hash_pcr_values)) {
                         r = tpm2_pcr_read_missing_values(tpm2_context, hash_pcr_values, n_hash_pcr_values);
index d902c591dffc1c333594ca51f024d6b2766bbd32..08f901c5481ad6b21ad50c3744ac13fc92c0723b 100644 (file)
@@ -87,9 +87,9 @@ int acquire_luks2_key(
         }
 
         _cleanup_(tpm2_context_unrefp) Tpm2Context *tpm2_context = NULL;
-        r = tpm2_context_new(device, &tpm2_context);
+        r = tpm2_context_new_or_warn(device, &tpm2_context);
         if (r < 0)
-                return log_error_errno(r, "Failed to create TPM2 context: %m");
+                return r;
 
         r = tpm2_unseal(tpm2_context,
                         hash_pcr_mask,
index 34ad9ebd1455e3bdeee63c16291ea397d1563ce5..89bea30d00aa2e12071d05673e91ef43469af05d 100644 (file)
@@ -919,9 +919,9 @@ static int measure_volume_key(
 
 #if HAVE_TPM2
         _cleanup_(tpm2_context_unrefp) Tpm2Context *c = NULL;
-        r = tpm2_context_new(arg_tpm2_device, &c);
+        r = tpm2_context_new_or_warn(arg_tpm2_device, &c);
         if (r < 0)
-                return log_error_errno(r, "Failed to create TPM2 context: %m");
+                return r;
 
         _cleanup_strv_free_ char **l = NULL;
         if (strv_isempty(arg_tpm2_measure_banks)) {
index b1fa04481341029bebf2c7c4a3bac255b82e0d29..3887bf57023ae6a596e6411f393b4e7b593bb22d 100644 (file)
@@ -13,6 +13,7 @@ const char* const unified_sections[_UNIFIED_SECTION_MAX + 1] = {
         [UNIFIED_SECTION_OSREL]   = ".osrel",
         [UNIFIED_SECTION_CMDLINE] = ".cmdline",
         [UNIFIED_SECTION_INITRD]  = ".initrd",
+        [UNIFIED_SECTION_UCODE]   = ".ucode",
         [UNIFIED_SECTION_SPLASH]  = ".splash",
         [UNIFIED_SECTION_DTB]     = ".dtb",
         [UNIFIED_SECTION_UNAME]   = ".uname",
index ffa960f01b4c2abc098efddf755fd07d0e647add..8ab742dd58f3b549e2cb801ec964614d183bd948 100644 (file)
@@ -10,6 +10,7 @@ typedef enum UnifiedSection {
         UNIFIED_SECTION_OSREL,
         UNIFIED_SECTION_CMDLINE,
         UNIFIED_SECTION_INITRD,
+        UNIFIED_SECTION_UCODE,
         UNIFIED_SECTION_SPLASH,
         UNIFIED_SECTION_DTB,
         UNIFIED_SECTION_UNAME,
index 288f91cedf52716605c82facee5b53f8d5e9b515..acce71b1a88b19e70ef57b2b3f79c028954443ae 100644 (file)
@@ -150,7 +150,7 @@ static int add_credential_gettys(void) {
         };
         int r;
 
-        FOREACH_ARRAY(t, table, ELEMENTSOF(table)) {
+        FOREACH_ELEMENT(t, table) {
                 _cleanup_free_ char *b = NULL;
                 size_t sz = 0;
 
index 8091a5d882b09fa9ef3ce671bf3e138470fd0668..378781c138dce130e9ac916ddc87c87026644d1e 100644 (file)
 /* Pref64 option type (RFC8781, section 4) */
 #define RADV_OPT_PREF64                           38
 
-enum RAdvState {
+typedef enum RAdvState {
         RADV_STATE_IDLE                      = 0,
         RADV_STATE_ADVERTISING               = 1,
-};
-typedef enum RAdvState RAdvState;
+} RAdvState;
 
 struct sd_radv_opt_dns {
         uint8_t type;
@@ -98,6 +97,7 @@ struct sd_radv {
 
         int ifindex;
         char *ifname;
+        struct in6_addr ipv6ll;
 
         sd_event *event;
         int event_priority;
@@ -105,6 +105,7 @@ struct sd_radv {
         struct ether_addr mac_addr;
         uint8_t hop_limit;
         uint8_t flags;
+        uint8_t preference;
         uint32_t mtu;
         usec_t retransmit_usec;
         usec_t lifetime_usec; /* timespan */
index 79210f9e2ca182352ac6ac0a1e52b6e1290604bf..f6f30ce3197764f74e521d4135de82d4fa808246 100644 (file)
@@ -129,9 +129,32 @@ static bool router_lifetime_is_valid(usec_t lifetime_usec) {
                  lifetime_usec <= RADV_MAX_ROUTER_LIFETIME_USEC);
 }
 
-static int radv_send_router(sd_radv *ra, const struct in6_addr *dst, usec_t lifetime_usec) {
+static int radv_send_router_on_stop(sd_radv *ra) {
+        static const struct nd_router_advert adv = {
+                .nd_ra_type = ND_ROUTER_ADVERT,
+        };
+
+        _cleanup_set_free_ Set *options = NULL;
+        usec_t time_now;
+        int r;
+
+        assert(ra);
+
+        r = sd_event_now(ra->event, CLOCK_BOOTTIME, &time_now);
+        if (r < 0)
+                return r;
+
+        if (!ether_addr_is_null(&ra->mac_addr)) {
+                r = ndisc_option_set_link_layer_address(&options, SD_NDISC_OPTION_SOURCE_LL_ADDRESS, &ra->mac_addr);
+                if (r < 0)
+                        return r;
+        }
+
+        return ndisc_send(ra->fd, &IN6_ADDR_ALL_NODES_MULTICAST, &adv.nd_ra_hdr, options, time_now);
+}
+
+static int radv_send_router(sd_radv *ra, const struct in6_addr *dst) {
         assert(ra);
-        assert(router_lifetime_is_valid(lifetime_usec));
 
         struct sockaddr_in6 dst_addr = {
                 .sin6_family = AF_INET6,
@@ -139,7 +162,7 @@ static int radv_send_router(sd_radv *ra, const struct in6_addr *dst, usec_t life
         };
         struct nd_router_advert adv = {
                 .nd_ra_type = ND_ROUTER_ADVERT,
-                .nd_ra_router_lifetime = usec_to_be16_sec(lifetime_usec),
+                .nd_ra_router_lifetime = usec_to_be16_sec(ra->lifetime_usec),
                 .nd_ra_retransmit = usec_to_be32_msec(ra->retransmit_usec),
         };
         struct {
@@ -178,7 +201,9 @@ static int radv_send_router(sd_radv *ra, const struct in6_addr *dst, usec_t life
         /* The nd_ra_curhoplimit and nd_ra_flags_reserved fields cannot specified with nd_ra_router_lifetime
          * simultaneously in the structured initializer in the above. */
         adv.nd_ra_curhoplimit = ra->hop_limit;
-        adv.nd_ra_flags_reserved = ra->flags;
+        /* RFC 4191, Section 2.2,
+         * "...If the Router Lifetime is zero, the preference value MUST be set to (00) by the sender..." */
+        adv.nd_ra_flags_reserved = ra->flags | (ra->lifetime_usec > 0 ? (ra->preference << 3) : 0);
         iov[msg.msg_iovlen++] = IOVEC_MAKE(&adv, sizeof(adv));
 
         /* MAC address is optional, either because the link does not use L2 addresses or load sharing is
@@ -256,12 +281,18 @@ static int radv_process_packet(sd_radv *ra, ICMP6Packet *packet) {
         if (r < 0)
                 return r;
 
-        struct in6_addr src = {};
+        struct in6_addr src;
         r = sd_ndisc_router_solicit_get_sender_address(rs, &src);
-        if (r < 0 && r != -ENODATA) /* null address is allowed */
+        if (r == -ENODATA) /* null address is allowed */
+                return sd_radv_send(ra); /* When an unsolicited RA, we need to also update timer. */
+        if (r < 0)
                 return log_radv_errno(ra, r, "Failed to get sender address of RS, ignoring: %m");
+        if (in6_addr_equal(&src, &ra->ipv6ll))
+                /* This should be definitely caused by a misconfiguration. If we send RA to ourself, the
+                 * kernel complains about that. Let's ignore the packet. */
+                return log_radv_errno(ra, SYNTHETIC_ERRNO(EADDRINUSE), "Received RS from the same interface, ignoring.");
 
-        r = radv_send_router(ra, &src, ra->lifetime_usec);
+        r = radv_send_router(ra, &src);
         if (r < 0)
                 return log_radv_errno(ra, r, "Unable to send solicited Router Advertisement to %s, ignoring: %m", IN6_ADDR_TO_STRING(&src));
 
@@ -287,24 +318,35 @@ static int radv_recv(sd_event_source *s, int fd, uint32_t revents, void *userdat
 }
 
 static int radv_timeout(sd_event_source *s, uint64_t usec, void *userdata) {
-        usec_t min_timeout, max_timeout, time_now, timeout;
         sd_radv *ra = ASSERT_PTR(userdata);
+
+        if (sd_radv_send(ra) < 0)
+                (void) sd_radv_stop(ra);
+
+        return 0;
+}
+
+int sd_radv_send(sd_radv *ra) {
+        usec_t min_timeout, max_timeout, time_now, timeout;
         int r;
 
-        assert(s);
-        assert(ra->event);
+        assert_return(ra, -EINVAL);
+        assert_return(ra->event, -EINVAL);
+        assert_return(sd_radv_is_running(ra), -EINVAL);
         assert(router_lifetime_is_valid(ra->lifetime_usec));
 
         r = sd_event_now(ra->event, CLOCK_BOOTTIME, &time_now);
         if (r < 0)
-                goto fail;
+                return r;
 
-        r = radv_send_router(ra, NULL, ra->lifetime_usec);
+        r = radv_send_router(ra, NULL);
         if (r < 0)
-                log_radv_errno(ra, r, "Unable to send Router Advertisement, ignoring: %m");
+                return log_radv_errno(ra, r, "Unable to send Router Advertisement: %m");
+
+        ra->ra_sent++;
 
         /* RFC 4861, Section 6.2.4, sending initial Router Advertisements */
-        if (ra->ra_sent < RADV_MAX_INITIAL_RTR_ADVERTISEMENTS)
+        if (ra->ra_sent <= RADV_MAX_INITIAL_RTR_ADVERTISEMENTS)
                 max_timeout = RADV_MAX_INITIAL_RTR_ADVERT_INTERVAL_USEC;
         else
                 max_timeout = RADV_DEFAULT_MAX_TIMEOUT_USEC;
@@ -328,41 +370,29 @@ static int radv_timeout(sd_event_source *s, uint64_t usec, void *userdata) {
         assert(min_timeout <= max_timeout * 3 / 4);
 
         timeout = min_timeout + random_u64_range(max_timeout - min_timeout);
-        log_radv(ra, "Next Router Advertisement in %s", FORMAT_TIMESPAN(timeout, USEC_PER_SEC));
-
-        r = event_reset_time(ra->event, &ra->timeout_event_source,
-                             CLOCK_BOOTTIME,
-                             usec_add(time_now, timeout), MSEC_PER_SEC,
-                             radv_timeout, ra,
-                             ra->event_priority, "radv-timeout", true);
-        if (r < 0)
-                goto fail;
-
-        ra->ra_sent++;
-
-        return 0;
-
-fail:
-        sd_radv_stop(ra);
-
-        return 0;
+        log_radv(ra, "Sent unsolicited Router Advertisement. Next advertisement will be in %s.",
+                 FORMAT_TIMESPAN(timeout, USEC_PER_SEC));
+
+        return event_reset_time(
+                        ra->event, &ra->timeout_event_source,
+                        CLOCK_BOOTTIME,
+                        usec_add(time_now, timeout), MSEC_PER_SEC,
+                        radv_timeout, ra,
+                        ra->event_priority, "radv-timeout", true);
 }
 
 int sd_radv_stop(sd_radv *ra) {
         int r;
 
-        if (!ra)
-                return 0;
-
-        if (ra->state == RADV_STATE_IDLE)
-                return 0;
+        if (!sd_radv_is_running(ra))
+                return 0; /* Already stopped. */
 
         log_radv(ra, "Stopping IPv6 Router Advertisement daemon");
 
         /* RFC 4861, Section 6.2.5:
          * the router SHOULD transmit one or more (but not more than MAX_FINAL_RTR_ADVERTISEMENTS) final
          * multicast Router Advertisements on the interface with a Router Lifetime field of zero. */
-        r = radv_send_router(ra, NULL, 0);
+        r = radv_send_router_on_stop(ra);
         if (r < 0)
                 log_radv_errno(ra, r, "Unable to send last Router Advertisement with router lifetime set to zero, ignoring: %m");
 
@@ -408,8 +438,8 @@ int sd_radv_start(sd_radv *ra) {
         assert_return(ra->event, -EINVAL);
         assert_return(ra->ifindex > 0, -EINVAL);
 
-        if (ra->state != RADV_STATE_IDLE)
-                return 0;
+        if (sd_radv_is_running(ra))
+                return 0; /* Already started. */
 
         r = radv_setup_recv_event(ra);
         if (r < 0)
@@ -437,13 +467,10 @@ int sd_radv_start(sd_radv *ra) {
 
 int sd_radv_set_ifindex(sd_radv *ra, int ifindex) {
         assert_return(ra, -EINVAL);
+        assert_return(!sd_radv_is_running(ra), -EBUSY);
         assert_return(ifindex > 0, -EINVAL);
 
-        if (ra->state != RADV_STATE_IDLE)
-                return -EBUSY;
-
         ra->ifindex = ifindex;
-
         return 0;
 }
 
@@ -472,11 +499,20 @@ int sd_radv_get_ifname(sd_radv *ra, const char **ret) {
         return 0;
 }
 
-int sd_radv_set_mac(sd_radv *ra, const struct ether_addr *mac_addr) {
+int sd_radv_set_link_local_address(sd_radv *ra, const struct in6_addr *addr) {
         assert_return(ra, -EINVAL);
+        assert_return(!addr || in6_addr_is_link_local(addr), -EINVAL);
+
+        if (addr)
+                ra->ipv6ll = *addr;
+        else
+                zero(ra->ipv6ll);
 
-        if (ra->state != RADV_STATE_IDLE)
-                return -EBUSY;
+        return 0;
+}
+
+int sd_radv_set_mac(sd_radv *ra, const struct ether_addr *mac_addr) {
+        assert_return(ra, -EINVAL);
 
         if (mac_addr)
                 ra->mac_addr = *mac_addr;
@@ -498,23 +534,13 @@ int sd_radv_set_mtu(sd_radv *ra, uint32_t mtu) {
 int sd_radv_set_hop_limit(sd_radv *ra, uint8_t hop_limit) {
         assert_return(ra, -EINVAL);
 
-        if (ra->state != RADV_STATE_IDLE)
-                return -EBUSY;
-
         ra->hop_limit = hop_limit;
-
         return 0;
 }
 
 int sd_radv_set_retransmit(sd_radv *ra, uint64_t usec) {
         assert_return(ra, -EINVAL);
 
-        if (ra->state != RADV_STATE_IDLE)
-                return -EBUSY;
-
-        if (usec > RADV_MAX_RETRANSMIT_USEC)
-                return -EINVAL;
-
         ra->retransmit_usec = usec;
         return 0;
 }
@@ -522,89 +548,55 @@ int sd_radv_set_retransmit(sd_radv *ra, uint64_t usec) {
 int sd_radv_set_router_lifetime(sd_radv *ra, uint64_t usec) {
         assert_return(ra, -EINVAL);
 
-        if (ra->state != RADV_STATE_IDLE)
-                return -EBUSY;
-
         if (!router_lifetime_is_valid(usec))
                 return -EINVAL;
 
-        /* RFC 4191, Section 2.2, "...If the Router Lifetime is zero, the preference value MUST be set
-         * to (00) by the sender..." */
-        if (usec == 0 &&
-            (ra->flags & (0x3 << 3)) != (SD_NDISC_PREFERENCE_MEDIUM << 3))
-                return -EINVAL;
-
         ra->lifetime_usec = usec;
         return 0;
 }
 
-int sd_radv_set_managed_information(sd_radv *ra, int managed) {
+int sd_radv_set_managed_information(sd_radv *ra, int b) {
         assert_return(ra, -EINVAL);
 
-        if (ra->state != RADV_STATE_IDLE)
-                return -EBUSY;
-
-        SET_FLAG(ra->flags, ND_RA_FLAG_MANAGED, managed);
-
+        SET_FLAG(ra->flags, ND_RA_FLAG_MANAGED, b);
         return 0;
 }
 
-int sd_radv_set_other_information(sd_radv *ra, int other) {
+int sd_radv_set_other_information(sd_radv *ra, int b) {
         assert_return(ra, -EINVAL);
 
-        if (ra->state != RADV_STATE_IDLE)
-                return -EBUSY;
-
-        SET_FLAG(ra->flags, ND_RA_FLAG_OTHER, other);
-
+        SET_FLAG(ra->flags, ND_RA_FLAG_OTHER, b);
         return 0;
 }
 
-int sd_radv_set_preference(sd_radv *ra, unsigned preference) {
+int sd_radv_set_preference(sd_radv *ra, uint8_t preference) {
         assert_return(ra, -EINVAL);
         assert_return(IN_SET(preference,
                              SD_NDISC_PREFERENCE_LOW,
                              SD_NDISC_PREFERENCE_MEDIUM,
                              SD_NDISC_PREFERENCE_HIGH), -EINVAL);
 
-        /* RFC 4191, Section 2.2, "...If the Router Lifetime is zero, the preference value MUST be set
-         * to (00) by the sender..." */
-        if (ra->lifetime_usec == 0 && preference != SD_NDISC_PREFERENCE_MEDIUM)
-                return -EINVAL;
-
-        ra->flags = (ra->flags & ~(0x3 << 3)) | (preference << 3);
-
+        ra->preference = preference;
         return 0;
 }
 
 int sd_radv_set_home_agent_information(sd_radv *ra, int home_agent) {
         assert_return(ra, -EINVAL);
 
-        if (ra->state != RADV_STATE_IDLE)
-                return -EBUSY;
-
         SET_FLAG(ra->flags, ND_RA_FLAG_HOME_AGENT, home_agent);
-
         return 0;
 }
 
 int sd_radv_set_home_agent_preference(sd_radv *ra, uint16_t preference) {
         assert_return(ra, -EINVAL);
 
-        if (ra->state != RADV_STATE_IDLE)
-                return -EBUSY;
-
         ra->home_agent.nd_opt_home_agent_info_preference = htobe16(preference);
-
         return 0;
 }
 
 int sd_radv_set_home_agent_lifetime(sd_radv *ra, uint64_t lifetime_usec) {
         assert_return(ra, -EINVAL);
 
-        if (ra->state != RADV_STATE_IDLE)
-                return -EBUSY;
-
         if (lifetime_usec > RADV_HOME_AGENT_MAX_LIFETIME_USEC)
                 return -EINVAL;
 
@@ -672,14 +664,14 @@ int sd_radv_add_prefix(sd_radv *ra, sd_radv_prefix *p) {
                 log_radv(ra, "Added prefix %s", addr_p);
         }
 
-        if (ra->state == RADV_STATE_IDLE)
+        if (!sd_radv_is_running(ra))
                 return 0;
 
         if (ra->ra_sent == 0)
                 return 0;
 
         /* If RAs have already been sent, send an RA immediately to announce the newly-added prefix */
-        r = radv_send_router(ra, NULL, ra->lifetime_usec);
+        r = radv_send_router(ra, NULL);
         if (r < 0)
                 log_radv_errno(ra, r, "Unable to send Router Advertisement for added prefix %s, ignoring: %m", addr_p);
         else
@@ -768,14 +760,14 @@ int sd_radv_add_route_prefix(sd_radv *ra, sd_radv_route_prefix *p) {
                 log_radv(ra, "Added route prefix %s", strna(addr_p));
         }
 
-        if (ra->state == RADV_STATE_IDLE)
+        if (!sd_radv_is_running(ra))
                 return 0;
 
         if (ra->ra_sent == 0)
                 return 0;
 
         /* If RAs have already been sent, send an RA immediately to announce the newly-added route prefix */
-        r = radv_send_router(ra, NULL, ra->lifetime_usec);
+        r = radv_send_router(ra, NULL);
         if (r < 0)
                 log_radv_errno(ra, r, "Unable to send Router Advertisement for added route prefix %s, ignoring: %m",
                                strna(addr_p));
@@ -840,14 +832,14 @@ int sd_radv_add_pref64_prefix(sd_radv *ra, sd_radv_pref64_prefix *p) {
                 log_radv(ra, "Added PREF64 prefix %s", strna(addr_p));
         }
 
-        if (ra->state == RADV_STATE_IDLE)
+        if (!sd_radv_is_running(ra))
                 return 0;
 
         if (ra->ra_sent == 0)
                 return 0;
 
         /* If RAs have already been sent, send an RA immediately to announce the newly-added route prefix */
-        r = radv_send_router(ra, NULL, ra->lifetime_usec);
+        r = radv_send_router(ra, NULL);
         if (r < 0)
                 log_radv_errno(ra, r, "Unable to send Router Advertisement for added PREF64 prefix %s, ignoring: %m",
                                strna(addr_p));
index c5f10cc7955e552d26af75f8d3251bfa5745c1c8..78635ee657670f22b58cc783ced763f6f14ffe40 100644 (file)
@@ -191,12 +191,6 @@ TEST(radv) {
         assert_se(sd_radv_set_preference(ra, SD_NDISC_PREFERENCE_HIGH) >= 0);
         ASSERT_RETURN_EXPECTED_SE(sd_radv_set_preference(ra, ~0) < 0);
 
-        assert_se(sd_radv_set_preference(ra, SD_NDISC_PREFERENCE_HIGH) >= 0);
-        assert_se(sd_radv_set_router_lifetime(ra, 300 * USEC_PER_SEC) >= 0);
-        assert_se(sd_radv_set_router_lifetime(ra, 0) < 0);
-        assert_se(sd_radv_set_preference(ra, SD_NDISC_PREFERENCE_MEDIUM) >= 0);
-        assert_se(sd_radv_set_router_lifetime(ra, 0) >= 0);
-
         ASSERT_RETURN_EXPECTED_SE(sd_radv_set_managed_information(NULL, true) < 0);
         assert_se(sd_radv_set_managed_information(ra, true) >= 0);
         assert_se(sd_radv_set_managed_information(ra, false) >= 0);
@@ -208,7 +202,7 @@ TEST(radv) {
         ASSERT_RETURN_EXPECTED_SE(sd_radv_set_retransmit(NULL, 10 * USEC_PER_MSEC) < 0);
         assert_se(sd_radv_set_retransmit(ra, 10 * USEC_PER_MSEC) >= 0);
         assert_se(sd_radv_set_retransmit(ra, 0) >= 0);
-        assert_se(sd_radv_set_retransmit(ra, usec_add(UINT32_MAX * USEC_PER_MSEC, USEC_PER_MSEC)) < 0);
+        assert_se(sd_radv_set_retransmit(ra, USEC_INFINITY) >= 0);
 
         ASSERT_RETURN_EXPECTED_SE(sd_radv_set_rdnss(NULL, 0, NULL, 0) < 0);
         assert_se(sd_radv_set_rdnss(ra, 0, NULL, 0) >= 0);
@@ -294,8 +288,13 @@ static void verify_message(const uint8_t *buf, size_t len) {
         /* verify only up to known options, rest is not yet implemented */
         for (size_t i = 0, m = MIN(len, sizeof(advertisement)); i < m; i++) {
                 if (test_stopped)
+                        /* on stop, many header fields are zero */
                         switch (i) {
-                        case 6 ... 7: /* router lifetime must be zero on stop. */
+                        case 4: /* hop limit */
+                        case 5: /* flags */
+                        case 6 ... 7: /* router lifetime */
+                        case 8 ... 11: /* reachable time */
+                        case 12 ... 15: /* retrans timer */
                                 assert_se(buf[i] == 0);
                                 continue;
                         }
index d23da4c1515c84cd4e54fea36a117c3478d1f9ad..78b4453462935be22843585844a7bfab1c87623a 100644 (file)
@@ -839,5 +839,7 @@ LIBSYSTEMD_256 {
 global:
         sd_bus_creds_get_pidfd_dup;
         sd_bus_creds_new_from_pidfd;
+        sd_id128_get_invocation_app_specific;
         sd_journal_stream_fd_with_namespace;
+        sd_event_source_get_inotify_path;
 } LIBSYSTEMD_255;
index f4e38d78d085b847334df478042697ecbc4b9023..d05bcf0538df633cf97ad0f1d074ec7c2b25f0d8 100644 (file)
@@ -189,6 +189,9 @@ struct inode_data {
          * iteration. */
         int fd;
 
+        /* The path that the fd points to. The field is optional. */
+        char *path;
+
         /* The inotify "watch descriptor" */
         int wd;
 
index 12aa98059381516a805e4eb5fddae02a61d08726..bd5bd81ac4314bb2dcf2a10bffe497ff5d71625d 100644 (file)
@@ -2272,6 +2272,7 @@ static void event_free_inode_data(
                 assert_se(hashmap_remove(d->inotify_data->inodes, d) == d);
         }
 
+        free(d->path);
         free(d);
 }
 
@@ -2415,7 +2416,7 @@ static int inode_data_realize_watch(sd_event *e, struct inode_data *d) {
 
         wd = inotify_add_watch_fd(d->inotify_data->fd, d->fd, combined_mask);
         if (wd < 0)
-                return -errno;
+                return wd;
 
         if (d->wd < 0) {
                 r = hashmap_put(d->inotify_data->wd, INT_TO_PTR(wd), d);
@@ -2512,6 +2513,15 @@ static int event_add_inotify_fd_internal(
                 }
 
                 LIST_PREPEND(to_close, e->inode_data_to_close_list, inode_data);
+
+                _cleanup_free_ char *path = NULL;
+                r = fd_get_path(inode_data->fd, &path);
+                if (r < 0 && r != -ENOSYS) { /* The path is optional, hence ignore -ENOSYS. */
+                        event_gc_inode_data(e, inode_data);
+                        return r;
+                }
+
+                free_and_replace(inode_data->path, path);
         }
 
         /* Link our event source to the inode data object */
@@ -2637,7 +2647,7 @@ _public_ int sd_event_source_get_io_fd(sd_event_source *s) {
 }
 
 _public_ int sd_event_source_set_io_fd(sd_event_source *s, int fd) {
-        int r;
+        int saved_fd, r;
 
         assert_return(s, -EINVAL);
         assert_return(fd >= 0, -EBADF);
@@ -2647,16 +2657,12 @@ _public_ int sd_event_source_set_io_fd(sd_event_source *s, int fd) {
         if (s->io.fd == fd)
                 return 0;
 
-        if (event_source_is_offline(s)) {
-                s->io.fd = fd;
-                s->io.registered = false;
-        } else {
-                int saved_fd;
+        saved_fd = s->io.fd;
+        s->io.fd = fd;
 
-                saved_fd = s->io.fd;
-                assert(s->io.registered);
+        assert(event_source_is_offline(s) == !s->io.registered);
 
-                s->io.fd = fd;
+        if (s->io.registered) {
                 s->io.registered = false;
 
                 r = source_io_register(s, s->enabled, s->io.events);
@@ -2669,6 +2675,9 @@ _public_ int sd_event_source_set_io_fd(sd_event_source *s, int fd) {
                 (void) epoll_ctl(s->event->epoll_fd, EPOLL_CTL_DEL, saved_fd, NULL);
         }
 
+        if (s->io.owned)
+                safe_close(saved_fd);
+
         return 0;
 }
 
@@ -2798,6 +2807,13 @@ _public_ int sd_event_source_set_priority(sd_event_source *s, int64_t priority)
                         }
 
                         LIST_PREPEND(to_close, s->event->inode_data_to_close_list, new_inode_data);
+
+                        _cleanup_free_ char *path = NULL;
+                        r = fd_get_path(new_inode_data->fd, &path);
+                        if (r < 0 && r != -ENOSYS)
+                                goto fail;
+
+                        free_and_replace(new_inode_data->path, path);
                 }
 
                 /* Move the event source to the new inode data structure */
@@ -3282,13 +3298,29 @@ _public_ int sd_event_source_set_child_process_own(sd_event_source *s, int own)
         return 0;
 }
 
-_public_ int sd_event_source_get_inotify_mask(sd_event_source *s, uint32_t *mask) {
+_public_ int sd_event_source_get_inotify_mask(sd_event_source *s, uint32_t *ret) {
+        assert_return(s, -EINVAL);
+        assert_return(ret, -EINVAL);
+        assert_return(s->type == SOURCE_INOTIFY, -EDOM);
+        assert_return(!event_origin_changed(s->event), -ECHILD);
+
+        *ret = s->inotify.mask;
+        return 0;
+}
+
+_public_ int sd_event_source_get_inotify_path(sd_event_source *s, const char **ret) {
         assert_return(s, -EINVAL);
-        assert_return(mask, -EINVAL);
+        assert_return(ret, -EINVAL);
         assert_return(s->type == SOURCE_INOTIFY, -EDOM);
         assert_return(!event_origin_changed(s->event), -ECHILD);
 
-        *mask = s->inotify.mask;
+        if (!s->inotify.inode_data)
+                return -ESTALE; /* already disconnected. */
+
+        if (!s->inotify.inode_data->path)
+                return -ENOSYS; /* /proc was not mounted? */
+
+        *ret = s->inotify.inode_data->path;
         return 0;
 }
 
index e02de097baa44180489def6a1e23e90ce5349b6c..c617935992245d5426fc97ccafe35ffa7b3a9259 100644 (file)
@@ -396,6 +396,7 @@ struct inotify_context {
         unsigned create_called[CREATE_EVENTS_MAX];
         unsigned create_overflow;
         unsigned n_create_events;
+        const char *path;
 };
 
 static void maybe_exit(sd_event_source *s, struct inotify_context *c) {
@@ -422,10 +423,12 @@ static void maybe_exit(sd_event_source *s, struct inotify_context *c) {
 }
 
 static int inotify_handler(sd_event_source *s, const struct inotify_event *ev, void *userdata) {
-        struct inotify_context *c = userdata;
-        const char *description;
+        struct inotify_context *c = ASSERT_PTR(userdata);
+        const char *path, *description;
         unsigned bit, n;
 
+        assert_se(sd_event_source_get_inotify_path(s, &path) >= 0);
+
         assert_se(sd_event_source_get_description(s, &description) >= 0);
         assert_se(safe_atou(description, &n) >= 0);
 
@@ -433,11 +436,12 @@ static int inotify_handler(sd_event_source *s, const struct inotify_event *ev, v
         bit = 1U << n;
 
         if (ev->mask & IN_Q_OVERFLOW) {
-                log_info("inotify-handler <%s>: overflow", description);
+                log_info("inotify-handler for %s <%s>: overflow", path, description);
                 c->create_overflow |= bit;
         } else if (ev->mask & IN_CREATE) {
+                assert_se(path_equal_or_inode_same(path, c->path, 0));
                 if (streq(ev->name, "sub"))
-                        log_debug("inotify-handler <%s>: create on %s", description, ev->name);
+                        log_debug("inotify-handler for %s <%s>: create on %s", path, description, ev->name);
                 else {
                         unsigned i;
 
@@ -446,7 +450,7 @@ static int inotify_handler(sd_event_source *s, const struct inotify_event *ev, v
                         c->create_called[i] |= bit;
                 }
         } else if (ev->mask & IN_DELETE) {
-                log_info("inotify-handler <%s>: delete of %s", description, ev->name);
+                log_info("inotify-handler for %s <%s>: delete of %s", path, description, ev->name);
                 assert_se(streq(ev->name, "sub"));
         } else
                 assert_not_reached();
@@ -456,16 +460,19 @@ static int inotify_handler(sd_event_source *s, const struct inotify_event *ev, v
 }
 
 static int delete_self_handler(sd_event_source *s, const struct inotify_event *ev, void *userdata) {
-        struct inotify_context *c = userdata;
+        struct inotify_context *c = ASSERT_PTR(userdata);
+        const char *path;
+
+        assert_se(sd_event_source_get_inotify_path(s, &path) >= 0);
 
         if (ev->mask & IN_Q_OVERFLOW) {
-                log_info("delete-self-handler: overflow");
+                log_info("delete-self-handler for %s: overflow", path);
                 c->delete_self_handler_called = true;
         } else if (ev->mask & IN_DELETE_SELF) {
-                log_info("delete-self-handler: delete-self");
+                log_info("delete-self-handler for %s: delete-self", path);
                 c->delete_self_handler_called = true;
         } else if (ev->mask & IN_IGNORED) {
-                log_info("delete-self-handler: ignore");
+                log_info("delete-self-handler for %s: ignore", path);
         } else
                 assert_not_reached();
 
@@ -480,7 +487,7 @@ static void test_inotify_one(unsigned n_create_events) {
                 .n_create_events = n_create_events,
         };
         sd_event *e = NULL;
-        const char *q;
+        const char *q, *pp;
         unsigned i;
 
         log_info("/* %s(%u) */", __func__, n_create_events);
@@ -488,6 +495,7 @@ static void test_inotify_one(unsigned n_create_events) {
         assert_se(sd_event_default(&e) >= 0);
 
         assert_se(mkdtemp_malloc("/tmp/test-inotify-XXXXXX", &p) >= 0);
+        context.path = p;
 
         assert_se(sd_event_add_inotify(e, &a, p, IN_CREATE|IN_ONLYDIR, inotify_handler, &context) >= 0);
         assert_se(sd_event_add_inotify(e, &b, p, IN_CREATE|IN_DELETE|IN_DONT_FOLLOW, inotify_handler, &context) >= 0);
@@ -500,6 +508,13 @@ static void test_inotify_one(unsigned n_create_events) {
         assert_se(sd_event_source_set_description(b, "1") >= 0);
         assert_se(sd_event_source_set_description(c, "2") >= 0);
 
+        assert_se(sd_event_source_get_inotify_path(a, &pp) >= 0);
+        assert_se(path_equal_or_inode_same(pp, p, 0));
+        assert_se(sd_event_source_get_inotify_path(b, &pp) >= 0);
+        assert_se(path_equal_or_inode_same(pp, p, 0));
+        assert_se(sd_event_source_get_inotify_path(b, &pp) >= 0);
+        assert_se(path_equal_or_inode_same(pp, p, 0));
+
         q = strjoina(p, "/sub");
         assert_se(touch(q) >= 0);
         assert_se(sd_event_add_inotify(e, &d, q, IN_DELETE_SELF, delete_self_handler, &context) >= 0);
@@ -828,6 +843,24 @@ TEST(fork) {
         assert_se(r >= 0);
 }
 
+TEST(sd_event_source_set_io_fd) {
+        _cleanup_(sd_event_source_unrefp) sd_event_source *s = NULL;
+        _cleanup_(sd_event_unrefp) sd_event *e = NULL;
+        _cleanup_close_pair_ int pfd_a[2] = EBADF_PAIR, pfd_b[2] = EBADF_PAIR;
+
+        assert_se(sd_event_default(&e) >= 0);
+
+        assert_se(pipe2(pfd_a, O_CLOEXEC) >= 0);
+        assert_se(pipe2(pfd_b, O_CLOEXEC) >= 0);
+
+        assert_se(sd_event_add_io(e, &s, pfd_a[0], EPOLLIN, NULL, INT_TO_PTR(-ENOANO)) >= 0);
+        assert_se(sd_event_source_set_io_fd_own(s, true) >= 0);
+        TAKE_FD(pfd_a[0]);
+
+        assert_se(sd_event_source_set_io_fd(s, pfd_b[0]) >= 0);
+        TAKE_FD(pfd_b[0]);
+}
+
 static int hup_callback(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
         unsigned *c = userdata;
 
index 4336d3f1b709c2a5b43fb7a6e385d453f7feb3aa..62b8aaa347dd2b68e7359b13cc1b65c87fad26f0 100644 (file)
@@ -390,3 +390,16 @@ _public_ int sd_id128_get_boot_app_specific(sd_id128_t app_id, sd_id128_t *ret)
 
         return sd_id128_get_app_specific(id, app_id, ret);
 }
+
+_public_ int sd_id128_get_invocation_app_specific(sd_id128_t app_id, sd_id128_t *ret) {
+        sd_id128_t id;
+        int r;
+
+        assert_return(ret, -EINVAL);
+
+        r = sd_id128_get_invocation(&id);
+        if (r < 0)
+                return r;
+
+        return sd_id128_get_app_specific(id, app_id, ret);
+}
index 2e09e715745a5e42240a0e375d53dacc158bb58a..e852591a89140baa6a5d10e795ff76f7829ebc67 100644 (file)
@@ -163,7 +163,7 @@ static int journal_file_object_verify(JournalFile *f, uint64_t offset, Object *o
                 int r;
 
                 if (le64toh(o->data.entry_offset) == 0)
-                        warning(offset, "Unused data (entry_offset==0)");
+                        debug(offset, "Unused data (entry_offset==0)");
 
                 if ((le64toh(o->data.entry_offset) == 0) ^ (le64toh(o->data.n_entries) == 0)) {
                         error(offset, "Bad n_entries: %"PRIu64, le64toh(o->data.n_entries));
index a4ed3804d8661aac82d03c3c04fb71cacaeae1e6..713cdcc0ec6323625fbde916131ba03f473089c5 100644 (file)
@@ -1973,7 +1973,7 @@ static void directory_watch(sd_journal *j, Directory *m, int fd, uint32_t mask)
 
         m->wd = inotify_add_watch_fd(j->inotify_fd, fd, mask);
         if (m->wd < 0) {
-                log_debug_errno(errno, "Failed to watch journal directory '%s', ignoring: %m", m->path);
+                log_debug_errno(m->wd, "Failed to watch journal directory '%s', ignoring: %m", m->path);
                 return;
         }
 
index 18c2f9618c004859a2085e4607ea2707e99a9b43..ed1918d10385c4503d774af0b1c68c918e0e2c6f 100644 (file)
@@ -74,9 +74,9 @@ static void test_done(const char *t) {
 }
 
 static void append_number(JournalFile *f, int n, const sd_id128_t *boot_id, uint64_t *seqnum, uint64_t *ret_offset) {
-        _cleanup_free_ char *p = NULL, *q = NULL;
+        _cleanup_free_ char *p = NULL, *q = NULL, *s = NULL;
         dual_timestamp ts;
-        struct iovec iovec[2];
+        struct iovec iovec[3];
         size_t n_iov = 0;
 
         dual_timestamp_now(&ts);
@@ -92,6 +92,9 @@ static void append_number(JournalFile *f, int n, const sd_id128_t *boot_id, uint
         assert_se(asprintf(&p, "NUMBER=%d", n) >= 0);
         iovec[n_iov++] = IOVEC_MAKE_STRING(p);
 
+        assert_se(s = strjoin("LESS_THAN_FIVE=%d", yes_no(n < 5)));
+        iovec[n_iov++] = IOVEC_MAKE_STRING(s);
+
         if (boot_id) {
                 assert_se(q = strjoin("_BOOT_ID=", SD_ID128_TO_STRING(*boot_id)));
                 iovec[n_iov++] = IOVEC_MAKE_STRING(q);
@@ -250,6 +253,37 @@ static void mkdtemp_chdir_chattr(char *path) {
         (void) chattr_path(path, FS_NOCOW_FL, FS_NOCOW_FL, NULL);
 }
 
+static void test_cursor(sd_journal *j) {
+        _cleanup_strv_free_ char **cursors = NULL;
+        int r;
+
+        assert_se(sd_journal_seek_head(j) >= 0);
+
+        for (;;) {
+                r = sd_journal_next(j);
+                assert_se(r >= 0);
+                if (r == 0)
+                        break;
+
+                _cleanup_free_ char *cursor = NULL;
+                assert_se(sd_journal_get_cursor(j, &cursor) >= 0);
+                assert_se(sd_journal_test_cursor(j, cursor) > 0);
+                assert_se(strv_consume(&cursors, TAKE_PTR(cursor)) >= 0);
+        }
+
+        STRV_FOREACH(c, cursors) {
+                assert_se(sd_journal_seek_cursor(j, *c) >= 0);
+                assert_se(sd_journal_next(j) >= 0);
+                assert_se(sd_journal_test_cursor(j, *c) > 0);
+        }
+
+        assert_se(sd_journal_seek_head(j) >= 0);
+        STRV_FOREACH(c, cursors) {
+                assert_se(sd_journal_next(j) >= 0);
+                assert_se(sd_journal_test_cursor(j, *c) > 0);
+        }
+}
+
 static void test_skip_one(void (*setup)(void)) {
         char t[] = "/var/tmp/journal-skip-XXXXXX";
         sd_journal *j;
@@ -391,6 +425,30 @@ static void test_skip_one(void (*setup)(void)) {
         test_check_numbers_up(j, 9);
         sd_journal_close(j);
 
+        /* For issue #31516. */
+        assert_ret(sd_journal_open_directory(&j, t, SD_JOURNAL_ASSUME_IMMUTABLE));
+        test_cursor(j);
+        sd_journal_flush_matches(j);
+        assert_se(sd_journal_add_match(j, "LESS_THAN_FIVE=yes", SIZE_MAX) >= 0);
+        test_cursor(j);
+        sd_journal_flush_matches(j);
+        assert_se(sd_journal_add_match(j, "LESS_THAN_FIVE=no", SIZE_MAX) >= 0);
+        test_cursor(j);
+        sd_journal_flush_matches(j);
+        assert_se(sd_journal_add_match(j, "LESS_THAN_FIVE=hoge", SIZE_MAX) >= 0);
+        test_cursor(j);
+        sd_journal_flush_matches(j);
+        assert_se(sd_journal_add_match(j, "LESS_THAN_FIVE=yes", SIZE_MAX) >= 0);
+        assert_se(sd_journal_add_match(j, "NUMBER=3", SIZE_MAX) >= 0);
+        test_cursor(j);
+        sd_journal_flush_matches(j);
+        assert_se(sd_journal_add_match(j, "LESS_THAN_FIVE=yes", SIZE_MAX) >= 0);
+        assert_se(sd_journal_add_match(j, "NUMBER=3", SIZE_MAX) >= 0);
+        assert_se(sd_journal_add_match(j, "NUMBER=4", SIZE_MAX) >= 0);
+        assert_se(sd_journal_add_match(j, "NUMBER=5", SIZE_MAX) >= 0);
+        assert_se(sd_journal_add_match(j, "NUMBER=6", SIZE_MAX) >= 0);
+        test_cursor(j);
+
         test_done(t);
 }
 
index c86856806b591cd2df6e7485a52ba0356a142b36..9325d91549d5e5ac88d169307819145726a4b05f 100644 (file)
@@ -156,7 +156,7 @@ int handle_action_get_enabled_sleep_actions(HandleActionSleepMask mask, char ***
 
         assert(ret);
 
-        FOREACH_ARRAY(i, sleep_actions, ELEMENTSOF(sleep_actions))
+        FOREACH_ELEMENT(i, sleep_actions)
                 if (FLAGS_SET(mask, 1U << *i)) {
                         r = strv_extend(&actions, handle_action_to_string(*i));
                         if (r < 0)
@@ -170,7 +170,7 @@ int handle_action_get_enabled_sleep_actions(HandleActionSleepMask mask, char ***
 HandleAction handle_action_sleep_select(Manager *m) {
         assert(m);
 
-        FOREACH_ARRAY(i, sleep_actions, ELEMENTSOF(sleep_actions)) {
+        FOREACH_ELEMENT(i, sleep_actions) {
                 HandleActionSleepMask action_mask = 1U << *i;
                 const HandleActionData *a;
                 _cleanup_free_ char *load_state = NULL;
index 537cc551cc0b379a0a2d8c980e6c134987068caf..1c2ac81353f4d927c41efcc391b65b99fc95e2f1 100644 (file)
@@ -469,7 +469,7 @@ static int user_update_slice(User *u) {
                 { "IOWeight",   u->user_record->io_weight   },
         };
 
-        FOREACH_ARRAY(st, settings, ELEMENTSOF(settings)) {
+        FOREACH_ELEMENT(st, settings) {
                 if (st->value == UINT64_MAX)
                         continue;
 
index 14810ddb75f0d182c85aa70e215068da726d9e03..a983dff48bb30e2a746efff3c60a127cf44482fb 100644 (file)
@@ -47,6 +47,7 @@ sources = files(
         'networkd-dhcp4.c',
         'networkd-dhcp6-bus.c',
         'networkd-dhcp6.c',
+        'networkd-dns.c',
         'networkd-ipv4acd.c',
         'networkd-ipv4ll.c',
         'networkd-ipv6-proxy-ndp.c',
@@ -65,6 +66,7 @@ sources = files(
         'networkd-network-bus.c',
         'networkd-network.c',
         'networkd-nexthop.c',
+        'networkd-ntp.c',
         'networkd-queue.c',
         'networkd-radv.c',
         'networkd-route.c',
index 3015e2b5c8b03d8bc715bd38a21a13afb90c7806..9f0268d934e472432fd6506a0b377529ccd4e720 100644 (file)
@@ -530,160 +530,6 @@ int config_parse_dhcp_send_hostname(
 
         return 0;
 }
-int config_parse_dhcp_use_dns(
-                const char* unit,
-                const char *filename,
-                unsigned line,
-                const char *section,
-                unsigned section_line,
-                const char *lvalue,
-                int ltype,
-                const char *rvalue,
-                void *data,
-                void *userdata) {
-
-        Network *network = userdata;
-        int r;
-
-        assert(filename);
-        assert(lvalue);
-        assert(IN_SET(ltype, AF_UNSPEC, AF_INET, AF_INET6));
-        assert(rvalue);
-        assert(data);
-
-        r = parse_boolean(rvalue);
-        if (r < 0) {
-                log_syntax(unit, LOG_WARNING, filename, line, r,
-                           "Failed to parse UseDNS=%s, ignoring assignment: %m", rvalue);
-                return 0;
-        }
-
-        switch (ltype) {
-        case AF_INET:
-                network->dhcp_use_dns = r;
-                network->dhcp_use_dns_set = true;
-                break;
-        case AF_INET6:
-                network->dhcp6_use_dns = r;
-                network->dhcp6_use_dns_set = true;
-                break;
-        case AF_UNSPEC:
-                /* For backward compatibility. */
-                if (!network->dhcp_use_dns_set)
-                        network->dhcp_use_dns = r;
-                if (!network->dhcp6_use_dns_set)
-                        network->dhcp6_use_dns = r;
-                break;
-        default:
-                assert_not_reached();
-        }
-
-        return 0;
-}
-
-int config_parse_dhcp_use_domains(
-                const char* unit,
-                const char *filename,
-                unsigned line,
-                const char *section,
-                unsigned section_line,
-                const char *lvalue,
-                int ltype,
-                const char *rvalue,
-                void *data,
-                void *userdata) {
-
-        Network *network = userdata;
-        DHCPUseDomains d;
-
-        assert(filename);
-        assert(lvalue);
-        assert(IN_SET(ltype, AF_UNSPEC, AF_INET, AF_INET6));
-        assert(rvalue);
-        assert(data);
-
-        d = dhcp_use_domains_from_string(rvalue);
-        if (d < 0) {
-                log_syntax(unit, LOG_WARNING, filename, line, d,
-                           "Failed to parse %s=%s, ignoring assignment: %m", lvalue, rvalue);
-                return 0;
-        }
-
-        switch (ltype) {
-        case AF_INET:
-                network->dhcp_use_domains = d;
-                network->dhcp_use_domains_set = true;
-                break;
-        case AF_INET6:
-                network->dhcp6_use_domains = d;
-                network->dhcp6_use_domains_set = true;
-                break;
-        case AF_UNSPEC:
-                /* For backward compatibility. */
-                if (!network->dhcp_use_domains_set)
-                        network->dhcp_use_domains = d;
-                if (!network->dhcp6_use_domains_set)
-                        network->dhcp6_use_domains = d;
-                break;
-        default:
-                assert_not_reached();
-        }
-
-        return 0;
-}
-
-DEFINE_CONFIG_PARSE_ENUM(config_parse_default_dhcp_use_domains, dhcp_use_domains, DHCPUseDomains, "Failed to parse UseDomains=")
-
-int config_parse_dhcp_use_ntp(
-                const char* unit,
-                const char *filename,
-                unsigned line,
-                const char *section,
-                unsigned section_line,
-                const char *lvalue,
-                int ltype,
-                const char *rvalue,
-                void *data,
-                void *userdata) {
-
-        Network *network = userdata;
-        int r;
-
-        assert(filename);
-        assert(lvalue);
-        assert(IN_SET(ltype, AF_UNSPEC, AF_INET, AF_INET6));
-        assert(rvalue);
-        assert(data);
-
-        r = parse_boolean(rvalue);
-        if (r < 0) {
-                log_syntax(unit, LOG_WARNING, filename, line, r,
-                           "Failed to parse UseNTP=%s, ignoring assignment: %m", rvalue);
-                return 0;
-        }
-
-        switch (ltype) {
-        case AF_INET:
-                network->dhcp_use_ntp = r;
-                network->dhcp_use_ntp_set = true;
-                break;
-        case AF_INET6:
-                network->dhcp6_use_ntp = r;
-                network->dhcp6_use_ntp_set = true;
-                break;
-        case AF_UNSPEC:
-                /* For backward compatibility. */
-                if (!network->dhcp_use_ntp_set)
-                        network->dhcp_use_ntp = r;
-                if (!network->dhcp6_use_ntp_set)
-                        network->dhcp6_use_ntp = r;
-                break;
-        default:
-                assert_not_reached();
-        }
-
-        return 0;
-}
 
 int config_parse_dhcp_or_ra_route_table(
                 const char *unit,
@@ -1139,14 +985,6 @@ int config_parse_dhcp_request_options(
         }
 }
 
-static const char* const dhcp_use_domains_table[_DHCP_USE_DOMAINS_MAX] = {
-        [DHCP_USE_DOMAINS_NO] = "no",
-        [DHCP_USE_DOMAINS_ROUTE] = "route",
-        [DHCP_USE_DOMAINS_YES] = "yes",
-};
-
-DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(dhcp_use_domains, DHCPUseDomains, DHCP_USE_DOMAINS_YES);
-
 static const char * const dhcp_option_data_type_table[_DHCP_OPTION_DATA_MAX] = {
         [DHCP_OPTION_DATA_UINT8]       = "uint8",
         [DHCP_OPTION_DATA_UINT16]      = "uint16",
index 53e3e12f53c39487f912d8c7bc26374281d655f1..3390d7dcff1bda903007029d2540c03bc1653be6 100644 (file)
@@ -24,14 +24,6 @@ typedef struct Link Link;
 typedef struct Manager Manager;
 typedef struct Network Network;
 
-typedef enum DHCPUseDomains {
-        DHCP_USE_DOMAINS_NO,
-        DHCP_USE_DOMAINS_YES,
-        DHCP_USE_DOMAINS_ROUTE,
-        _DHCP_USE_DOMAINS_MAX,
-        _DHCP_USE_DOMAINS_INVALID = -EINVAL,
-} DHCPUseDomains;
-
 typedef enum DHCPOptionDataType {
         DHCP_OPTION_DATA_UINT8,
         DHCP_OPTION_DATA_UINT16,
@@ -87,9 +79,6 @@ static inline bool in6_prefix_is_filtered(const struct in6_addr *prefix, uint8_t
 
 int link_get_captive_portal(Link *link, const char **ret);
 
-const char* dhcp_use_domains_to_string(DHCPUseDomains p) _const_;
-DHCPUseDomains dhcp_use_domains_from_string(const char *s) _pure_;
-
 const char *dhcp_option_data_type_to_string(DHCPOptionDataType d) _const_;
 DHCPOptionDataType dhcp_option_data_type_from_string(const char *d) _pure_;
 
@@ -97,10 +86,6 @@ CONFIG_PARSER_PROTOTYPE(config_parse_dhcp);
 CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_route_metric);
 CONFIG_PARSER_PROTOTYPE(config_parse_ndisc_route_metric);
 CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_send_hostname);
-CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_use_dns);
-CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_use_domains);
-CONFIG_PARSER_PROTOTYPE(config_parse_default_dhcp_use_domains);
-CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_use_ntp);
 CONFIG_PARSER_PROTOTYPE(config_parse_iaid);
 CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_or_ra_route_table);
 CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_user_or_vendor_class);
index b1953545eda769e3885fea87b9056f7fd5bc7838..c35102af74a5ff97c4cecd3c872b0183e132cb58 100644 (file)
@@ -18,6 +18,7 @@
 #include "networkd-link.h"
 #include "networkd-manager.h"
 #include "networkd-network.h"
+#include "networkd-ntp.h"
 #include "networkd-queue.h"
 #include "networkd-route-util.h"
 #include "parse-util.h"
@@ -342,7 +343,7 @@ static int link_push_uplink_to_dhcp_server(
                         addresses[n_addresses++] = ia;
                 }
 
-                use_dhcp_lease_data = link->network->dhcp_use_dns;
+                use_dhcp_lease_data = link_get_use_dns(link, NETWORK_CONFIG_SOURCE_DHCP4);
                 break;
 
         case SD_DHCP_LEASE_NTP: {
@@ -365,7 +366,7 @@ static int link_push_uplink_to_dhcp_server(
                         addresses[n_addresses++] = ia.in;
                 }
 
-                use_dhcp_lease_data = link->network->dhcp_use_ntp;
+                use_dhcp_lease_data = link_get_use_ntp(link, NETWORK_CONFIG_SOURCE_DHCP4);
                 break;
         }
 
index 71b132f94f5070817ba6dc400c32929b87e38845..cb470ec4e11ed6a9d3142955b2de41bc0f3ffea1 100644 (file)
@@ -20,6 +20,7 @@
 #include "networkd-manager.h"
 #include "networkd-network.h"
 #include "networkd-nexthop.h"
+#include "networkd-ntp.h"
 #include "networkd-queue.h"
 #include "networkd-route.h"
 #include "networkd-setlink.h"
@@ -728,7 +729,7 @@ static int dhcp4_request_routes_to_dns(Link *link) {
         assert(link->dhcp_lease);
         assert(link->network);
 
-        if (!link->network->dhcp_use_dns ||
+        if (!link_get_use_dns(link, NETWORK_CONFIG_SOURCE_DHCP4) ||
             !link->network->dhcp_routes_to_dns)
                 return 0;
 
@@ -749,7 +750,7 @@ static int dhcp4_request_routes_to_ntp(Link *link) {
         assert(link->dhcp_lease);
         assert(link->network);
 
-        if (!link->network->dhcp_use_ntp ||
+        if (!link_get_use_ntp(link, NETWORK_CONFIG_SOURCE_DHCP4) ||
             !link->network->dhcp_routes_to_ntp)
                 return 0;
 
@@ -1540,13 +1541,13 @@ static int dhcp4_configure(Link *link) {
                                 return log_link_debug_errno(link, r, "DHCPv4 CLIENT: Failed to set request flag for classless static route: %m");
                 }
 
-                if (link->network->dhcp_use_domains != DHCP_USE_DOMAINS_NO) {
+                if (link_get_use_domains(link, NETWORK_CONFIG_SOURCE_DHCP4) > 0) {
                         r = sd_dhcp_client_set_request_option(link->dhcp_client, SD_DHCP_OPTION_DOMAIN_SEARCH);
                         if (r < 0)
                                 return log_link_debug_errno(link, r, "DHCPv4 CLIENT: Failed to set request flag for domain search list: %m");
                 }
 
-                if (link->network->dhcp_use_ntp) {
+                if (link_get_use_ntp(link, NETWORK_CONFIG_SOURCE_DHCP4)) {
                         r = sd_dhcp_client_set_request_option(link->dhcp_client, SD_DHCP_OPTION_NTP_SERVER);
                         if (r < 0)
                                 return log_link_debug_errno(link, r, "DHCPv4 CLIENT: Failed to set request flag for NTP server: %m");
index cf643194d58f55bab25792aa7339ab409143665f..852987bd865b4ad382ad0071ec5d9acabe6390c4 100644 (file)
@@ -14,6 +14,7 @@
 #include "networkd-dhcp6.h"
 #include "networkd-link.h"
 #include "networkd-manager.h"
+#include "networkd-ntp.h"
 #include "networkd-queue.h"
 #include "networkd-route.h"
 #include "networkd-state-file.h"
@@ -633,13 +634,13 @@ static int dhcp6_configure(Link *link) {
                         return log_link_debug_errno(link, r, "DHCPv6 CLIENT: Failed to set MUD URL: %m");
         }
 
-        if (link->network->dhcp6_use_dns) {
+        if (link_get_use_dns(link, NETWORK_CONFIG_SOURCE_DHCP6)) {
                 r = sd_dhcp6_client_set_request_option(client, SD_DHCP6_OPTION_DNS_SERVER);
                 if (r < 0)
                         return log_link_debug_errno(link, r, "DHCPv6 CLIENT: Failed to request DNS servers: %m");
         }
 
-        if (link->network->dhcp6_use_domains > 0) {
+        if (link_get_use_domains(link, NETWORK_CONFIG_SOURCE_DHCP6) > 0) {
                 r = sd_dhcp6_client_set_request_option(client, SD_DHCP6_OPTION_DOMAIN);
                 if (r < 0)
                         return log_link_debug_errno(link, r, "DHCPv6 CLIENT: Failed to request domains: %m");
@@ -651,7 +652,7 @@ static int dhcp6_configure(Link *link) {
                         return log_link_debug_errno(link, r, "DHCPv6 CLIENT: Failed to request captive portal: %m");
         }
 
-        if (link->network->dhcp6_use_ntp) {
+        if (link_get_use_ntp(link, NETWORK_CONFIG_SOURCE_DHCP6)) {
                 r = sd_dhcp6_client_set_request_option(client, SD_DHCP6_OPTION_NTP_SERVER);
                 if (r < 0)
                         return log_link_debug_errno(link, r, "DHCPv6 CLIENT: Failed to request NTP servers: %m");
diff --git a/src/network/networkd-dns.c b/src/network/networkd-dns.c
new file mode 100644 (file)
index 0000000..7078419
--- /dev/null
@@ -0,0 +1,294 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "dns-domain.h"
+#include "hostname-util.h"
+#include "networkd-dns.h"
+#include "networkd-manager.h"
+#include "networkd-network.h"
+#include "parse-util.h"
+#include "string-table.h"
+
+UseDomains link_get_use_domains(Link *link, NetworkConfigSource proto) {
+        UseDomains n, c, m;
+
+        assert(link);
+        assert(link->manager);
+
+        if (!link->network)
+                return USE_DOMAINS_NO;
+
+        switch (proto) {
+        case NETWORK_CONFIG_SOURCE_DHCP4:
+                n = link->network->dhcp_use_domains;
+                c = link->network->compat_dhcp_use_domains;
+                m = link->manager->dhcp_use_domains;
+                break;
+        case NETWORK_CONFIG_SOURCE_DHCP6:
+                n = link->network->dhcp6_use_domains;
+                c = link->network->compat_dhcp_use_domains;
+                m = link->manager->dhcp6_use_domains;
+                break;
+        case NETWORK_CONFIG_SOURCE_NDISC:
+                n = link->network->ndisc_use_domains;
+                c = _USE_DOMAINS_INVALID;
+                m = link->manager->ndisc_use_domains;
+                break;
+        default:
+                assert_not_reached();
+        }
+
+        /* If per-network and per-protocol setting is specified, use it. */
+        if (n >= 0)
+                return n;
+
+        /* If compat setting is specified, use it. */
+        if (c >= 0)
+                return c;
+
+        /* If per-network but protocol-independent setting is specified, use it. */
+        if (link->network->use_domains >= 0)
+                return link->network->use_domains;
+
+        /* If global per-protocol setting is specified, use it. */
+        if (m >= 0)
+                return m;
+
+        /* If none of them are specified, use the global protocol-independent value. */
+        return link->manager->use_domains;
+}
+
+bool link_get_use_dns(Link *link, NetworkConfigSource proto) {
+        int n, c;
+
+        assert(link);
+
+        if (!link->network)
+                return false;
+
+        switch (proto) {
+        case NETWORK_CONFIG_SOURCE_DHCP4:
+                n = link->network->dhcp_use_dns;
+                c = link->network->compat_dhcp_use_dns;
+                break;
+        case NETWORK_CONFIG_SOURCE_DHCP6:
+                n = link->network->dhcp6_use_dns;
+                c = link->network->compat_dhcp_use_dns;
+                break;
+        case NETWORK_CONFIG_SOURCE_NDISC:
+                n = link->network->ndisc_use_dns;
+                c = -1;
+                break;
+        default:
+                assert_not_reached();
+        }
+
+        /* If per-network and per-protocol setting is specified, use it. */
+        if (n >= 0)
+                return n;
+
+        /* If compat setting is specified, use it. */
+        if (c >= 0)
+                return c;
+
+        /* Otherwise, defaults to yes. */
+        return true;
+}
+
+int config_parse_domains(
+                const char *unit,
+                const char *filename,
+                unsigned line,
+                const char *section,
+                unsigned section_line,
+                const char *lvalue,
+                int ltype,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
+
+        Network *n = ASSERT_PTR(userdata);
+        int r;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+
+        if (isempty(rvalue)) {
+                n->search_domains = ordered_set_free(n->search_domains);
+                n->route_domains = ordered_set_free(n->route_domains);
+                return 0;
+        }
+
+        for (const char *p = rvalue;;) {
+                _cleanup_free_ char *w = NULL, *normalized = NULL;
+                const char *domain;
+                bool is_route;
+
+                r = extract_first_word(&p, &w, NULL, 0);
+                if (r == -ENOMEM)
+                        return log_oom();
+                if (r < 0) {
+                        log_syntax(unit, LOG_WARNING, filename, line, r,
+                                   "Failed to extract search or route domain, ignoring: %s", rvalue);
+                        return 0;
+                }
+                if (r == 0)
+                        return 0;
+
+                is_route = w[0] == '~';
+                domain = is_route ? w + 1 : w;
+
+                if (dns_name_is_root(domain) || streq(domain, "*")) {
+                        /* If the root domain appears as is, or the special token "*" is found, we'll
+                         * consider this as routing domain, unconditionally. */
+                        is_route = true;
+                        domain = "."; /* make sure we don't allow empty strings, thus write the root
+                                       * domain as "." */
+                } else {
+                        r = dns_name_normalize(domain, 0, &normalized);
+                        if (r < 0) {
+                                log_syntax(unit, LOG_WARNING, filename, line, r,
+                                           "'%s' is not a valid domain name, ignoring.", domain);
+                                continue;
+                        }
+
+                        domain = normalized;
+
+                        if (is_localhost(domain)) {
+                                log_syntax(unit, LOG_WARNING, filename, line, 0,
+                                           "'localhost' domain may not be configured as search or route domain, ignoring assignment: %s",
+                                           domain);
+                                continue;
+                        }
+                }
+
+                OrderedSet **set = is_route ? &n->route_domains : &n->search_domains;
+                r = ordered_set_put_strdup(set, domain);
+                if (r == -EEXIST)
+                        continue;
+                if (r < 0)
+                        return log_oom();
+        }
+}
+
+int config_parse_dns(
+                const char *unit,
+                const char *filename,
+                unsigned line,
+                const char *section,
+                unsigned section_line,
+                const char *lvalue,
+                int ltype,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
+
+        Network *n = ASSERT_PTR(userdata);
+        int r;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+
+        if (isempty(rvalue)) {
+                for (unsigned i = 0; i < n->n_dns; i++)
+                        in_addr_full_free(n->dns[i]);
+                n->dns = mfree(n->dns);
+                n->n_dns = 0;
+                return 0;
+        }
+
+        for (const char *p = rvalue;;) {
+                _cleanup_(in_addr_full_freep) struct in_addr_full *dns = NULL;
+                _cleanup_free_ char *w = NULL;
+                struct in_addr_full **m;
+
+                r = extract_first_word(&p, &w, NULL, 0);
+                if (r == -ENOMEM)
+                        return log_oom();
+                if (r < 0) {
+                        log_syntax(unit, LOG_WARNING, filename, line, r,
+                                   "Invalid syntax, ignoring: %s", rvalue);
+                        return 0;
+                }
+                if (r == 0)
+                        return 0;
+
+                r = in_addr_full_new_from_string(w, &dns);
+                if (r < 0) {
+                        log_syntax(unit, LOG_WARNING, filename, line, r,
+                                   "Failed to parse dns server address, ignoring: %s", w);
+                        continue;
+                }
+
+                if (IN_SET(dns->port, 53, 853))
+                        dns->port = 0;
+
+                m = reallocarray(n->dns, n->n_dns + 1, sizeof(struct in_addr_full*));
+                if (!m)
+                        return log_oom();
+
+                m[n->n_dns++] = TAKE_PTR(dns);
+                n->dns = m;
+        }
+}
+
+int config_parse_dnssec_negative_trust_anchors(
+                const char *unit,
+                const char *filename,
+                unsigned line,
+                const char *section,
+                unsigned section_line,
+                const char *lvalue,
+                int ltype,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
+
+        Set **nta = ASSERT_PTR(data);
+        int r;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+
+        if (isempty(rvalue)) {
+                *nta = set_free_free(*nta);
+                return 0;
+        }
+
+        for (const char *p = rvalue;;) {
+                _cleanup_free_ char *w = NULL;
+
+                r = extract_first_word(&p, &w, NULL, 0);
+                if (r == -ENOMEM)
+                        return log_oom();
+                if (r < 0) {
+                        log_syntax(unit, LOG_WARNING, filename, line, r,
+                                   "Failed to extract negative trust anchor domain, ignoring: %s", rvalue);
+                        return 0;
+                }
+                if (r == 0)
+                        return 0;
+
+                r = dns_name_is_valid(w);
+                if (r <= 0) {
+                        log_syntax(unit, LOG_WARNING, filename, line, r,
+                                   "%s is not a valid domain name, ignoring.", w);
+                        continue;
+                }
+
+                r = set_ensure_consume(nta, &dns_name_hash_ops, TAKE_PTR(w));
+                if (r < 0)
+                        return log_oom();
+        }
+}
+
+static const char* const use_domains_table[_USE_DOMAINS_MAX] = {
+        [USE_DOMAINS_NO]    = "no",
+        [USE_DOMAINS_ROUTE] = "route",
+        [USE_DOMAINS_YES]   = "yes",
+};
+
+DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(use_domains, UseDomains, USE_DOMAINS_YES);
+DEFINE_CONFIG_PARSE_ENUM(config_parse_use_domains, use_domains, UseDomains, "Failed to parse UseDomains=")
diff --git a/src/network/networkd-dns.h b/src/network/networkd-dns.h
new file mode 100644 (file)
index 0000000..915cb32
--- /dev/null
@@ -0,0 +1,28 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include "conf-parser.h"
+#include "macro.h"
+#include "networkd-util.h"
+
+typedef struct Link Link;
+
+typedef enum UseDomains {
+        USE_DOMAINS_NO,
+        USE_DOMAINS_YES,
+        USE_DOMAINS_ROUTE,
+        _USE_DOMAINS_MAX,
+        _USE_DOMAINS_INVALID = -EINVAL,
+} UseDomains;
+
+UseDomains link_get_use_domains(Link *link, NetworkConfigSource proto);
+bool link_get_use_dns(Link *link, NetworkConfigSource proto);
+
+const char* use_domains_to_string(UseDomains p) _const_;
+UseDomains use_domains_from_string(const char *s) _pure_;
+
+CONFIG_PARSER_PROTOTYPE(config_parse_domains);
+CONFIG_PARSER_PROTOTYPE(config_parse_dns);
+CONFIG_PARSER_PROTOTYPE(config_parse_dnssec_negative_trust_anchors);
+CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_use_dns);
+CONFIG_PARSER_PROTOTYPE(config_parse_use_domains);
index 0c23b4ba8202442fc79772c5cc4ffc4948ef41b1..f02dfd7a05f9a87deb876cd2d720a89c88587824 100644 (file)
@@ -7,6 +7,7 @@ _Pragma("GCC diagnostic ignored \"-Wimplicit-fallthrough\"")
 #include "conf-parser.h"
 #include "networkd-conf.h"
 #include "networkd-dhcp-common.h"
+#include "networkd-dns.h"
 #include "networkd-manager.h"
 #include "networkd-route-util.h"
 %}
@@ -30,10 +31,12 @@ Network.RouteTable,                      config_parse_route_table_names,
 Network.IPv4Forwarding,                  config_parse_tristate,                  0,          offsetof(Manager, ip_forwarding[0])
 Network.IPv6Forwarding,                  config_parse_tristate,                  0,          offsetof(Manager, ip_forwarding[1])
 Network.IPv6PrivacyExtensions,           config_parse_ipv6_privacy_extensions,   0,          offsetof(Manager, ipv6_privacy_extensions)
-DHCPv4.UseDomains,                       config_parse_default_dhcp_use_domains,  0,          offsetof(Manager, dhcp_use_domains)
+Network.UseDomains,                      config_parse_use_domains,               0,          offsetof(Manager, use_domains)
+IPv6AcceptRA.UseDomains,                 config_parse_use_domains,               0,          offsetof(Manager, ndisc_use_domains)
+DHCPv4.UseDomains,                       config_parse_use_domains,               0,          offsetof(Manager, dhcp_use_domains)
 DHCPv4.DUIDType,                         config_parse_duid_type,                 0,          offsetof(Manager, dhcp_duid)
 DHCPv4.DUIDRawData,                      config_parse_duid_rawdata,              0,          offsetof(Manager, dhcp_duid)
-DHCPv6.UseDomains,                       config_parse_default_dhcp_use_domains,  0,          offsetof(Manager, dhcp6_use_domains)
+DHCPv6.UseDomains,                       config_parse_use_domains,               0,          offsetof(Manager, dhcp6_use_domains)
 DHCPv6.DUIDType,                         config_parse_duid_type,                 0,          offsetof(Manager, dhcp6_duid)
 DHCPv6.DUIDRawData,                      config_parse_duid_rawdata,              0,          offsetof(Manager, dhcp6_duid)
 DHCPServer.PersistLeases,                config_parse_bool,                      0,          offsetof(Manager, dhcp_server_persist_leases)
index 88df71b0536dba3a58b831f0f86db048a3b09cdf..fb9f492be49b5713a95c49f530f92ae9ef43ad3c 100644 (file)
@@ -17,6 +17,7 @@
 #include "networkd-neighbor.h"
 #include "networkd-network.h"
 #include "networkd-nexthop.h"
+#include "networkd-ntp.h"
 #include "networkd-route-util.h"
 #include "networkd-route.h"
 #include "networkd-routing-policy-rule.h"
@@ -448,7 +449,7 @@ static int dns_append_json(Link *link, JsonVariant **v) {
                                 return r;
                 }
 
-                if (link->dhcp_lease && link->network->dhcp_use_dns) {
+                if (link->dhcp_lease && link_get_use_dns(link, NETWORK_CONFIG_SOURCE_DHCP4)) {
                         const struct in_addr *dns;
                         union in_addr_union s;
                         int n_dns;
@@ -469,7 +470,7 @@ static int dns_append_json(Link *link, JsonVariant **v) {
                         }
                 }
 
-                if (link->dhcp6_lease && link->network->dhcp6_use_dns) {
+                if (link->dhcp6_lease && link_get_use_dns(link, NETWORK_CONFIG_SOURCE_DHCP6)) {
                         const struct in6_addr *dns;
                         union in_addr_union s;
                         int n_dns;
@@ -490,7 +491,7 @@ static int dns_append_json(Link *link, JsonVariant **v) {
                         }
                 }
 
-                if (link->network->ndisc_use_dns) {
+                if (link_get_use_dns(link, NETWORK_CONFIG_SOURCE_NDISC)) {
                         NDiscRDNSS *a;
 
                         SET_FOREACH(a, link->ndisc_rdnss) {
@@ -564,7 +565,7 @@ static int ntp_append_json(Link *link, JsonVariant **v) {
         }
 
         if (!link->ntp) {
-                if (link->dhcp_lease && link->network->dhcp_use_ntp) {
+                if (link->dhcp_lease && link_get_use_ntp(link, NETWORK_CONFIG_SOURCE_DHCP4)) {
                         const struct in_addr *ntp;
                         union in_addr_union s;
                         int n_ntp;
@@ -585,7 +586,7 @@ static int ntp_append_json(Link *link, JsonVariant **v) {
                         }
                 }
 
-                if (link->dhcp6_lease && link->network->dhcp6_use_ntp) {
+                if (link->dhcp6_lease && link_get_use_ntp(link, NETWORK_CONFIG_SOURCE_DHCP6)) {
                         const struct in6_addr *ntp_addr;
                         union in_addr_union s;
                         char **ntp_fqdn;
@@ -671,7 +672,7 @@ static int domain_append_json(int family, const char *domain, NetworkConfigSourc
 static int domains_append_json(Link *link, bool is_route, JsonVariant **v) {
         _cleanup_(json_variant_unrefp) JsonVariant *array = NULL;
         OrderedSet *link_domains, *network_domains;
-        DHCPUseDomains use_domains;
+        UseDomains use_domains;
         union in_addr_union s;
         char **domains;
         const char *domain;
@@ -685,7 +686,7 @@ static int domains_append_json(Link *link, bool is_route, JsonVariant **v) {
 
         link_domains = is_route ? link->route_domains : link->search_domains;
         network_domains = is_route ? link->network->route_domains : link->network->search_domains;
-        use_domains = is_route ? DHCP_USE_DOMAINS_ROUTE : DHCP_USE_DOMAINS_YES;
+        use_domains = is_route ? USE_DOMAINS_ROUTE : USE_DOMAINS_YES;
 
         ORDERED_SET_FOREACH(domain, link_domains ?: network_domains) {
                 r = domain_append_json(AF_UNSPEC, domain,
@@ -697,7 +698,7 @@ static int domains_append_json(Link *link, bool is_route, JsonVariant **v) {
 
         if (!link_domains) {
                 if (link->dhcp_lease &&
-                    link->network->dhcp_use_domains == use_domains) {
+                    link_get_use_domains(link, NETWORK_CONFIG_SOURCE_DHCP4) == use_domains) {
                         r = sd_dhcp_lease_get_server_identifier(link->dhcp_lease, &s.in);
                         if (r < 0)
                                 return r;
@@ -717,7 +718,7 @@ static int domains_append_json(Link *link, bool is_route, JsonVariant **v) {
                 }
 
                 if (link->dhcp6_lease &&
-                    link->network->dhcp6_use_domains == use_domains) {
+                    link_get_use_domains(link, NETWORK_CONFIG_SOURCE_DHCP6) == use_domains) {
                         r = sd_dhcp6_lease_get_server_address(link->dhcp6_lease, &s.in6);
                         if (r < 0)
                                 return r;
@@ -730,7 +731,7 @@ static int domains_append_json(Link *link, bool is_route, JsonVariant **v) {
                                 }
                 }
 
-                if (link->network->ndisc_use_domains == use_domains) {
+                if (link_get_use_domains(link, NETWORK_CONFIG_SOURCE_NDISC) == use_domains) {
                         NDiscDNSSL *a;
 
                         SET_FOREACH(a, link->ndisc_dnssl) {
index 8e8b27d78db00cdfa520c03ff2ed7e380b1180f7..4ec4550caf3cb1f035f101f15dcbdeb96cfb96f8 100644 (file)
@@ -599,6 +599,9 @@ int manager_new(Manager **ret, bool test_mode) {
                 .manage_foreign_nexthops = true,
                 .ethtool_fd = -EBADF,
                 .persistent_storage_fd = persistent_storage_open(),
+                .dhcp_use_domains = _USE_DOMAINS_INVALID,
+                .dhcp6_use_domains = _USE_DOMAINS_INVALID,
+                .ndisc_use_domains = _USE_DOMAINS_INVALID,
                 .dhcp_duid.type = DUID_TYPE_EN,
                 .dhcp6_duid.type = DUID_TYPE_EN,
                 .duid_product_uuid.type = DUID_TYPE_UUID,
index 9f621b748648dfc9ae53fb012193df1be099bc00..c14a98fb9753e13e10d3bf199b76da166404e973 100644 (file)
@@ -64,8 +64,10 @@ struct Manager {
         OrderedSet *address_pools;
         Set *dhcp_pd_subnet_ids;
 
-        DHCPUseDomains dhcp_use_domains;
-        DHCPUseDomains dhcp6_use_domains;
+        UseDomains use_domains; /* default for all protocols */
+        UseDomains dhcp_use_domains;
+        UseDomains dhcp6_use_domains;
+        UseDomains ndisc_use_domains;
 
         DUID dhcp_duid;
         DUID dhcp6_duid;
index 7e74712680fde39a50baefd9ace9acfee0465a9e..92ff7c71f36e2e3dd0de325ff0a03a0a57812a27 100644 (file)
@@ -53,9 +53,14 @@ bool link_ndisc_enabled(Link *link) {
         if (!link_may_have_ipv6ll(link, /* check_multicast = */ true))
                 return false;
 
+        /* Honor explicitly specified value. */
         if (link->network->ndisc >= 0)
                 return link->network->ndisc;
 
+        /* Disable if RADV is enabled. */
+        if (link_radv_enabled(link))
+                return false;
+
         /* Accept RAs if IPv6 forwarding is disabled, and ignore RAs if IPv6 forwarding is enabled. */
         int t = link_get_ip_forwarding(link, AF_INET6);
         if (t >= 0)
@@ -1404,7 +1409,7 @@ static int ndisc_router_process_rdnss(Link *link, sd_ndisc_router *rt) {
         assert(link->network);
         assert(rt);
 
-        if (!link->network->ndisc_use_dns)
+        if (!link_get_use_dns(link, NETWORK_CONFIG_SOURCE_NDISC))
                 return 0;
 
         r = sd_ndisc_router_get_sender_address(rt, &router);
@@ -1496,7 +1501,7 @@ static int ndisc_router_process_dnssl(Link *link, sd_ndisc_router *rt) {
         assert(link->network);
         assert(rt);
 
-        if (link->network->ndisc_use_domains == DHCP_USE_DOMAINS_NO)
+        if (link_get_use_domains(link, NETWORK_CONFIG_SOURCE_NDISC) <= 0)
                 return 0;
 
         r = sd_ndisc_router_get_sender_address(rt, &router);
@@ -2478,7 +2483,5 @@ static const char* const ndisc_start_dhcp6_client_table[_IPV6_ACCEPT_RA_START_DH
 
 DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING_WITH_BOOLEAN(ndisc_start_dhcp6_client, IPv6AcceptRAStartDHCP6Client, IPV6_ACCEPT_RA_START_DHCP6_CLIENT_YES);
 
-DEFINE_CONFIG_PARSE_ENUM(config_parse_ndisc_use_domains, dhcp_use_domains, DHCPUseDomains,
-                         "Failed to parse UseDomains= setting");
 DEFINE_CONFIG_PARSE_ENUM(config_parse_ndisc_start_dhcp6_client, ndisc_start_dhcp6_client, IPv6AcceptRAStartDHCP6Client,
                          "Failed to parse DHCPv6Client= setting");
index 019fe4da425a0a7c2d2ff14ab19bbe3f102e0728..6094881ac5358afc9750ab2dc21be849b96b5448 100644 (file)
@@ -65,4 +65,3 @@ int link_request_ndisc(Link *link);
 int ndisc_reconfigure_address(Address *address, Link *link);
 
 CONFIG_PARSER_PROTOTYPE(config_parse_ndisc_start_dhcp6_client);
-CONFIG_PARSER_PROTOTYPE(config_parse_ndisc_use_domains);
index 2f9855465fe5b63db1701eef34eb6877cfa3eb5a..62f11c5c5c91823dec1f2c900e3bf37e8bed3e4a 100644 (file)
@@ -20,6 +20,7 @@ _Pragma("GCC diagnostic ignored \"-Wimplicit-fallthrough\"")
 #include "networkd-dhcp-server.h"
 #include "networkd-dhcp4.h"
 #include "networkd-dhcp6.h"
+#include "networkd-dns.h"
 #include "networkd-ipv4ll.h"
 #include "networkd-ipv6-proxy-ndp.h"
 #include "networkd-ipv6ll.h"
@@ -29,6 +30,7 @@ _Pragma("GCC diagnostic ignored \"-Wimplicit-fallthrough\"")
 #include "networkd-network.h"
 #include "networkd-neighbor.h"
 #include "networkd-nexthop.h"
+#include "networkd-ntp.h"
 #include "networkd-radv.h"
 #include "networkd-route.h"
 #include "networkd-routing-policy-rule.h"
@@ -116,6 +118,7 @@ Network.EmitLLDP,                            config_parse_lldp_multicast_mode,
 Network.Address,                             config_parse_address,                                     0,                             0
 Network.Gateway,                             config_parse_gateway,                                     0,                             0
 Network.Domains,                             config_parse_domains,                                     0,                             0
+Network.UseDomains,                          config_parse_use_domains,                                 0,                             offsetof(Network, use_domains)
 Network.DNS,                                 config_parse_dns,                                         0,                             0
 Network.DNSDefaultRoute,                     config_parse_tristate,                                    0,                             offsetof(Network, dns_default_route)
 Network.LLMNR,                               config_parse_resolve_support,                             0,                             offsetof(Network, llmnr)
@@ -221,15 +224,15 @@ NextHop.Blackhole,                           config_parse_nexthop_blackhole,
 NextHop.Group,                               config_parse_nexthop_group,                               0,                             0
 DHCPv4.RequestAddress,                       config_parse_in_addr_non_null,                            AF_INET,                       offsetof(Network, dhcp_request_address)
 DHCPv4.ClientIdentifier,                     config_parse_dhcp_client_identifier,                      0,                             offsetof(Network, dhcp_client_identifier)
-DHCPv4.UseDNS,                               config_parse_dhcp_use_dns,                                AF_INET,                       0
+DHCPv4.UseDNS,                               config_parse_tristate,                                    0,                             offsetof(Network, dhcp_use_dns)
 DHCPv4.RoutesToDNS,                          config_parse_bool,                                        0,                             offsetof(Network, dhcp_routes_to_dns)
-DHCPv4.UseNTP,                               config_parse_dhcp_use_ntp,                                AF_INET,                       0
+DHCPv4.UseNTP,                               config_parse_tristate,                                    0,                             offsetof(Network, dhcp_use_ntp)
 DHCPv4.RoutesToNTP,                          config_parse_bool,                                        0,                             offsetof(Network, dhcp_routes_to_ntp)
 DHCPv4.UseSIP,                               config_parse_bool,                                        0,                             offsetof(Network, dhcp_use_sip)
 DHCPv4.UseCaptivePortal,                     config_parse_bool,                                        0,                             offsetof(Network, dhcp_use_captive_portal)
 DHCPv4.UseMTU,                               config_parse_bool,                                        0,                             offsetof(Network, dhcp_use_mtu)
 DHCPv4.UseHostname,                          config_parse_bool,                                        0,                             offsetof(Network, dhcp_use_hostname)
-DHCPv4.UseDomains,                           config_parse_dhcp_use_domains,                            AF_INET,                       0
+DHCPv4.UseDomains,                           config_parse_use_domains,                                 0,                             offsetof(Network, dhcp_use_domains)
 DHCPv4.UseRoutes,                            config_parse_bool,                                        0,                             offsetof(Network, dhcp_use_routes)
 DHCPv4.UseGateway,                           config_parse_tristate,                                    0,                             offsetof(Network, dhcp_use_gateway)
 DHCPv4.QuickAck,                             config_parse_bool,                                        0,                             offsetof(Network, dhcp_quickack)
@@ -270,10 +273,10 @@ DHCPv4.NFTSet,                               config_parse_nft_set,
 DHCPv4.RapidCommit,                          config_parse_tristate,                                    0,                             offsetof(Network, dhcp_use_rapid_commit)
 DHCPv6.UseAddress,                           config_parse_bool,                                        0,                             offsetof(Network, dhcp6_use_address)
 DHCPv6.UseDelegatedPrefix,                   config_parse_bool,                                        0,                             offsetof(Network, dhcp6_use_pd_prefix)
-DHCPv6.UseDNS,                               config_parse_dhcp_use_dns,                                AF_INET6,                      0
+DHCPv6.UseDNS,                               config_parse_tristate,                                    0,                             offsetof(Network, dhcp6_use_dns)
 DHCPv6.UseHostname,                          config_parse_bool,                                        0,                             offsetof(Network, dhcp6_use_hostname)
-DHCPv6.UseDomains,                           config_parse_dhcp_use_domains,                            AF_INET6,                      0
-DHCPv6.UseNTP,                               config_parse_dhcp_use_ntp,                                AF_INET6,                      0
+DHCPv6.UseDomains,                           config_parse_use_domains,                                 0,                             offsetof(Network, dhcp6_use_domains)
+DHCPv6.UseNTP,                               config_parse_tristate,                                    0,                             offsetof(Network, dhcp6_use_ntp)
 DHCPv6.UseCaptivePortal,                     config_parse_bool,                                        0,                             offsetof(Network, dhcp6_use_captive_portal)
 DHCPv6.MUDURL,                               config_parse_mud_url,                                     0,                             offsetof(Network, dhcp6_mudurl)
 DHCPv6.SendHostname,                         config_parse_dhcp_send_hostname,                          AF_INET6,                      0
@@ -298,8 +301,8 @@ IPv6AcceptRA.UseRoutePrefix,                 config_parse_bool,
 IPv6AcceptRA.UseAutonomousPrefix,            config_parse_bool,                                        0,                             offsetof(Network, ndisc_use_autonomous_prefix)
 IPv6AcceptRA.UseOnLinkPrefix,                config_parse_bool,                                        0,                             offsetof(Network, ndisc_use_onlink_prefix)
 IPv6AcceptRA.UsePREF64,                      config_parse_bool,                                        0,                             offsetof(Network, ndisc_use_pref64)
-IPv6AcceptRA.UseDNS,                         config_parse_bool,                                        0,                             offsetof(Network, ndisc_use_dns)
-IPv6AcceptRA.UseDomains,                     config_parse_ndisc_use_domains,                           0,                             offsetof(Network, ndisc_use_domains)
+IPv6AcceptRA.UseDNS,                         config_parse_tristate,                                    0,                             offsetof(Network, ndisc_use_dns)
+IPv6AcceptRA.UseDomains,                     config_parse_use_domains,                                 0,                             offsetof(Network, ndisc_use_domains)
 IPv6AcceptRA.UseMTU,                         config_parse_bool,                                        0,                             offsetof(Network, ndisc_use_mtu)
 IPv6AcceptRA.UseHopLimit,                    config_parse_bool,                                        0,                             offsetof(Network, ndisc_use_hop_limit)
 IPv6AcceptRA.UseReachableTime,               config_parse_bool,                                        0,                             offsetof(Network, ndisc_use_reachable_time)
@@ -587,12 +590,12 @@ IPv6PrefixDelegation.Domains,                config_parse_radv_search_domains,
 IPv6PrefixDelegation.DNSLifetimeSec,         config_parse_sec,                                         0,                             offsetof(Network, router_dns_lifetime_usec)
 DHCPv4.BlackList,                            config_parse_in_addr_prefixes,                            AF_INET,                       offsetof(Network, dhcp_deny_listed_ip)
 DHCP.ClientIdentifier,                       config_parse_dhcp_client_identifier,                      0,                             offsetof(Network, dhcp_client_identifier)
-DHCP.UseDNS,                                 config_parse_dhcp_use_dns,                                AF_UNSPEC,                     0
-DHCP.UseNTP,                                 config_parse_dhcp_use_ntp,                                AF_UNSPEC,                     0
+DHCP.UseDNS,                                 config_parse_tristate,                                    0,                             offsetof(Network, compat_dhcp_use_dns)
+DHCP.UseNTP,                                 config_parse_tristate,                                    0,                             offsetof(Network, compat_dhcp_use_ntp)
 DHCP.UseMTU,                                 config_parse_bool,                                        0,                             offsetof(Network, dhcp_use_mtu)
 DHCP.UseHostname,                            config_parse_bool,                                        0,                             offsetof(Network, dhcp_use_hostname)
-DHCP.UseDomains,                             config_parse_dhcp_use_domains,                            AF_UNSPEC,                     0
-DHCP.UseDomainName,                          config_parse_dhcp_use_domains,                            AF_UNSPEC,                     0
+DHCP.UseDomains,                             config_parse_use_domains,                                 0,                             offsetof(Network, compat_dhcp_use_domains)
+DHCP.UseDomainName,                          config_parse_use_domains,                                 0,                             offsetof(Network, compat_dhcp_use_domains)
 DHCP.UseRoutes,                              config_parse_bool,                                        0,                             offsetof(Network, dhcp_use_routes)
 DHCP.Anonymize,                              config_parse_bool,                                        0,                             offsetof(Network, dhcp_anonymize)
 DHCP.SendHostname,                           config_parse_dhcp_send_hostname,                          AF_UNSPEC,                     0
@@ -610,7 +613,7 @@ DHCP.UseTimezone,                            config_parse_bool,
 DHCP.ListenPort,                             config_parse_uint16,                                      0,                             offsetof(Network, dhcp_client_port)
 DHCP.RapidCommit,                            config_parse_bool,                                        0,                             offsetof(Network, dhcp6_use_rapid_commit)
 DHCP.ForceDHCPv6PDOtherInformation,          config_parse_warn_compat,                                 DISABLED_LEGACY,               0
-DHCPv4.UseDomainName,                        config_parse_dhcp_use_domains,                            AF_INET,                       0
+DHCPv4.UseDomainName,                        config_parse_use_domains,                                 0,                             offsetof(Network, dhcp_use_domains)
 DHCPv4.CriticalConnection,                   config_parse_tristate,                                    0,                             offsetof(Network, dhcp_critical)
 DHCPv6.RouteMetric,                          config_parse_ndisc_route_metric,                          AF_INET6,                      0
 DHCPv6.ForceDHCPv6PDOtherInformation,        config_parse_warn_compat,                                 DISABLED_LEGACY,               0
index 833f1bd059c8d9cf5c26000fe57f1ec1eaa9db2e..c64091c15aecccae8bf93103d48898d4de07244a 100644 (file)
@@ -43,9 +43,6 @@
 #include "strv.h"
 #include "tclass.h"
 
-/* Let's assume that anything above this number is a user misconfiguration. */
-#define MAX_NTP_SERVERS 128U
-
 static int network_resolve_netdev_one(Network *network, const char *name, NetDevKind kind, NetDev **ret) {
         const char *kind_string;
         NetDev *netdev;
@@ -380,15 +377,21 @@ int network_load_one(Manager *manager, OrderedHashmap **networks, const char *fi
 
                 .keep_configuration = manager->keep_configuration,
 
+                .use_domains = _USE_DOMAINS_INVALID,
+
+                .compat_dhcp_use_domains = _USE_DOMAINS_INVALID,
+                .compat_dhcp_use_dns = -1,
+                .compat_dhcp_use_ntp = -1,
+
                 .dhcp_duid.type = _DUID_TYPE_INVALID,
                 .dhcp_critical = -1,
-                .dhcp_use_ntp = true,
+                .dhcp_use_ntp = -1,
                 .dhcp_routes_to_ntp = true,
                 .dhcp_use_sip = true,
                 .dhcp_use_captive_portal = true,
-                .dhcp_use_dns = true,
+                .dhcp_use_dns = -1,
                 .dhcp_routes_to_dns = true,
-                .dhcp_use_domains = manager->dhcp_use_domains,
+                .dhcp_use_domains = _USE_DOMAINS_INVALID,
                 .dhcp_use_hostname = true,
                 .dhcp_use_routes = true,
                 .dhcp_use_gateway = -1,
@@ -404,10 +407,10 @@ int network_load_one(Manager *manager, OrderedHashmap **networks, const char *fi
 
                 .dhcp6_use_address = true,
                 .dhcp6_use_pd_prefix = true,
-                .dhcp6_use_dns = true,
-                .dhcp6_use_domains = manager->dhcp6_use_domains,
+                .dhcp6_use_dns = -1,
+                .dhcp6_use_domains = _USE_DOMAINS_INVALID,
                 .dhcp6_use_hostname = true,
-                .dhcp6_use_ntp = true,
+                .dhcp6_use_ntp = -1,
                 .dhcp6_use_captive_portal = true,
                 .dhcp6_use_rapid_commit = true,
                 .dhcp6_send_hostname = true,
@@ -478,7 +481,7 @@ int network_load_one(Manager *manager, OrderedHashmap **networks, const char *fi
 
                 .ndisc = -1,
                 .ndisc_use_redirect = true,
-                .ndisc_use_dns = true,
+                .ndisc_use_dns = -1,
                 .ndisc_use_gateway = true,
                 .ndisc_use_captive_portal = true,
                 .ndisc_use_route_prefix = true,
@@ -488,6 +491,7 @@ int network_load_one(Manager *manager, OrderedHashmap **networks, const char *fi
                 .ndisc_use_hop_limit = true,
                 .ndisc_use_reachable_time = true,
                 .ndisc_use_retransmission_time = true,
+                .ndisc_use_domains = _USE_DOMAINS_INVALID,
                 .ndisc_route_table = RT_TABLE_MAIN,
                 .ndisc_route_metric_high = IPV6RA_ROUTE_METRIC_HIGH,
                 .ndisc_route_metric_medium = IPV6RA_ROUTE_METRIC_MEDIUM,
@@ -922,288 +926,6 @@ int config_parse_stacked_netdev(
         return 0;
 }
 
-int config_parse_domains(
-                const char *unit,
-                const char *filename,
-                unsigned line,
-                const char *section,
-                unsigned section_line,
-                const char *lvalue,
-                int ltype,
-                const char *rvalue,
-                void *data,
-                void *userdata) {
-
-        Network *n = ASSERT_PTR(userdata);
-        int r;
-
-        assert(filename);
-        assert(lvalue);
-        assert(rvalue);
-
-        if (isempty(rvalue)) {
-                n->search_domains = ordered_set_free(n->search_domains);
-                n->route_domains = ordered_set_free(n->route_domains);
-                return 0;
-        }
-
-        for (const char *p = rvalue;;) {
-                _cleanup_free_ char *w = NULL, *normalized = NULL;
-                const char *domain;
-                bool is_route;
-
-                r = extract_first_word(&p, &w, NULL, 0);
-                if (r == -ENOMEM)
-                        return log_oom();
-                if (r < 0) {
-                        log_syntax(unit, LOG_WARNING, filename, line, r,
-                                   "Failed to extract search or route domain, ignoring: %s", rvalue);
-                        return 0;
-                }
-                if (r == 0)
-                        return 0;
-
-                is_route = w[0] == '~';
-                domain = is_route ? w + 1 : w;
-
-                if (dns_name_is_root(domain) || streq(domain, "*")) {
-                        /* If the root domain appears as is, or the special token "*" is found, we'll
-                         * consider this as routing domain, unconditionally. */
-                        is_route = true;
-                        domain = "."; /* make sure we don't allow empty strings, thus write the root
-                                       * domain as "." */
-                } else {
-                        r = dns_name_normalize(domain, 0, &normalized);
-                        if (r < 0) {
-                                log_syntax(unit, LOG_WARNING, filename, line, r,
-                                           "'%s' is not a valid domain name, ignoring.", domain);
-                                continue;
-                        }
-
-                        domain = normalized;
-
-                        if (is_localhost(domain)) {
-                                log_syntax(unit, LOG_WARNING, filename, line, 0,
-                                           "'localhost' domain may not be configured as search or route domain, ignoring assignment: %s",
-                                           domain);
-                                continue;
-                        }
-                }
-
-                OrderedSet **set = is_route ? &n->route_domains : &n->search_domains;
-                r = ordered_set_put_strdup(set, domain);
-                if (r == -EEXIST)
-                        continue;
-                if (r < 0)
-                        return log_oom();
-        }
-}
-
-int config_parse_timezone(
-                const char *unit,
-                const char *filename,
-                unsigned line,
-                const char *section,
-                unsigned section_line,
-                const char *lvalue,
-                int ltype,
-                const char *rvalue,
-                void *data,
-                void *userdata) {
-
-        char **tz = ASSERT_PTR(data);
-        int r;
-
-        assert(filename);
-        assert(lvalue);
-        assert(rvalue);
-
-        if (isempty(rvalue)) {
-                *tz = mfree(*tz);
-                return 0;
-        }
-
-        r = verify_timezone(rvalue, LOG_WARNING);
-        if (r < 0) {
-                log_syntax(unit, LOG_WARNING, filename, line, r,
-                           "Timezone is not valid, ignoring assignment: %s", rvalue);
-                return 0;
-        }
-
-        return free_and_strdup_warn(tz, rvalue);
-}
-
-int config_parse_dns(
-                const char *unit,
-                const char *filename,
-                unsigned line,
-                const char *section,
-                unsigned section_line,
-                const char *lvalue,
-                int ltype,
-                const char *rvalue,
-                void *data,
-                void *userdata) {
-
-        Network *n = ASSERT_PTR(userdata);
-        int r;
-
-        assert(filename);
-        assert(lvalue);
-        assert(rvalue);
-
-        if (isempty(rvalue)) {
-                for (unsigned i = 0; i < n->n_dns; i++)
-                        in_addr_full_free(n->dns[i]);
-                n->dns = mfree(n->dns);
-                n->n_dns = 0;
-                return 0;
-        }
-
-        for (const char *p = rvalue;;) {
-                _cleanup_(in_addr_full_freep) struct in_addr_full *dns = NULL;
-                _cleanup_free_ char *w = NULL;
-                struct in_addr_full **m;
-
-                r = extract_first_word(&p, &w, NULL, 0);
-                if (r == -ENOMEM)
-                        return log_oom();
-                if (r < 0) {
-                        log_syntax(unit, LOG_WARNING, filename, line, r,
-                                   "Invalid syntax, ignoring: %s", rvalue);
-                        return 0;
-                }
-                if (r == 0)
-                        return 0;
-
-                r = in_addr_full_new_from_string(w, &dns);
-                if (r < 0) {
-                        log_syntax(unit, LOG_WARNING, filename, line, r,
-                                   "Failed to parse dns server address, ignoring: %s", w);
-                        continue;
-                }
-
-                if (IN_SET(dns->port, 53, 853))
-                        dns->port = 0;
-
-                m = reallocarray(n->dns, n->n_dns + 1, sizeof(struct in_addr_full*));
-                if (!m)
-                        return log_oom();
-
-                m[n->n_dns++] = TAKE_PTR(dns);
-                n->dns = m;
-        }
-}
-
-int config_parse_dnssec_negative_trust_anchors(
-                const char *unit,
-                const char *filename,
-                unsigned line,
-                const char *section,
-                unsigned section_line,
-                const char *lvalue,
-                int ltype,
-                const char *rvalue,
-                void *data,
-                void *userdata) {
-
-        Set **nta = ASSERT_PTR(data);
-        int r;
-
-        assert(filename);
-        assert(lvalue);
-        assert(rvalue);
-
-        if (isempty(rvalue)) {
-                *nta = set_free_free(*nta);
-                return 0;
-        }
-
-        for (const char *p = rvalue;;) {
-                _cleanup_free_ char *w = NULL;
-
-                r = extract_first_word(&p, &w, NULL, 0);
-                if (r == -ENOMEM)
-                        return log_oom();
-                if (r < 0) {
-                        log_syntax(unit, LOG_WARNING, filename, line, r,
-                                   "Failed to extract negative trust anchor domain, ignoring: %s", rvalue);
-                        return 0;
-                }
-                if (r == 0)
-                        return 0;
-
-                r = dns_name_is_valid(w);
-                if (r <= 0) {
-                        log_syntax(unit, LOG_WARNING, filename, line, r,
-                                   "%s is not a valid domain name, ignoring.", w);
-                        continue;
-                }
-
-                r = set_ensure_consume(nta, &dns_name_hash_ops, TAKE_PTR(w));
-                if (r < 0)
-                        return log_oom();
-        }
-}
-
-int config_parse_ntp(
-                const char *unit,
-                const char *filename,
-                unsigned line,
-                const char *section,
-                unsigned section_line,
-                const char *lvalue,
-                int ltype,
-                const char *rvalue,
-                void *data,
-                void *userdata) {
-
-        char ***l = ASSERT_PTR(data);
-        int r;
-
-        assert(filename);
-        assert(lvalue);
-        assert(rvalue);
-
-        if (isempty(rvalue)) {
-                *l = strv_free(*l);
-                return 0;
-        }
-
-        for (const char *p = rvalue;;) {
-                _cleanup_free_ char *w = NULL;
-
-                r = extract_first_word(&p, &w, NULL, 0);
-                if (r == -ENOMEM)
-                        return log_oom();
-                if (r < 0) {
-                        log_syntax(unit, LOG_WARNING, filename, line, r,
-                                   "Failed to extract NTP server name, ignoring: %s", rvalue);
-                        return 0;
-                }
-                if (r == 0)
-                        return 0;
-
-                r = dns_name_is_valid_or_address(w);
-                if (r <= 0) {
-                        log_syntax(unit, LOG_WARNING, filename, line, r,
-                                   "%s is not a valid domain name or IP address, ignoring.", w);
-                        continue;
-                }
-
-                if (strv_length(*l) > MAX_NTP_SERVERS) {
-                        log_syntax(unit, LOG_WARNING, filename, line, 0,
-                                   "More than %u NTP servers specified, ignoring \"%s\" and any subsequent entries.",
-                                   MAX_NTP_SERVERS, w);
-                        return 0;
-                }
-
-                r = strv_consume(l, TAKE_PTR(w));
-                if (r < 0)
-                        return log_oom();
-        }
-}
-
 int config_parse_required_for_online(
                 const char *unit,
                 const char *filename,
index eacf3a3dd614ead0be852e013cc41d34d6821e22..c8573e097342d75fac3db7dc6013bf3db6ea1bc0 100644 (file)
@@ -20,6 +20,7 @@
 #include "networkd-dhcp-common.h"
 #include "networkd-dhcp4.h"
 #include "networkd-dhcp6.h"
+#include "networkd-dns.h"
 #include "networkd-ipv6ll.h"
 #include "networkd-lldp-rx.h"
 #include "networkd-ndisc.h"
@@ -112,6 +113,14 @@ struct Network {
         bool default_route_on_device;
         AddressFamily ip_masquerade;
 
+        /* Protocol independent settings */
+        UseDomains use_domains;
+
+        /* For backward compatibility, only applied to DHCPv4 and DHCPv6. */
+        UseDomains compat_dhcp_use_domains;
+        int compat_dhcp_use_dns;
+        int compat_dhcp_use_ntp;
+
         /* DHCP Client Support */
         AddressFamily dhcp;
         struct in_addr dhcp_request_address;
@@ -143,11 +152,9 @@ struct Network {
         int dhcp_broadcast;
         int dhcp_ipv6_only_mode;
         int dhcp_use_rapid_commit;
-        bool dhcp_use_dns;
-        bool dhcp_use_dns_set;
+        int dhcp_use_dns;
         bool dhcp_routes_to_dns;
-        bool dhcp_use_ntp;
-        bool dhcp_use_ntp_set;
+        int dhcp_use_ntp;
         bool dhcp_routes_to_ntp;
         bool dhcp_use_sip;
         bool dhcp_use_captive_portal;
@@ -162,8 +169,7 @@ struct Network {
         bool dhcp_use_6rd;
         bool dhcp_send_release;
         bool dhcp_send_decline;
-        DHCPUseDomains dhcp_use_domains;
-        bool dhcp_use_domains_set;
+        UseDomains dhcp_use_domains;
         Set *dhcp_deny_listed_ip;
         Set *dhcp_allow_listed_ip;
         Set *dhcp_request_options;
@@ -177,15 +183,12 @@ struct Network {
         bool dhcp6_use_pd_prefix;
         bool dhcp6_send_hostname;
         bool dhcp6_send_hostname_set;
-        bool dhcp6_use_dns;
-        bool dhcp6_use_dns_set;
+        int dhcp6_use_dns;
         bool dhcp6_use_hostname;
-        bool dhcp6_use_ntp;
-        bool dhcp6_use_ntp_set;
+        int dhcp6_use_ntp;
         bool dhcp6_use_captive_portal;
         bool dhcp6_use_rapid_commit;
-        DHCPUseDomains dhcp6_use_domains;
-        bool dhcp6_use_domains_set;
+        UseDomains dhcp6_use_domains;
         uint32_t dhcp6_iaid;
         bool dhcp6_iaid_set;
         bool dhcp6_iaid_set_explicitly;
@@ -338,7 +341,7 @@ struct Network {
         /* NDisc support */
         int ndisc;
         bool ndisc_use_redirect;
-        bool ndisc_use_dns;
+        int ndisc_use_dns;
         bool ndisc_use_gateway;
         bool ndisc_use_route_prefix;
         bool ndisc_use_autonomous_prefix;
@@ -352,7 +355,7 @@ struct Network {
         bool ndisc_use_pref64;
         bool active_slave;
         bool primary_slave;
-        DHCPUseDomains ndisc_use_domains;
+        UseDomains ndisc_use_domains;
         IPv6AcceptRAStartDHCP6Client ndisc_start_dhcp6_client;
         uint32_t ndisc_route_table;
         bool ndisc_route_table_set;
@@ -424,11 +427,6 @@ bool network_has_static_ipv6_configurations(Network *network);
 
 CONFIG_PARSER_PROTOTYPE(config_parse_stacked_netdev);
 CONFIG_PARSER_PROTOTYPE(config_parse_tunnel);
-CONFIG_PARSER_PROTOTYPE(config_parse_domains);
-CONFIG_PARSER_PROTOTYPE(config_parse_dns);
-CONFIG_PARSER_PROTOTYPE(config_parse_timezone);
-CONFIG_PARSER_PROTOTYPE(config_parse_dnssec_negative_trust_anchors);
-CONFIG_PARSER_PROTOTYPE(config_parse_ntp);
 CONFIG_PARSER_PROTOTYPE(config_parse_required_for_online);
 CONFIG_PARSER_PROTOTYPE(config_parse_required_family_for_online);
 CONFIG_PARSER_PROTOTYPE(config_parse_keep_configuration);
diff --git a/src/network/networkd-ntp.c b/src/network/networkd-ntp.c
new file mode 100644 (file)
index 0000000..e764fea
--- /dev/null
@@ -0,0 +1,101 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "dns-domain.h"
+#include "networkd-network.h"
+#include "networkd-ntp.h"
+#include "parse-util.h"
+#include "strv.h"
+
+/* Let's assume that anything above this number is a user misconfiguration. */
+#define MAX_NTP_SERVERS 128U
+
+bool link_get_use_ntp(Link *link, NetworkConfigSource proto) {
+        int n, c;
+
+        assert(link);
+
+        if (!link->network)
+                return false;
+
+        switch (proto) {
+        case NETWORK_CONFIG_SOURCE_DHCP4:
+                n = link->network->dhcp_use_ntp;
+                c = link->network->compat_dhcp_use_ntp;
+                break;
+        case NETWORK_CONFIG_SOURCE_DHCP6:
+                n = link->network->dhcp6_use_ntp;
+                c = link->network->compat_dhcp_use_ntp;
+                break;
+        default:
+                assert_not_reached();
+        }
+
+        /* If per-network and per-protocol setting is specified, use it. */
+        if (n >= 0)
+                return n;
+
+        /* If compat setting is specified, use it. */
+        if (c >= 0)
+                return c;
+
+        /* Otherwise, defaults to yes. */
+        return true;
+}
+
+int config_parse_ntp(
+                const char *unit,
+                const char *filename,
+                unsigned line,
+                const char *section,
+                unsigned section_line,
+                const char *lvalue,
+                int ltype,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
+
+        char ***l = ASSERT_PTR(data);
+        int r;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+
+        if (isempty(rvalue)) {
+                *l = strv_free(*l);
+                return 0;
+        }
+
+        for (const char *p = rvalue;;) {
+                _cleanup_free_ char *w = NULL;
+
+                r = extract_first_word(&p, &w, NULL, 0);
+                if (r == -ENOMEM)
+                        return log_oom();
+                if (r < 0) {
+                        log_syntax(unit, LOG_WARNING, filename, line, r,
+                                   "Failed to extract NTP server name, ignoring: %s", rvalue);
+                        return 0;
+                }
+                if (r == 0)
+                        return 0;
+
+                r = dns_name_is_valid_or_address(w);
+                if (r <= 0) {
+                        log_syntax(unit, LOG_WARNING, filename, line, r,
+                                   "%s is not a valid domain name or IP address, ignoring.", w);
+                        continue;
+                }
+
+                if (strv_length(*l) > MAX_NTP_SERVERS) {
+                        log_syntax(unit, LOG_WARNING, filename, line, 0,
+                                   "More than %u NTP servers specified, ignoring \"%s\" and any subsequent entries.",
+                                   MAX_NTP_SERVERS, w);
+                        return 0;
+                }
+
+                r = strv_consume(l, TAKE_PTR(w));
+                if (r < 0)
+                        return log_oom();
+        }
+}
diff --git a/src/network/networkd-ntp.h b/src/network/networkd-ntp.h
new file mode 100644 (file)
index 0000000..44e7678
--- /dev/null
@@ -0,0 +1,11 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include "conf-parser.h"
+#include "networkd-util.h"
+
+typedef struct Link Link;
+
+bool link_get_use_ntp(Link *link, NetworkConfigSource proto);
+
+CONFIG_PARSER_PROTOTYPE(config_parse_ntp);
index b137b9384f7f56746d0a56d5ae1feebd00bb1525..fa5884a6c354db1b4d35733e4c3c29e862365319 100644 (file)
@@ -627,9 +627,6 @@ static int radv_configure(Link *link) {
 }
 
 int radv_update_mac(Link *link) {
-        bool restart;
-        int r;
-
         assert(link);
 
         if (!link->radv)
@@ -638,23 +635,7 @@ int radv_update_mac(Link *link) {
         if (link->hw_addr.length != ETH_ALEN)
                 return 0;
 
-        restart = sd_radv_is_running(link->radv);
-
-        r = sd_radv_stop(link->radv);
-        if (r < 0)
-                return r;
-
-        r = sd_radv_set_mac(link->radv, &link->hw_addr.ether);
-        if (r < 0)
-                return r;
-
-        if (restart) {
-                r = sd_radv_start(link->radv);
-                if (r < 0)
-                        return r;
-        }
-
-        return 0;
+        return sd_radv_set_mac(link->radv, &link->hw_addr.ether);
 }
 
 static int radv_is_ready_to_configure(Link *link) {
@@ -769,6 +750,10 @@ int radv_start(Link *link) {
                         return log_link_debug_errno(link, r, "Failed to request DHCP delegated subnet prefix: %m");
         }
 
+        r = sd_radv_set_link_local_address(link->radv, &link->ipv6ll_address);
+        if (r < 0)
+                return r;
+
         log_link_debug(link, "Starting IPv6 Router Advertisements");
         return sd_radv_start(link->radv);
 }
index b1bbe136097b8d8289596a7cbb7bf69d4e6b6f82..fbe4fee17deaf88ca027aac5903ca75d8793f0f2 100644 (file)
@@ -15,6 +15,7 @@
 #include "networkd-manager-bus.h"
 #include "networkd-manager.h"
 #include "networkd-network.h"
+#include "networkd-ntp.h"
 #include "networkd-state-file.h"
 #include "ordered-set.h"
 #include "set.h"
@@ -101,7 +102,7 @@ static int link_put_dns(Link *link, OrderedSet **s) {
         if (r < 0)
                 return r;
 
-        if (link->dhcp_lease && link->network->dhcp_use_dns) {
+        if (link->dhcp_lease && link_get_use_dns(link, NETWORK_CONFIG_SOURCE_DHCP4)) {
                 const struct in_addr *addresses;
 
                 r = sd_dhcp_lease_get_dns(link->dhcp_lease, &addresses);
@@ -112,7 +113,7 @@ static int link_put_dns(Link *link, OrderedSet **s) {
                 }
         }
 
-        if (link->dhcp6_lease && link->network->dhcp6_use_dns) {
+        if (link->dhcp6_lease && link_get_use_dns(link, NETWORK_CONFIG_SOURCE_DHCP6)) {
                 const struct in6_addr *addresses;
 
                 r = sd_dhcp6_lease_get_dns(link->dhcp6_lease, &addresses);
@@ -123,7 +124,7 @@ static int link_put_dns(Link *link, OrderedSet **s) {
                 }
         }
 
-        if (link->network->ndisc_use_dns) {
+        if (link_get_use_dns(link, NETWORK_CONFIG_SOURCE_NDISC)) {
                 NDiscRDNSS *a;
 
                 SET_FOREACH(a, link->ndisc_rdnss) {
@@ -150,7 +151,7 @@ static int link_put_ntp(Link *link, OrderedSet **s) {
         if (r < 0)
                 return r;
 
-        if (link->dhcp_lease && link->network->dhcp_use_ntp) {
+        if (link->dhcp_lease && link_get_use_ntp(link, NETWORK_CONFIG_SOURCE_DHCP4)) {
                 const struct in_addr *addresses;
 
                 r = sd_dhcp_lease_get_ntp(link->dhcp_lease, &addresses);
@@ -161,7 +162,7 @@ static int link_put_ntp(Link *link, OrderedSet **s) {
                 }
         }
 
-        if (link->dhcp6_lease && link->network->dhcp6_use_ntp) {
+        if (link->dhcp6_lease && link_get_use_ntp(link, NETWORK_CONFIG_SOURCE_DHCP6)) {
                 const struct in6_addr *addresses;
                 char **fqdn;
 
@@ -206,7 +207,7 @@ static int link_put_sip(Link *link, OrderedSet **s) {
 
 static int link_put_domains(Link *link, bool is_route, OrderedSet **s) {
         OrderedSet *link_domains, *network_domains;
-        DHCPUseDomains use_domains;
+        UseDomains use_domains;
         int r;
 
         assert(link);
@@ -215,7 +216,7 @@ static int link_put_domains(Link *link, bool is_route, OrderedSet **s) {
 
         link_domains = is_route ? link->route_domains : link->search_domains;
         network_domains = is_route ? link->network->route_domains : link->network->search_domains;
-        use_domains = is_route ? DHCP_USE_DOMAINS_ROUTE : DHCP_USE_DOMAINS_YES;
+        use_domains = is_route ? USE_DOMAINS_ROUTE : USE_DOMAINS_YES;
 
         if (link_domains)
                 return ordered_set_put_string_set(s, link_domains);
@@ -224,7 +225,7 @@ static int link_put_domains(Link *link, bool is_route, OrderedSet **s) {
         if (r < 0)
                 return r;
 
-        if (link->dhcp_lease && link->network->dhcp_use_domains == use_domains) {
+        if (link->dhcp_lease && link_get_use_domains(link, NETWORK_CONFIG_SOURCE_DHCP4) == use_domains) {
                 const char *domainname;
                 char **domains;
 
@@ -243,7 +244,7 @@ static int link_put_domains(Link *link, bool is_route, OrderedSet **s) {
                 }
         }
 
-        if (link->dhcp6_lease && link->network->dhcp6_use_domains == use_domains) {
+        if (link->dhcp6_lease && link_get_use_domains(link, NETWORK_CONFIG_SOURCE_DHCP6) == use_domains) {
                 char **domains;
 
                 r = sd_dhcp6_lease_get_domains(link->dhcp6_lease, &domains);
@@ -254,7 +255,7 @@ static int link_put_domains(Link *link, bool is_route, OrderedSet **s) {
                 }
         }
 
-        if (link->network->ndisc_use_domains == use_domains) {
+        if (link_get_use_domains(link, NETWORK_CONFIG_SOURCE_NDISC) == use_domains) {
                 NDiscDNSSL *a;
 
                 SET_FOREACH(a, link->ndisc_dnssl) {
@@ -528,7 +529,7 @@ static void serialize_addresses(
                 fputc('\n', f);
 }
 
-static void link_save_domains(Link *link, FILE *f, OrderedSet *static_domains, DHCPUseDomains use_domains) {
+static void link_save_domains(Link *link, FILE *f, OrderedSet *static_domains, UseDomains use_domains) {
         bool space = false;
         const char *p;
 
@@ -539,10 +540,10 @@ static void link_save_domains(Link *link, FILE *f, OrderedSet *static_domains, D
         ORDERED_SET_FOREACH(p, static_domains)
                 fputs_with_separator(f, p, NULL, &space);
 
-        if (use_domains == DHCP_USE_DOMAINS_NO)
+        if (use_domains == USE_DOMAINS_NO)
                 return;
 
-        if (link->dhcp_lease && link->network->dhcp_use_domains == use_domains) {
+        if (link->dhcp_lease && link_get_use_domains(link, NETWORK_CONFIG_SOURCE_DHCP4) == use_domains) {
                 const char *domainname;
                 char **domains;
 
@@ -552,14 +553,14 @@ static void link_save_domains(Link *link, FILE *f, OrderedSet *static_domains, D
                         fputstrv(f, domains, NULL, &space);
         }
 
-        if (link->dhcp6_lease && link->network->dhcp6_use_domains == use_domains) {
+        if (link->dhcp6_lease && link_get_use_domains(link, NETWORK_CONFIG_SOURCE_DHCP6) == use_domains) {
                 char **domains;
 
                 if (sd_dhcp6_lease_get_domains(link->dhcp6_lease, &domains) >= 0)
                         fputstrv(f, domains, NULL, &space);
         }
 
-        if (link->network->ndisc_use_domains == use_domains) {
+        if (link_get_use_domains(link, NETWORK_CONFIG_SOURCE_NDISC) == use_domains) {
                 NDiscDNSSL *dd;
 
                 SET_FOREACH(dd, link->ndisc_dnssl)
@@ -666,14 +667,14 @@ static int link_save(Link *link) {
                         serialize_addresses(f, NULL, &space,
                                             NULL,
                                             link->dhcp_lease,
-                                            link->network->dhcp_use_dns,
+                                            link_get_use_dns(link, NETWORK_CONFIG_SOURCE_DHCP4),
                                             SD_DHCP_LEASE_DNS,
                                             link->dhcp6_lease,
-                                            link->network->dhcp6_use_dns,
+                                            link_get_use_dns(link, NETWORK_CONFIG_SOURCE_DHCP6),
                                             sd_dhcp6_lease_get_dns,
                                             NULL);
 
-                        if (link->network->ndisc_use_dns) {
+                        if (link_get_use_dns(link, NETWORK_CONFIG_SOURCE_NDISC)) {
                                 NDiscRDNSS *dd;
 
                                 SET_FOREACH(dd, link->ndisc_rdnss)
@@ -693,10 +694,10 @@ static int link_save(Link *link) {
                         serialize_addresses(f, "NTP", NULL,
                                             link->network->ntp,
                                             link->dhcp_lease,
-                                            link->network->dhcp_use_ntp,
+                                            link_get_use_ntp(link, NETWORK_CONFIG_SOURCE_DHCP4),
                                             SD_DHCP_LEASE_NTP,
                                             link->dhcp6_lease,
-                                            link->network->dhcp6_use_ntp,
+                                            link_get_use_ntp(link, NETWORK_CONFIG_SOURCE_DHCP6),
                                             sd_dhcp6_lease_get_ntp_addrs,
                                             sd_dhcp6_lease_get_ntp_fqdn);
 
@@ -720,18 +721,18 @@ static int link_save(Link *link) {
 
                 fputs("DOMAINS=", f);
                 if (link->search_domains)
-                        link_save_domains(link, f, link->search_domains, DHCP_USE_DOMAINS_NO);
+                        link_save_domains(link, f, link->search_domains, USE_DOMAINS_NO);
                 else
-                        link_save_domains(link, f, link->network->search_domains, DHCP_USE_DOMAINS_YES);
+                        link_save_domains(link, f, link->network->search_domains, USE_DOMAINS_YES);
                 fputc('\n', f);
 
                 /************************************************************/
 
                 fputs("ROUTE_DOMAINS=", f);
                 if (link->route_domains)
-                        link_save_domains(link, f, link->route_domains, DHCP_USE_DOMAINS_NO);
+                        link_save_domains(link, f, link->route_domains, USE_DOMAINS_NO);
                 else
-                        link_save_domains(link, f, link->network->route_domains, DHCP_USE_DOMAINS_ROUTE);
+                        link_save_domains(link, f, link->network->route_domains, USE_DOMAINS_ROUTE);
                 fputc('\n', f);
 
                 /************************************************************/
index 828dbb96388a6230daf564e62da3ab64d6c0d07a..06d436245e7bbe66612d90b24593001fc150b546 100644 (file)
 #ManageForeignNextHops=yes
 #RouteTable=
 #IPv6PrivacyExtensions=no
+#UseDomains=no
+
+[IPv6AcceptRA]
+#UseDomains=
 
 [DHCPv4]
 #DUIDType=vendor
 #DUIDRawData=
-#UseDomains=no
+#UseDomains=
 
 [DHCPv6]
 #DUIDType=vendor
 #DUIDRawData=
-#UseDomains=no
+#UseDomains=
 
 [DHCPServer]
 #PersistLeases=yes
index 96f5b4d705aedb460434f4bb9ccf35523981cf95..f4e14c6d9b300327a6c5b2d692e17dded1e2d4eb 100644 (file)
@@ -32,7 +32,7 @@ int main(int argc, char **argv) {
         test_table(bond_xmit_hash_policy, NETDEV_BOND_XMIT_HASH_POLICY);
         test_table(dhcp6_message_status, DHCP6_STATUS);
         test_table_sparse(dhcp6_message_type, DHCP6_MESSAGE_TYPE); /* enum starts from 1 */
-        test_table(dhcp_use_domains, DHCP_USE_DOMAINS);
+        test_table(use_domains, USE_DOMAINS);
         test_table(duplex, DUP);
         test_table(ip6tnl_mode, NETDEV_IP6_TNL_MODE);
         test_table(ipv6_privacy_extensions, IPV6_PRIVACY_EXTENSIONS);
index 9e1210f876ef72f6d9e5ac5163479b2ca93de652..123ef0c6c87c395d17316fde647c13e7c51e8c9e 100644 (file)
@@ -58,7 +58,7 @@ Exec.OOMScoreAdjust,          config_parse_oom_score_adjust, 0,
 Exec.CPUAffinity,             config_parse_cpu_affinity,   0,                        0
 Exec.ResolvConf,              config_parse_resolv_conf,    0,                        offsetof(Settings, resolv_conf)
 Exec.LinkJournal,             config_parse_link_journal,   0,                        0
-Exec.Timezone,                config_parse_timezone,       0,                        offsetof(Settings, timezone)
+Exec.Timezone,                config_parse_timezone_mode,  0,                        offsetof(Settings, timezone)
 Exec.SuppressSync,            config_parse_tristate,       0,                        offsetof(Settings, suppress_sync)
 Files.ReadOnly,               config_parse_tristate,       0,                        offsetof(Settings, read_only)
 Files.Volatile,               config_parse_volatile_mode,  0,                        offsetof(Settings, volatile_mode)
index 161b1c1c7047f38d961213ced63b4d7a81560803..132a54338f740b0dd40801329c05874f266af0c7 100644 (file)
@@ -835,21 +835,21 @@ int config_parse_cpu_affinity(
 DEFINE_CONFIG_PARSE_ENUM(config_parse_resolv_conf, resolv_conf_mode, ResolvConfMode, "Failed to parse resolv.conf mode");
 
 static const char *const resolv_conf_mode_table[_RESOLV_CONF_MODE_MAX] = {
-        [RESOLV_CONF_OFF] = "off",
-        [RESOLV_CONF_COPY_HOST] = "copy-host",
-        [RESOLV_CONF_COPY_STATIC] = "copy-static",
-        [RESOLV_CONF_COPY_UPLINK] = "copy-uplink",
-        [RESOLV_CONF_COPY_STUB] = "copy-stub",
-        [RESOLV_CONF_REPLACE_HOST] = "replace-host",
+        [RESOLV_CONF_OFF]            = "off",
+        [RESOLV_CONF_COPY_HOST]      = "copy-host",
+        [RESOLV_CONF_COPY_STATIC]    = "copy-static",
+        [RESOLV_CONF_COPY_UPLINK]    = "copy-uplink",
+        [RESOLV_CONF_COPY_STUB]      = "copy-stub",
+        [RESOLV_CONF_REPLACE_HOST]   = "replace-host",
         [RESOLV_CONF_REPLACE_STATIC] = "replace-static",
         [RESOLV_CONF_REPLACE_UPLINK] = "replace-uplink",
-        [RESOLV_CONF_REPLACE_STUB] = "replace-stub",
-        [RESOLV_CONF_BIND_HOST] = "bind-host",
-        [RESOLV_CONF_BIND_STATIC] = "bind-static",
-        [RESOLV_CONF_BIND_UPLINK] = "bind-uplink",
-        [RESOLV_CONF_BIND_STUB] = "bind-stub",
-        [RESOLV_CONF_DELETE] = "delete",
-        [RESOLV_CONF_AUTO] = "auto",
+        [RESOLV_CONF_REPLACE_STUB]   = "replace-stub",
+        [RESOLV_CONF_BIND_HOST]      = "bind-host",
+        [RESOLV_CONF_BIND_STATIC]    = "bind-static",
+        [RESOLV_CONF_BIND_UPLINK]    = "bind-uplink",
+        [RESOLV_CONF_BIND_STUB]      = "bind-stub",
+        [RESOLV_CONF_DELETE]         = "delete",
+        [RESOLV_CONF_AUTO]           = "auto",
 };
 
 DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(resolv_conf_mode, ResolvConfMode, RESOLV_CONF_AUTO);
@@ -914,15 +914,15 @@ int config_parse_link_journal(
         return 0;
 }
 
-DEFINE_CONFIG_PARSE_ENUM(config_parse_timezone, timezone_mode, TimezoneMode, "Failed to parse timezone mode");
+DEFINE_CONFIG_PARSE_ENUM(config_parse_timezone_mode, timezone_mode, TimezoneMode, "Failed to parse timezone mode");
 
 static const char *const timezone_mode_table[_TIMEZONE_MODE_MAX] = {
-        [TIMEZONE_OFF] = "off",
-        [TIMEZONE_COPY] = "copy",
-        [TIMEZONE_BIND] = "bind",
+        [TIMEZONE_OFF]     = "off",
+        [TIMEZONE_COPY]    = "copy",
+        [TIMEZONE_BIND]    = "bind",
         [TIMEZONE_SYMLINK] = "symlink",
-        [TIMEZONE_DELETE] = "delete",
-        [TIMEZONE_AUTO] = "auto",
+        [TIMEZONE_DELETE]  = "delete",
+        [TIMEZONE_AUTO]    = "auto",
 };
 
 DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(timezone_mode, TimezoneMode, TIMEZONE_AUTO);
@@ -930,10 +930,10 @@ DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(timezone_mode, TimezoneMode, TIMEZONE_AU
 DEFINE_CONFIG_PARSE_ENUM(config_parse_userns_ownership, user_namespace_ownership, UserNamespaceOwnership, "Failed to parse user namespace ownership mode");
 
 static const char *const user_namespace_ownership_table[_USER_NAMESPACE_OWNERSHIP_MAX] = {
-        [USER_NAMESPACE_OWNERSHIP_OFF] = "off",
+        [USER_NAMESPACE_OWNERSHIP_OFF]   = "off",
         [USER_NAMESPACE_OWNERSHIP_CHOWN] = "chown",
-        [USER_NAMESPACE_OWNERSHIP_MAP] = "map",
-        [USER_NAMESPACE_OWNERSHIP_AUTO] = "auto",
+        [USER_NAMESPACE_OWNERSHIP_MAP]   = "map",
+        [USER_NAMESPACE_OWNERSHIP_AUTO]  = "auto",
 };
 
 DEFINE_STRING_TABLE_LOOKUP(user_namespace_ownership, UserNamespaceOwnership);
index 8edf8a3552279a246401186b38b00d0a7f7f7137..0bcb285f8621dadf0bf62c073935af6904f2087d 100644 (file)
@@ -268,7 +268,7 @@ CONFIG_PARSER_PROTOTYPE(config_parse_oom_score_adjust);
 CONFIG_PARSER_PROTOTYPE(config_parse_cpu_affinity);
 CONFIG_PARSER_PROTOTYPE(config_parse_resolv_conf);
 CONFIG_PARSER_PROTOTYPE(config_parse_link_journal);
-CONFIG_PARSER_PROTOTYPE(config_parse_timezone);
+CONFIG_PARSER_PROTOTYPE(config_parse_timezone_mode);
 CONFIG_PARSER_PROTOTYPE(config_parse_userns_chown);
 CONFIG_PARSER_PROTOTYPE(config_parse_userns_ownership);
 CONFIG_PARSER_PROTOTYPE(config_parse_bind_user);
index fa556874dffd3b407c47e18b02b02a9c3718adb9..51ae8592fdefe7440ef67e456fd64923ea3c8646 100644 (file)
@@ -4067,9 +4067,9 @@ static int partition_encrypt(Context *context, Partition *p, PartitionTarget *ta
                                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
                                                        "Must provide all PCR values when using TPM2 device key.");
                 } else {
-                        r = tpm2_context_new(arg_tpm2_device, &tpm2_context);
+                        r = tpm2_context_new_or_warn(arg_tpm2_device, &tpm2_context);
                         if (r < 0)
-                                return log_error_errno(r, "Failed to create TPM2 context: %m");
+                                return r;
 
                         if (!tpm2_pcr_values_has_all_values(arg_tpm2_hash_pcr_values, arg_tpm2_n_hash_pcr_values)) {
                                 r = tpm2_pcr_read_missing_values(tpm2_context, arg_tpm2_hash_pcr_values, arg_tpm2_n_hash_pcr_values);
index ead353f5a69704e22f0d312574480fe566991816..ba2b171250ac683eabae4ada36d2cd9cadbcab4f 100644 (file)
@@ -199,7 +199,7 @@ static int extend_now(unsigned pcr, const void *data, size_t size, Tpm2Userspace
         _cleanup_(tpm2_context_unrefp) Tpm2Context *c = NULL;
         int r;
 
-        r = tpm2_context_new(arg_tpm2_device, &c);
+        r = tpm2_context_new_or_warn(arg_tpm2_device, &c);
         if (r < 0)
                 return r;
 
index 1fb9d692a276ad16c4c043b9ae3c28a5c75041b8..4e86b527d3b727a8ff5c9f7c581f7c4bfff9f076 100644 (file)
@@ -43,6 +43,7 @@
 #include "random-util.h"
 #include "recovery-key.h"
 #include "sort-util.h"
+#include "string-table.h"
 #include "terminal-util.h"
 #include "tpm2-util.h"
 #include "unaligned.h"
 #include "varlink-io.systemd.PCRLock.h"
 #include "verbs.h"
 
+typedef enum RecoveryPinMode {
+        RECOVERY_PIN_HIDE,           /* generate a recovery PIN automatically, but don't show it (alias: "no") */
+        RECOVERY_PIN_SHOW,           /* generate a recovery PIN automatically, and display it to the user */
+        RECOVERY_PIN_QUERY,          /* asks the user for a PIN to use interactively (alias: "yes") */
+        _RECOVERY_PIN_MODE_MAX,
+        _RECOVERY_PIN_MODE_INVALID = -EINVAL,
+} RecoveryPinMode;
+
 static PagerFlags arg_pager_flags = 0;
 static JsonFormatFlags arg_json_format_flags = JSON_FORMAT_OFF|JSON_FORMAT_NEWLINE;
 static char **arg_components = NULL;
@@ -62,7 +71,7 @@ static bool arg_raw_description = false;
 static char *arg_location_start = NULL;
 static char *arg_location_end = NULL;
 static TPM2_HANDLE arg_nv_index = 0;
-static bool arg_recovery_pin = false;
+static RecoveryPinMode arg_recovery_pin = RECOVERY_PIN_HIDE;
 static char *arg_policy_path = NULL;
 static bool arg_force = false;
 static BootEntryTokenType arg_entry_token_type = BOOT_ENTRY_TOKEN_AUTO;
@@ -104,6 +113,14 @@ STATIC_DESTRUCTOR_REGISTER(arg_entry_token, freep);
          (UINT32_C(1) << TPM2_PCR_SHIM_POLICY) |             \
          (UINT32_C(1) << TPM2_PCR_SYSTEM_IDENTITY))
 
+static const char* recovery_pin_mode_table[_RECOVERY_PIN_MODE_MAX] = {
+        [RECOVERY_PIN_HIDE]  = "hide",
+        [RECOVERY_PIN_SHOW]  = "show",
+        [RECOVERY_PIN_QUERY] = "query",
+};
+
+DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING_WITH_BOOLEAN(recovery_pin_mode, RecoveryPinMode, RECOVERY_PIN_QUERY);
+
 typedef struct EventLogRecordBank EventLogRecordBank;
 typedef struct EventLogRecord EventLogRecord;
 typedef struct EventLogRegisterBank EventLogRegisterBank;
@@ -1211,7 +1228,7 @@ static int event_log_read_pcrs(EventLog *el) {
 
         assert(el);
 
-        r = tpm2_context_new(NULL, &tc);
+        r = tpm2_context_new_or_warn(/* device= */ NULL, &tc);
         if (r < 0)
                 return r;
 
@@ -2662,7 +2679,7 @@ static int make_pcrlock_record(
                 assert_se(a = tpm2_hash_alg_to_string(*pa));
                 assert_se(md = EVP_get_digestbyname(a));
                 hash_ssize = EVP_MD_size(md);
-                assert_se(hash_ssize > 0);
+                assert(hash_ssize > 0);
                 hash_usize = hash_ssize;
 
                 hash = malloc(hash_usize);
@@ -2691,6 +2708,101 @@ static int make_pcrlock_record(
         return 0;
 }
 
+static void evp_md_ctx_free_all(EVP_MD_CTX *(*md)[TPM2_N_HASH_ALGORITHMS]) {
+        assert(md);
+        FOREACH_ARRAY(alg, *md, TPM2_N_HASH_ALGORITHMS)
+                if (*alg)
+                        EVP_MD_CTX_free(*alg);
+}
+
+static int make_pcrlock_record_from_stream(
+                uint32_t pcr_mask,
+                FILE *f,
+                JsonVariant **ret_records) {
+
+        _cleanup_(json_variant_unrefp) JsonVariant *digests = NULL;
+        _cleanup_(evp_md_ctx_free_all) EVP_MD_CTX *mdctx[TPM2_N_HASH_ALGORITHMS] = {};
+        int r;
+
+        assert(f);
+        assert(ret_records);
+
+        for (size_t i = 0; i < TPM2_N_HASH_ALGORITHMS; i++) {
+                const char *a;
+                const EVP_MD *md;
+
+                assert_se(a = tpm2_hash_alg_to_string(tpm2_hash_algorithms[i]));
+                assert_se(md = EVP_get_digestbyname(a));
+
+                mdctx[i] = EVP_MD_CTX_new();
+                if (!mdctx[i])
+                        return log_oom();
+
+                if (EVP_DigestInit_ex(mdctx[i], md, NULL) != 1)
+                        return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
+                                               "Failed to initialize message digest for %s.", a);
+        }
+
+        for (;;) {
+                uint8_t buffer[64*1024];
+                size_t n;
+
+                n = fread(buffer, 1, sizeof(buffer), f);
+                if (ferror(f))
+                        return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "Failed to read file: %m");
+                if (n == 0 && feof(f))
+                        break;
+
+                for (size_t i = 0; i < TPM2_N_HASH_ALGORITHMS; i++)
+                        if (EVP_DigestUpdate(mdctx[i], buffer, n) != 1)
+                                return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "Unable to hash data.");
+        }
+
+        for (size_t i = 0; i < TPM2_N_HASH_ALGORITHMS; i++) {
+                const char *a;
+                int hash_ssize;
+                unsigned hash_usize;
+
+                assert_se(a = tpm2_hash_alg_to_string(tpm2_hash_algorithms[i]));
+                hash_ssize = EVP_MD_CTX_size(mdctx[i]);
+                assert(hash_ssize > 0 && hash_ssize <= EVP_MAX_MD_SIZE);
+                hash_usize = hash_ssize;
+                unsigned char hash[hash_usize];
+
+                if (EVP_DigestFinal_ex(mdctx[i], hash, &hash_usize) != 1)
+                        return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
+                                               "Failed to finalize hash context for algorithn '%s'.", a);
+
+                r = json_variant_append_arrayb(
+                                &digests,
+                                JSON_BUILD_OBJECT(
+                                                JSON_BUILD_PAIR("hashAlg", JSON_BUILD_STRING(a)),
+                                                JSON_BUILD_PAIR("digest", JSON_BUILD_HEX(hash, hash_usize))));
+                if (r < 0)
+                        return log_error_errno(r, "Failed to build JSON digest object: %m");
+        }
+
+        for (uint32_t i = 0; i < TPM2_PCRS_MAX; i++) {
+                _cleanup_(json_variant_unrefp) JsonVariant *record = NULL;
+
+                if (!FLAGS_SET(pcr_mask, UINT32_C(1) << i))
+                        continue;
+
+                r = json_build(&record,
+                               JSON_BUILD_OBJECT(
+                                               JSON_BUILD_PAIR("pcr", JSON_BUILD_UNSIGNED(i)),
+                                               JSON_BUILD_PAIR("digests", JSON_BUILD_VARIANT(digests))));
+                if (r < 0)
+                        return log_error_errno(r, "Failed to build record object: %m");
+
+                r = json_variant_append_array(ret_records, record);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to append to JSON array: %m");
+        }
+
+        return 0;
+}
+
 static const char *pcrlock_path(const char *default_pcrlock_path) {
         return arg_pcrlock_path ?: arg_pcrlock_auto ? default_pcrlock_path : NULL;
 }
@@ -2754,10 +2866,8 @@ static int unlink_pcrlock(const char *default_pcrlock_path) {
 }
 
 static int verb_lock_raw(int argc, char *argv[], void *userdata) {
-        _cleanup_(json_variant_unrefp) JsonVariant *array = NULL;
-        _cleanup_free_ char *data = NULL;
+        _cleanup_(json_variant_unrefp) JsonVariant *records = NULL;
         _cleanup_fclose_ FILE *f = NULL;
-        size_t size;
         int r;
 
         if (arg_pcr_mask == 0)
@@ -2769,26 +2879,11 @@ static int verb_lock_raw(int argc, char *argv[], void *userdata) {
                         return log_error_errno(errno, "Failed to open '%s': %m", argv[1]);
         }
 
-        r = read_full_stream(f ?: stdin, &data, &size);
+        r = make_pcrlock_record_from_stream(arg_pcr_mask, f ?: stdin, &records);
         if (r < 0)
-                return log_error_errno(r, "Failed to read data from stdin: %m");
-
-        for (uint32_t i = 0; i < TPM2_PCRS_MAX; i++) {
-                _cleanup_(json_variant_unrefp) JsonVariant *record = NULL;
-
-                if (!FLAGS_SET(arg_pcr_mask, UINT32_C(1) << i))
-                        continue;
-
-                r = make_pcrlock_record(i, data, size, &record);
-                if (r < 0)
-                        return r;
-
-                r = json_variant_append_array(&array, record);
-                if (r < 0)
-                        return log_error_errno(r, "Failed to append to JSON array: %m");
-        }
+                return r;
 
-        return write_pcrlock(array, NULL);
+        return write_pcrlock(records, NULL);
 }
 
 static int verb_unlock_simple(int argc, char *argv[], void *userdata) {
@@ -2816,7 +2911,7 @@ static int verb_lock_secureboot_policy(int argc, char *argv[], void *userdata) {
         /* Generates expected records from the current SecureBoot state, as readable in the EFI variables
          * right now. */
 
-        FOREACH_ARRAY(vv, variables, ELEMENTSOF(variables)) {
+        FOREACH_ELEMENT(vv, variables) {
                 _cleanup_(json_variant_unrefp) JsonVariant *record = NULL;
 
                 _cleanup_free_ char *name = NULL;
@@ -3796,10 +3891,9 @@ static int verb_unlock_kernel_cmdline(int argc, char *argv[], void *userdata) {
 }
 
 static int verb_lock_kernel_initrd(int argc, char *argv[], void *userdata) {
-        _cleanup_(json_variant_unrefp) JsonVariant *record = NULL, *array = NULL;
-        _cleanup_free_ void *data = NULL;
+        _cleanup_(json_variant_unrefp) JsonVariant *records = NULL;
         _cleanup_fclose_ FILE *f = NULL;
-        size_t size;
+        uint32_t pcr_mask = UINT32_C(1) << TPM2_PCR_KERNEL_INITRD;
         int r;
 
         if (argc >= 2) {
@@ -3808,19 +3902,11 @@ static int verb_lock_kernel_initrd(int argc, char *argv[], void *userdata) {
                         return log_error_errno(errno, "Failed to open '%s': %m", argv[1]);
         }
 
-        r = read_full_stream(f ?: stdin, (char**) &data, &size);
-        if (r < 0)
-                return log_error_errno(r, "Failed to read data from stdin: %m");
-
-        r = make_pcrlock_record(TPM2_PCR_KERNEL_INITRD /* = 9 */, data, size, &record);
+        r = make_pcrlock_record_from_stream(pcr_mask, f ?: stdin, &records);
         if (r < 0)
                 return r;
 
-        r = json_variant_new_array(&array, &record, 1);
-        if (r < 0)
-                return log_error_errno(r, "Failed to create record array: %m");
-
-        r = write_pcrlock(array, PCRLOCK_KERNEL_INITRD_PATH);
+        r = write_pcrlock(records, PCRLOCK_KERNEL_INITRD_PATH);
         if (r < 0)
                 return r;
 
@@ -4320,7 +4406,7 @@ static int write_boot_policy_file(const char *json_text) {
         return 1;
 }
 
-static int make_policy(bool force, bool recovery_pin) {
+static int make_policy(bool force, RecoveryPinMode recovery_pin_mode) {
         int r;
 
         /* Here's how this all works: after predicting all possible PCR values for next boot (with
@@ -4404,9 +4490,9 @@ static int make_policy(bool force, bool recovery_pin) {
         }
 
         _cleanup_(tpm2_context_unrefp) Tpm2Context *tc = NULL;
-        r = tpm2_context_new(NULL, &tc);
+        r = tpm2_context_new_or_warn(/* device= */ NULL, &tc);
         if (r < 0)
-                return log_error_errno(r, "Failed to allocate TPM2 context: %m");
+                return r;
 
         if (!tpm2_supports_command(tc, TPM2_CC_PolicyAuthorizeNV))
                 return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "TPM2 does not support PolicyAuthorizeNV command, refusing.");
@@ -4444,7 +4530,7 @@ static int make_policy(bool force, bool recovery_pin) {
 
         /* Acquire a recovery PIN, either from the user, or create a randomized one */
         _cleanup_(erase_and_freep) char *pin = NULL;
-        if (recovery_pin) {
+        if (recovery_pin_mode == RECOVERY_PIN_QUERY) {
                 r = getenv_steal_erase("PIN", &pin);
                 if (r < 0)
                         return log_error_errno(r, "Failed to acquire PIN from environment: %m");
@@ -4473,16 +4559,16 @@ static int make_policy(bool force, bool recovery_pin) {
                 }
 
         } else if (!have_old_policy) {
-                char rnd[256];
-
-                r = crypto_random_bytes(rnd, sizeof(rnd));
+                r = make_recovery_key(&pin);
                 if (r < 0)
                         return log_error_errno(r, "Failed to generate a randomized recovery PIN: %m");
 
-                (void) base64mem(rnd, sizeof(rnd), &pin);
-                explicit_bzero_safe(rnd, sizeof(rnd));
-                if (!pin)
-                        return log_oom();
+                if (recovery_pin_mode == RECOVERY_PIN_SHOW)
+                        printf("%s Selected recovery PIN is: %s%s%s\n",
+                               special_glyph(SPECIAL_GLYPH_LOCK_AND_KEY),
+                               ansi_highlight_cyan(),
+                               pin,
+                               ansi_normal());
         }
 
         _cleanup_(tpm2_handle_freep) Tpm2Handle *nv_handle = NULL;
@@ -4500,7 +4586,7 @@ static int make_policy(bool force, bool recovery_pin) {
         CLEANUP_ERASE(auth);
 
         if (pin) {
-                r = tpm2_get_pin_auth(TPM2_ALG_SHA256, pin, &auth);
+                r = tpm2_auth_value_from_pin(TPM2_ALG_SHA256, pin, &auth);
                 if (r < 0)
                         return log_error_errno(r, "Failed to hash PIN: %m");
         } else {
@@ -4567,15 +4653,28 @@ static int make_policy(bool force, bool recovery_pin) {
                 log_info("Retrieved PIN from TPM2 in %s.", FORMAT_TIMESPAN(usec_sub_unsigned(now(CLOCK_MONOTONIC), pin_start_usec), 1));
         }
 
-        TPM2B_NV_PUBLIC nv_public = {};
+        /* Now convert the PIN into an HMAC-SHA256 key that we can use in PolicySigned to protect access to the nvindex with */
+        _cleanup_(tpm2_handle_freep) Tpm2Handle *pin_handle = NULL;
+        r = tpm2_hmac_key_from_pin(tc, encryption_session, &auth, &pin_handle);
+        if (r < 0)
+                return r;
 
+        TPM2B_NV_PUBLIC nv_public = {};
         usec_t nv_index_start_usec = now(CLOCK_MONOTONIC);
 
         if (!iovec_is_set(&nv_blob)) {
+                _cleanup_(Esys_Freep) TPM2B_NAME *pin_name = NULL;
+                r = tpm2_get_name(
+                                tc,
+                                pin_handle,
+                                &pin_name);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to get name of PIN from TPM2: %m");
+
                 TPM2B_DIGEST recovery_policy_digest = TPM2B_DIGEST_MAKE(NULL, TPM2_SHA256_DIGEST_SIZE);
-                r = tpm2_calculate_policy_auth_value(&recovery_policy_digest);
+                r = tpm2_calculate_policy_signed(&recovery_policy_digest, pin_name);
                 if (r < 0)
-                        return log_error_errno(r, "Failed to calculate authentication value policy: %m");
+                        return log_error_errno(r, "Failed to calculate PolicySigned policy: %m");
 
                 log_debug("Allocating NV index to write PCR policy to...");
                 r = tpm2_define_policy_nv_index(
@@ -4583,8 +4682,6 @@ static int make_policy(bool force, bool recovery_pin) {
                                 encryption_session,
                                 arg_nv_index,
                                 &recovery_policy_digest,
-                                pin,
-                                &auth,
                                 &nv_index,
                                 &nv_handle,
                                 &nv_public);
@@ -4594,10 +4691,6 @@ static int make_policy(bool force, bool recovery_pin) {
                         return log_error_errno(r, "Failed to allocate NV index: %m");
         }
 
-        r = tpm2_set_auth_binary(tc, nv_handle, &auth);
-        if (r < 0)
-                return log_error_errno(r, "Failed to set authentication value on NV index: %m");
-
         _cleanup_(tpm2_handle_freep) Tpm2Handle *policy_session = NULL;
         r = tpm2_make_policy_session(
                         tc,
@@ -4607,9 +4700,11 @@ static int make_policy(bool force, bool recovery_pin) {
         if (r < 0)
                 return log_error_errno(r, "Failed to allocate policy session: %m");
 
-        r = tpm2_policy_auth_value(
+        r = tpm2_policy_signed_hmac_sha256(
                         tc,
                         policy_session,
+                        pin_handle,
+                        &IOVEC_MAKE(auth.buffer, auth.size),
                         /* ret_policy_digest= */ NULL);
         if (r < 0)
                 return log_error_errno(r, "Failed to submit authentication value policy: %m");
@@ -4741,7 +4836,7 @@ static int undefine_policy_nv_index(
         assert(srk_blob);
 
         _cleanup_(tpm2_context_unrefp) Tpm2Context *tc = NULL;
-        r = tpm2_context_new(NULL, &tc);
+        r = tpm2_context_new_or_warn(/* device= */ NULL, &tc);
         if (r < 0)
                 return r;
 
@@ -5044,9 +5139,9 @@ static int parse_argv(int argc, char *argv[]) {
                 }
 
                 case ARG_RECOVERY_PIN:
-                        r = parse_boolean_argument("--recovery-pin", optarg, &arg_recovery_pin);
-                        if (r < 0)
-                                return r;
+                        arg_recovery_pin = recovery_pin_mode_from_string(optarg);
+                        if (arg_recovery_pin < 0)
+                                return log_error_errno(arg_recovery_pin, "Failed to parse --recovery-pin= mode: %s", optarg);
                         break;
 
                 case ARG_PCRLOCK:
@@ -5210,7 +5305,7 @@ static int vl_method_make_policy(Varlink *link, JsonVariant *parameters, Varlink
         if (r != 0)
                 return r;
 
-        r = make_policy(p.force, /* recovery_key= */ false);
+        r = make_policy(p.force, /* recovery_key= */ RECOVERY_PIN_HIDE);
         if (r < 0)
                 return r;
         if (r == 0)
index 60dc98c5baf313feca9252a499f37ac85ab5621c..53418c417b51d072f0545906a5966534dff345f9 100644 (file)
@@ -43,6 +43,7 @@
 #include "strv.h"
 #include "tmpfile-util.h"
 #include "user-util.h"
+#include "vpick.h"
 
 /* Markers used in the first line of our 20-portable.conf unit file drop-in to determine, that a) the unit file was
  * dropped there by the portable service logic and b) for which image it was dropped there. */
@@ -564,6 +565,7 @@ static int extract_image_and_extensions(
         _cleanup_free_ char *id = NULL, *version_id = NULL, *sysext_level = NULL, *confext_level = NULL;
         _cleanup_(portable_metadata_unrefp) PortableMetadata *os_release = NULL;
         _cleanup_ordered_hashmap_free_ OrderedHashmap *extension_images = NULL, *extension_releases = NULL;
+        _cleanup_(pick_result_done) PickResult result = PICK_RESULT_NULL;
         _cleanup_hashmap_free_ Hashmap *unit_files = NULL;
         _cleanup_strv_free_ char **valid_prefixes = NULL;
         _cleanup_(image_unrefp) Image *image = NULL;
@@ -572,7 +574,27 @@ static int extract_image_and_extensions(
 
         assert(name_or_path);
 
-        r = image_find_harder(IMAGE_PORTABLE, name_or_path, NULL, &image);
+        /* If we get a path, then check if it can be resolved with vpick. We need this as we might just
+         * get a simple image name, which would make vpick error out. */
+        if (path_is_absolute(name_or_path)) {
+                r = path_pick(/* toplevel_path= */ NULL,
+                              /* toplevel_fd= */ AT_FDCWD,
+                              name_or_path,
+                              &pick_filter_image_any,
+                              PICK_ARCHITECTURE|PICK_TRIES|PICK_RESOLVE,
+                              &result);
+                if (r < 0)
+                        return r;
+                if (!result.path)
+                        return log_debug_errno(
+                                        SYNTHETIC_ERRNO(ENOENT),
+                                        "No matching entry in .v/ directory %s found.",
+                                        name_or_path);
+
+                name_or_path = result.path;
+        }
+
+        r = image_find_harder(IMAGE_PORTABLE, name_or_path, /* root= */ NULL, &image);
         if (r < 0)
                 return r;
 
@@ -588,9 +610,29 @@ static int extract_image_and_extensions(
                 }
 
                 STRV_FOREACH(p, extension_image_paths) {
+                        _cleanup_(pick_result_done) PickResult ext_result = PICK_RESULT_NULL;
                         _cleanup_(image_unrefp) Image *new = NULL;
+                        const char *path = *p;
+
+                        if (path_is_absolute(*p)) {
+                                r = path_pick(/* toplevel_path= */ NULL,
+                                              /* toplevel_fd= */ AT_FDCWD,
+                                              *p,
+                                              &pick_filter_image_any,
+                                              PICK_ARCHITECTURE|PICK_TRIES|PICK_RESOLVE,
+                                              &ext_result);
+                                if (r < 0)
+                                        return r;
+                                if (!ext_result.path)
+                                        return log_debug_errno(
+                                                        SYNTHETIC_ERRNO(ENOENT),
+                                                        "No matching entry in .v/ directory %s found.",
+                                                        *p);
+
+                                path = ext_result.path;
+                        }
 
-                        r = image_find_harder(IMAGE_PORTABLE, *p, NULL, &new);
+                        r = image_find_harder(IMAGE_PORTABLE, path, NULL, &new);
                         if (r < 0)
                                 return r;
 
@@ -1691,6 +1733,7 @@ static bool marker_matches_images(const char *marker, const char *name_or_path,
         while (!isempty(marker))
                 STRV_FOREACH(image_name_or_path, root_and_extensions) {
                         _cleanup_free_ char *image = NULL, *base_image = NULL, *base_image_name_or_path = NULL;
+                        _cleanup_(pick_result_done) PickResult result = PICK_RESULT_NULL;
 
                         r = extract_first_word(&marker, &image, ":", EXTRACT_UNQUOTE|EXTRACT_RETAIN_ESCAPE);
                         if (r < 0)
@@ -1702,9 +1745,23 @@ static bool marker_matches_images(const char *marker, const char *name_or_path,
                         if (r < 0)
                                 return log_debug_errno(r, "Failed to extract image name from %s, ignoring: %m", image);
 
-                        r = path_extract_image_name(*image_name_or_path, &base_image_name_or_path);
+                        r = path_pick(/* toplevel_path= */ NULL,
+                                      /* toplevel_fd= */ AT_FDCWD,
+                                      *image_name_or_path,
+                                      &pick_filter_image_any,
+                                      PICK_ARCHITECTURE|PICK_TRIES|PICK_RESOLVE,
+                                      &result);
+                        if (r < 0)
+                                return r;
+                        if (!result.path)
+                                return log_debug_errno(
+                                                SYNTHETIC_ERRNO(ENOENT),
+                                                "No matching entry in .v/ directory %s found.",
+                                                *image_name_or_path);
+
+                        r = path_extract_image_name(result.path, &base_image_name_or_path);
                         if (r < 0)
-                                return log_debug_errno(r, "Failed to extract image name from %s, ignoring: %m", *image_name_or_path);
+                                return log_debug_errno(r, "Failed to extract image name from %s, ignoring: %m", result.path);
 
                         if (!streq(base_image, base_image_name_or_path)) {
                                 if (match_all)
index 0f67e4432f2160dfc337518d920b3bc84e4626a6..57b930d6cba41be833bc6642ffdb5fa36fa3e4df 100644 (file)
@@ -50,6 +50,7 @@ static bool arg_now = false;
 static bool arg_no_block = false;
 static char **arg_extension_images = NULL;
 static bool arg_force = false;
+static bool arg_clean = false;
 
 STATIC_DESTRUCTOR_REGISTER(arg_extension_images, strv_freep);
 
@@ -769,13 +770,49 @@ static int maybe_stop_enable_restart(sd_bus *bus, sd_bus_message *reply) {
         return 0;
 }
 
-static int maybe_stop_disable(sd_bus *bus, char *image, char *argv[]) {
+static int maybe_clean_units(sd_bus *bus, char **units) {
+        int r;
+
+        assert(bus);
+
+        if (!arg_clean)
+                return 0;
+
+        STRV_FOREACH(name, units) {
+                _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+                _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
+
+                r = bus_message_new_method_call(bus, &m, bus_systemd_mgr, "CleanUnit");
+                if (r < 0)
+                        return bus_log_create_error(r);
+
+                r = sd_bus_message_append(m, "s", *name);
+                if (r < 0)
+                        return bus_log_create_error(r);
+
+                r = sd_bus_message_append_strv(m, STRV_MAKE("all", "fdstore"));
+                if (r < 0)
+                        return bus_log_create_error(r);
+
+                r = sd_bus_call(bus, m, 0, &error, NULL);
+                if (r < 0)
+                        return log_error_errno(
+                                        r,
+                                        "Failed to call CleanUnit on portable service %s: %s",
+                                        *name,
+                                        bus_error_message(&error, r));
+        }
+
+        return 0;
+}
+
+static int maybe_stop_disable_clean(sd_bus *bus, char *image, char *argv[]) {
         _cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *wait = NULL;
         _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
-        _cleanup_strv_free_ char **matches = NULL;
+        _cleanup_strv_free_ char **matches = NULL, **units = NULL;
         int r;
 
-        if (!arg_enable && !arg_now)
+        if (!arg_enable && !arg_now && !arg_clean)
                 return 0;
 
         r = determine_matches(argv[1], argv + 2, true, &matches);
@@ -829,6 +866,10 @@ static int maybe_stop_disable(sd_bus *bus, char *image, char *argv[]) {
 
                 (void) maybe_start_stop_restart(bus, name, "StopUnit", wait);
                 (void) maybe_enable_disable(bus, name, false);
+
+                r = strv_extend(&units, name);
+                if (r < 0)
+                        return log_oom();
         }
 
         r = sd_bus_message_exit_container(reply);
@@ -840,6 +881,9 @@ static int maybe_stop_disable(sd_bus *bus, char *image, char *argv[]) {
         if (r < 0)
                 return r;
 
+        /* Need to ensure all units are stopped before calling CleanUnit, as files might be in use. */
+        (void) maybe_clean_units(bus, units);
+
         return 0;
 }
 
@@ -942,7 +986,7 @@ static int detach_image(int argc, char *argv[], void *userdata) {
 
         (void) polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
 
-        (void) maybe_stop_disable(bus, image, argv);
+        (void) maybe_stop_disable_clean(bus, image, argv);
 
         method = strv_isempty(arg_extension_images) && !arg_force ? "DetachImage" : "DetachImageWithExtensions";
 
@@ -1265,6 +1309,9 @@ static int help(int argc, char *argv[], void *userdata) {
                "     --extension=PATH         Extend the image with an overlay\n"
                "     --force                  Skip 'already active' check when attaching or\n"
                "                              detaching an image (with extensions)\n"
+               "     --clean                  When detaching, also remove configuration, state,\n"
+               "                              cache, logs or runtime data of the portable\n"
+               "                              service(s)\n"
                "\nSee the %s for details.\n",
                program_invocation_short_name,
                ansi_highlight(),
@@ -1291,6 +1338,7 @@ static int parse_argv(int argc, char *argv[]) {
                 ARG_NO_BLOCK,
                 ARG_EXTENSION,
                 ARG_FORCE,
+                ARG_CLEAN,
         };
 
         static const struct option options[] = {
@@ -1312,6 +1360,7 @@ static int parse_argv(int argc, char *argv[]) {
                 { "no-block",        no_argument,       NULL, ARG_NO_BLOCK        },
                 { "extension",       required_argument, NULL, ARG_EXTENSION       },
                 { "force",           no_argument,       NULL, ARG_FORCE           },
+                { "clean",           no_argument,       NULL, ARG_CLEAN           },
                 {}
         };
 
@@ -1421,6 +1470,10 @@ static int parse_argv(int argc, char *argv[]) {
                         arg_force = true;
                         break;
 
+                case ARG_CLEAN:
+                        arg_clean = true;
+                        break;
+
                 case '?':
                         return -EINVAL;
 
index fbcee7fc634a80e35edbc1fe10589a3a0d243659..3112ccb677b75041418706a2c0eeca2e011fd719 100644 (file)
@@ -392,9 +392,6 @@ int dnstls_manager_init(Manager *manager) {
 
         assert(manager);
 
-        ERR_load_crypto_strings();
-        SSL_load_error_strings();
-
         manager->dnstls_data.ctx = SSL_CTX_new(TLS_client_method());
         if (!manager->dnstls_data.ctx)
                 return -ENOMEM;
index 2930552be486bb84afa84a3f53a417a6ae921a30..e12189f298be603479cdb7c45e3b75b97121731e 100644 (file)
@@ -187,7 +187,7 @@ static void log_job_error_with_service_result(const char* service, const char *r
         }
 
         if (!isempty(result))
-                FOREACH_ARRAY(i, explanations, ELEMENTSOF(explanations))
+                FOREACH_ELEMENT(i, explanations)
                         if (streq(result, i->result)) {
                                 log_error("Job for %s failed because %s.\n"
                                           "See \"%s status %s\" and \"%s -xeu %s\" for details.\n",
index 49963ffe3412a553da69e3a838a7ba658481a22c..6ccf822064f47cf072dac425457fa0a3c0596ebc 100644 (file)
@@ -18,7 +18,7 @@ typedef struct WaitForItem {
         sd_bus_slot *slot_get_all;
         sd_bus_slot *slot_properties_changed;
 
-        bus_wait_for_units_unit_callback unit_callback;
+        bus_wait_for_units_unit_callback_t unit_callback;
         void *userdata;
 
         char *active_state;
@@ -32,11 +32,6 @@ typedef struct BusWaitForUnits {
 
         Hashmap *items;
 
-        bus_wait_for_units_ready_callback ready_callback;
-        void *userdata;
-
-        WaitForItem *current;
-
         BusWaitForUnitsState state;
         bool has_failed:1;
 } BusWaitForUnits;
@@ -64,9 +59,6 @@ static WaitForItem *wait_for_item_free(WaitForItem *item) {
                 }
 
                 assert_se(hashmap_remove_value(item->parent->items, item->bus_path, item));
-
-                if (item->parent->current == item)
-                        item->parent->current = NULL;
         }
 
         sd_bus_slot_unref(item->slot_properties_changed);
@@ -82,8 +74,6 @@ static WaitForItem *wait_for_item_free(WaitForItem *item) {
 DEFINE_TRIVIAL_CLEANUP_FUNC(WaitForItem*, wait_for_item_free);
 
 static void call_unit_callback_and_wait(BusWaitForUnits *d, WaitForItem *item, bool good) {
-        d->current = item;
-
         if (item->unit_callback)
                 item->unit_callback(d, item->bus_path, good, item->userdata);
 
@@ -112,11 +102,7 @@ static int match_disconnected(sd_bus_message *m, void *userdata, sd_bus_error *e
         log_warning("D-Bus connection terminated while waiting for unit.");
 
         bus_wait_for_units_clear(d);
-
-        if (d->ready_callback)
-                d->ready_callback(d, BUS_WAIT_FAILURE, d->userdata);
-        else /* If no ready callback is specified close the connection so that the event loop exits */
-                sd_bus_close(sd_bus_message_get_bus(m));
+        sd_bus_close(sd_bus_message_get_bus(m));
 
         return 0;
 }
@@ -172,13 +158,6 @@ static bool bus_wait_for_units_is_ready(BusWaitForUnits *d) {
         return hashmap_isempty(d->items);
 }
 
-void bus_wait_for_units_set_ready_callback(BusWaitForUnits *d, bus_wait_for_units_ready_callback callback, void *userdata) {
-        assert(d);
-
-        d->ready_callback = callback;
-        d->userdata = userdata;
-}
-
 static void bus_wait_for_units_check_ready(BusWaitForUnits *d) {
         assert(d);
 
@@ -186,9 +165,6 @@ static void bus_wait_for_units_check_ready(BusWaitForUnits *d) {
                 return;
 
         d->state = d->has_failed ? BUS_WAIT_FAILURE : BUS_WAIT_SUCCESS;
-
-        if (d->ready_callback)
-                d->ready_callback(d, d->state, d->userdata);
 }
 
 static void wait_for_item_check_ready(WaitForItem *item) {
@@ -221,32 +197,26 @@ static void wait_for_item_check_ready(WaitForItem *item) {
         bus_wait_for_units_check_ready(d);
 }
 
-static int property_map_job(
+static int property_map_job_id(
                 sd_bus *bus,
                 const char *member,
                 sd_bus_message *m,
                 sd_bus_error *error,
                 void *userdata) {
 
-        WaitForItem *item = ASSERT_PTR(userdata);
-        const char *path;
-        uint32_t id;
-        int r;
+        uint32_t *job_id = ASSERT_PTR(userdata);
 
-        r = sd_bus_message_read(m, "(uo)", &id, &path);
-        if (r < 0)
-                return r;
+        assert(m);
 
-        item->job_id = id;
-        return 0;
+        return sd_bus_message_read(m, "(uo)", job_id, /* path = */ NULL);
 }
 
 static int wait_for_item_parse_properties(WaitForItem *item, sd_bus_message *m) {
 
         static const struct bus_properties_map map[] = {
-                { "ActiveState", "s",    NULL,             offsetof(WaitForItem, active_state) },
-                { "Job",         "(uo)", property_map_job, 0                                   },
-                { "CleanResult", "s",    NULL,             offsetof(WaitForItem, clean_result) },
+                { "ActiveState", "s",    NULL,                offsetof(WaitForItem, active_state) },
+                { "Job",         "(uo)", property_map_job_id, offsetof(WaitForItem, job_id)       },
+                { "CleanResult", "s",    NULL,                offsetof(WaitForItem, clean_result) },
                 {}
         };
 
@@ -315,20 +285,23 @@ int bus_wait_for_units_add_unit(
                 BusWaitForUnits *d,
                 const char *unit,
                 BusWaitForUnitsFlags flags,
-                bus_wait_for_units_unit_callback callback,
+                bus_wait_for_units_unit_callback_t callback,
                 void *userdata) {
 
         _cleanup_(wait_for_item_freep) WaitForItem *item = NULL;
+        _cleanup_free_ char *bus_path = NULL;
         int r;
 
         assert(d);
         assert(unit);
+        assert((flags & _BUS_WAIT_FOR_TARGET) != 0);
 
-        assert(flags != 0);
+        bus_path = unit_dbus_path_from_name(unit);
+        if (!bus_path)
+                return -ENOMEM;
 
-        r = hashmap_ensure_allocated(&d->items, &string_hash_ops);
-        if (r < 0)
-                return r;
+        if (hashmap_contains(d->items, bus_path))
+                return 0;
 
         item = new(WaitForItem, 1);
         if (!item)
@@ -336,15 +309,12 @@ int bus_wait_for_units_add_unit(
 
         *item = (WaitForItem) {
                 .flags = flags,
-                .bus_path = unit_dbus_path_from_name(unit),
+                .bus_path = TAKE_PTR(bus_path),
                 .unit_callback = callback,
                 .userdata = userdata,
                 .job_id = UINT32_MAX,
         };
 
-        if (!item->bus_path)
-                return -ENOMEM;
-
         if (!FLAGS_SET(item->flags, BUS_WAIT_REFFED)) {
                 r = sd_bus_call_method_async(
                                 d->bus,
@@ -388,14 +358,16 @@ int bus_wait_for_units_add_unit(
         if (r < 0)
                 return log_debug_errno(r, "Failed to request properties of unit %s: %m", unit);
 
-        r = hashmap_put(d->items, item->bus_path, item);
+        r = hashmap_ensure_put(&d->items, &string_hash_ops, item->bus_path, item);
         if (r < 0)
                 return r;
+        assert(r > 0);
 
         d->state = BUS_WAIT_RUNNING;
         item->parent = d;
         TAKE_PTR(item);
-        return 0;
+
+        return 1;
 }
 
 int bus_wait_for_units_run(BusWaitForUnits *d) {
index c87eabcbbceb89aaa9ab6d4ebeecf7304fcbba73..09c4f18906c3ec436d5977489b8b45db01cc848c 100644 (file)
@@ -19,17 +19,22 @@ typedef enum BusWaitForUnitsFlags {
         BUS_WAIT_FOR_INACTIVE        = 1 << 1, /* Wait until the unit is back in inactive or dead state */
         BUS_WAIT_NO_JOB              = 1 << 2, /* Wait until there's no more job pending */
         BUS_WAIT_REFFED              = 1 << 3, /* The unit is already reffed with RefUnit() */
+        _BUS_WAIT_FOR_TARGET         = BUS_WAIT_FOR_MAINTENANCE_END|BUS_WAIT_FOR_INACTIVE|BUS_WAIT_NO_JOB,
 } BusWaitForUnitsFlags;
 
-typedef void (*bus_wait_for_units_ready_callback)(BusWaitForUnits *d, BusWaitForUnitsState state, void *userdata);
-typedef void (*bus_wait_for_units_unit_callback)(BusWaitForUnits *d, const char *unit_path, bool good, void *userdata);
+typedef void (*bus_wait_for_units_unit_callback_t)(BusWaitForUnits *d, const char *unit_path, bool good, void *userdata);
 
 int bus_wait_for_units_new(sd_bus *bus, BusWaitForUnits **ret);
 
 BusWaitForUnits* bus_wait_for_units_free(BusWaitForUnits *d);
 DEFINE_TRIVIAL_CLEANUP_FUNC(BusWaitForUnits*, bus_wait_for_units_free);
 
-BusWaitForUnitsState bus_wait_for_units_state(BusWaitForUnits *d);
-void bus_wait_for_units_set_ready_callback(BusWaitForUnits *d, bus_wait_for_units_ready_callback callback, void *userdata);
-int bus_wait_for_units_add_unit(BusWaitForUnits *d, const char *unit, BusWaitForUnitsFlags flags, bus_wait_for_units_unit_callback callback, void *userdata);
+int bus_wait_for_units_add_unit(
+                BusWaitForUnits *d,
+                const char *unit,
+                BusWaitForUnitsFlags flags,
+                bus_wait_for_units_unit_callback_t callback,
+                void *userdata);
+
 int bus_wait_for_units_run(BusWaitForUnits *d);
+BusWaitForUnitsState bus_wait_for_units_state(BusWaitForUnits *d);
index b0cbe3007202325f5847034da9f760dc960f7aea..37d02325b749ea61bc03473e59190abda8fb86ed 100644 (file)
@@ -27,10 +27,11 @@ int clock_get_hwclock(struct tm *tm) {
         if (fd < 0)
                 return -errno;
 
-        /* This leaves the timezone fields of struct tm
-         * uninitialized! */
+        /* This leaves the timezone fields of struct tm uninitialized! */
         if (ioctl(fd, RTC_RD_TIME, tm) < 0)
-                return -errno;
+                /* Some drivers return -EINVAL in case the time could not be kept, i.e. power loss
+                 * happened. Let's turn that into a clearly recognizable error */
+                return errno == EINVAL ? -ENODATA : -errno;
 
         /* We don't know daylight saving, so we reset this in order not
          * to confuse mktime(). */
index 6df4be8333b7efe845675bd914e352b749ce489c..a13db8ee4fb6a9c736ba0e149adf73b888ed573c 100644 (file)
@@ -41,7 +41,7 @@ CompareOperator parse_compare_operator(const char **s, CompareOperatorParseFlags
                   * parse_compare_operator() are use on the same string? */
                 return _COMPARE_OPERATOR_INVALID;
 
-        FOREACH_ARRAY(i, table, ELEMENTSOF(table)) {
+        FOREACH_ELEMENT(i, table) {
                 const char *e;
 
                 if (i->need_mask != 0 && !FLAGS_SET(flags, i->need_mask))
index e2d3b65f88d22155562d3d52b9593b402f12f484..277f4ee42fd82b4e1e0cb5be1e64b8c029eef916 100644 (file)
@@ -1979,3 +1979,37 @@ int config_parse_unsigned_bounded(
 DEFINE_CONFIG_PARSE(config_parse_percent, parse_percent, "Failed to parse percent value");
 DEFINE_CONFIG_PARSE(config_parse_permyriad, parse_permyriad, "Failed to parse permyriad value");
 DEFINE_CONFIG_PARSE_PTR(config_parse_sec_fix_0, parse_sec_fix_0, usec_t, "Failed to parse time value");
+
+int config_parse_timezone(
+                const char *unit,
+                const char *filename,
+                unsigned line,
+                const char *section,
+                unsigned section_line,
+                const char *lvalue,
+                int ltype,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
+
+        char **tz = ASSERT_PTR(data);
+        int r;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+
+        if (isempty(rvalue)) {
+                *tz = mfree(*tz);
+                return 0;
+        }
+
+        r = verify_timezone(rvalue, LOG_WARNING);
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "Timezone is not valid, ignoring assignment: %s", rvalue);
+                return 0;
+        }
+
+        return free_and_strdup_warn(tz, rvalue);
+}
index 254d6cb70bc133e97c6a63a9760637eed003d5b6..35e203cb1271a2e20838fb6f57f565fe4709434d 100644 (file)
@@ -250,6 +250,7 @@ CONFIG_PARSER_PROTOTYPE(config_parse_percent);
 CONFIG_PARSER_PROTOTYPE(config_parse_permyriad);
 CONFIG_PARSER_PROTOTYPE(config_parse_pid);
 CONFIG_PARSER_PROTOTYPE(config_parse_sec_fix_0);
+CONFIG_PARSER_PROTOTYPE(config_parse_timezone);
 
 typedef enum Disabled {
         DISABLED_CONFIGURATION,
index 2374fa3b62f9f705526c48ed34a32f62428cb451..8389774db75e601065aa0bf2dd7164b5176d5a68 100644 (file)
@@ -335,7 +335,7 @@ int copy_bytes_full(
                 if (try_cfr) {
                         n = try_copy_file_range(fdf, NULL, fdt, NULL, m, 0u);
                         if (n < 0) {
-                                if (!IN_SET(n, -EINVAL, -ENOSYS, -EXDEV, -EBADF))
+                                if (!IN_SET(n, -EINVAL, -ENOSYS, -EXDEV, -EBADF, -EOPNOTSUPP))
                                         return n;
 
                                 try_cfr = false;
index bd2af6d172477df28534fa476871634db3a6d0ca..fd7388127b1dd349d9dbdda5d6a602b6a058ffa2 100644 (file)
@@ -919,9 +919,9 @@ int encrypt_credential_and_warn(
                         tpm2_pubkey_pcr_mask = 0;
 
                 _cleanup_(tpm2_context_unrefp) Tpm2Context *tpm2_context = NULL;
-                r = tpm2_context_new(tpm2_device, &tpm2_context);
+                r = tpm2_context_new_or_warn(tpm2_device, &tpm2_context);
                 if (r < 0)
-                        return log_error_errno(r, "Failed to create TPM2 context: %m");
+                        return r;
 
                 r = tpm2_get_best_pcr_bank(tpm2_context, tpm2_hash_pcr_mask | tpm2_pubkey_pcr_mask, &tpm2_pcr_bank);
                 if (r < 0)
index ee664a95a1abab21815d993457d4e0597fe93b3e..335bb2eb89045f5b3a7bbbd6ca6df838d7b51691 100644 (file)
@@ -143,9 +143,9 @@ int acquire_tpm2_key(
         }
 
         _cleanup_(tpm2_context_unrefp) Tpm2Context *tpm2_context = NULL;
-        r = tpm2_context_new(device, &tpm2_context);
+        r = tpm2_context_new_or_warn(device, &tpm2_context);
         if (r < 0)
-                return log_error_errno(r, "Failed to create TPM2 context: %m");
+                return r;
 
         if (!(flags & TPM2_FLAGS_USE_PIN)) {
                 r = tpm2_unseal(tpm2_context,
index 3592b0a6505ade49dcde4715f61d275a91769e07..459e7279921f2c278d56ae83d4dbd37c8ac81f66 100644 (file)
@@ -119,7 +119,7 @@ int make_inaccessible_nodes(
          * to lock down these nodes as much as we can, but otherwise try to match them as closely as possible with the
          * underlying file, i.e. in the best case we offer the same node type as the underlying node. */
 
-        FOREACH_ARRAY(m, table, ELEMENTSOF(table)) {
+        FOREACH_ELEMENT(m, table) {
                 _cleanup_free_ char *path = NULL;
                 mode_t inode_type = *m;
                 const char *fn;
index b7a97f2b63aa475e20d3b7cae2d5b69ae45772bb..4e7a2049cf4f1fde1e83a2cfd173676df3cb3a29 100644 (file)
@@ -639,7 +639,7 @@ int image_find(ImageClass class,
                                         .type_mask = endswith(suffix, ".raw") ? (UINT32_C(1) << DT_REG) | (UINT32_C(1) << DT_BLK) : (UINT32_C(1) << DT_DIR),
                                         .basename = name,
                                         .architecture = _ARCHITECTURE_INVALID,
-                                        .suffix = suffix,
+                                        .suffix = STRV_MAKE(suffix),
                                 };
 
                                 _cleanup_(pick_result_done) PickResult result = PICK_RESULT_NULL;
@@ -807,7 +807,7 @@ int image_discover(
                                                 .type_mask = endswith(suffix, ".raw") ? (UINT32_C(1) << DT_REG) | (UINT32_C(1) << DT_BLK) : (UINT32_C(1) << DT_DIR),
                                                 .basename = pretty,
                                                 .architecture = _ARCHITECTURE_INVALID,
-                                                .suffix = suffix,
+                                                .suffix = STRV_MAKE(suffix),
                                         };
 
                                         _cleanup_(pick_result_done) PickResult result = PICK_RESULT_NULL;
index 3c0132fd08653bbf60ad50c7c0463c539677159e..56381bc7d875ef5422aa63853e545fccde565fc6 100644 (file)
@@ -125,7 +125,7 @@ bool mount_point_is_api(const char *path) {
         /* Checks if this mount point is considered "API", and hence
          * should be ignored */
 
-        FOREACH_ARRAY(i, mount_table, ELEMENTSOF(mount_table))
+        FOREACH_ELEMENT(i, mount_table)
                 if (path_equal(path, i->where))
                         return true;
 
@@ -521,7 +521,7 @@ int mount_cgroup_legacy_controllers(bool loaded_policy) {
         if (!cg_is_legacy_force_enabled())
                 return -ERFKILL;
 
-        FOREACH_ARRAY(mp, cgroupv1_mount_table, ELEMENTSOF(cgroupv1_mount_table)) {
+        FOREACH_ELEMENT(mp, cgroupv1_mount_table) {
                 r = mount_one(mp, loaded_policy);
                 if (r < 0)
                         return r;
index 63c443cbfdde791216316737163abe35d2ebbaf9..dbb6537246a46ceff1a0171921c06dddc12587ef 100644 (file)
 #include "string-util.h"
 
 #if HAVE_OPENSSL
+#  include <openssl/rsa.h>
+#  include <openssl/ec.h>
+
+#  if !defined(OPENSSL_NO_ENGINE) && !defined(OPENSSL_NO_DEPRECATED_3_0)
+#    include <openssl/engine.h>
+DISABLE_WARNING_DEPRECATED_DECLARATIONS;
+DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(ENGINE*, ENGINE_free, NULL);
+REENABLE_WARNING;
+#  endif
+
 /* For each error in the OpenSSL thread error queue, log the provided message and the OpenSSL error
  * string. If there are no errors in the OpenSSL thread queue, this logs the message with "No OpenSSL
  * errors." This logs at level debug. Returns -EIO (or -ENOMEM). */
@@ -1344,6 +1354,7 @@ static int load_key_from_engine(const char *engine, const char *private_key_uri,
         assert(private_key_uri);
         assert(ret);
 
+#if !defined(OPENSSL_NO_ENGINE) && !defined(OPENSSL_NO_DEPRECATED_3_0)
         DISABLE_WARNING_DEPRECATED_DECLARATIONS;
         _cleanup_(ENGINE_freep) ENGINE *e = ENGINE_by_id(engine);
         if (!e)
@@ -1364,6 +1375,9 @@ static int load_key_from_engine(const char *engine, const char *private_key_uri,
         *ret = TAKE_PTR(private_key);
 
         return 0;
+#else
+        return -EOPNOTSUPP;
+#endif
 }
 
 int openssl_load_key_from_token(
index e5ecbad86d86cc721c78fff9e3e2fab23faa51d6..1a89fcc2bddc4f86d8947fa443b3ec5a7e278831 100644 (file)
@@ -21,7 +21,6 @@ int parse_openssl_key_source_argument(const char *argument, char **private_key_s
 #  include <openssl/bio.h>
 #  include <openssl/bn.h>
 #  include <openssl/crypto.h>
-#  include <openssl/engine.h>
 #  include <openssl/err.h>
 #  include <openssl/evp.h>
 #  include <openssl/opensslv.h>
@@ -54,9 +53,7 @@ DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(SSL*, SSL_free, NULL);
 DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(BIO*, BIO_free, NULL);
 DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(EVP_MD_CTX*, EVP_MD_CTX_free, NULL);
 DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(ASN1_OCTET_STRING*, ASN1_OCTET_STRING_free, NULL);
-DISABLE_WARNING_DEPRECATED_DECLARATIONS;
-DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(ENGINE*, ENGINE_free, NULL);
-REENABLE_WARNING;
+
 #if OPENSSL_VERSION_MAJOR >= 3
 DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(EVP_CIPHER*, EVP_CIPHER_free, NULL);
 DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(EVP_KDF*, EVP_KDF_free, NULL);
index b8cd83c7c1efb752be93de450b3b947f270cf4ce..aac145836b6c9f62072ef6476b5f71016836398d 100644 (file)
@@ -294,7 +294,7 @@ static int s2h_supported(const SleepConfig *sleep_config, SleepSupport *ret_supp
                 return false;
         }
 
-        FOREACH_ARRAY(i, operations, ELEMENTSOF(operations)) {
+        FOREACH_ELEMENT(i, operations) {
                 r = sleep_supported_internal(sleep_config, *i, /* check_allowed = */ false, &support);
                 if (r < 0)
                         return r;
index aba9d52e9677a950a8ffe02690e61a82ee569c28..cd87417a605d82442c6b1291957f5839adfb5732 100644 (file)
@@ -126,7 +126,7 @@ int switch_root(const char *new_root,
          * 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)) {
+        FOREACH_ELEMENT(transfer, transfer_table) {
                 _cleanup_free_ char *chased = NULL;
                 unsigned long mount_flags;
 
index 41177f821e6708874731e990adbec87cc25c75be..9169513e09cea2c9a3767d87e61d5f4950700b76 100644 (file)
@@ -116,8 +116,7 @@ bool slow_tests_enabled(void) {
 void test_setup_logging(int level) {
         log_set_assert_return_is_critical(true);
         log_set_max_level(level);
-        log_parse_environment();
-        log_open();
+        log_setup();
 }
 
 int write_tmpfile(char *pattern, const char *contents) {
@@ -190,12 +189,16 @@ static int allocate_scope(void) {
         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
         _cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *w = NULL;
         _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
-        _cleanup_free_ char *scope = NULL;
+        _cleanup_free_ char *scope = NULL, *cgroup_root = NULL;
         const char *object;
         int r;
 
         /* Let's try to run this test in a scope of its own, with delegation turned on, so that PID 1 doesn't
          * interfere with our cgroup management. */
+        if (cg_pid_get_path(NULL, 0, &cgroup_root) >= 0 && cg_is_delegated(cgroup_root) && stderr_is_journal()) {
+                log_debug("Already running as a unit with delegated cgroup, not allocating a cgroup subroot.");
+                return 0;
+        }
 
         r = sd_bus_default_system(&bus);
         if (r < 0)
index 784748545c28cb3b81860df2ea2ce1a339819f38..09fdfd6b75c81d205a1977397fe839c466c02075 100644 (file)
@@ -8,6 +8,7 @@
 #include "sd-daemon.h"
 
 #include "argv-util.h"
+#include "errno-util.h"
 #include "macro.h"
 #include "process-util.h"
 #include "rlimit-util.h"
@@ -226,6 +227,36 @@ static inline int run_test_table(void) {
                 }                                                                                               \
         })
 
+#define ASSERT_ERROR(expr1, expr2)                                                                              \
+        ({                                                                                                      \
+                int _expr1 = (expr1);                                                                           \
+                int _expr2 = (expr2);                                                                           \
+                if (_expr1 >= 0) {                                                                              \
+                        log_error("%s:%i: Assertion failed: expected \"%s\" to fail with error \"%s\", but it succeeded", \
+                                  PROJECT_FILE, __LINE__, #expr1, STRERROR(_expr2));                            \
+                        abort();                                                                                \
+                } else if (-_expr1 != _expr2) {                                                                  \
+                        log_error_errno(_expr1, "%s:%i: Assertion failed: expected \"%s\" to fail with error \"%s\", but got the following error: %m", \
+                                        PROJECT_FILE, __LINE__, #expr1, STRERROR(_expr2));                      \
+                        abort();                                                                                \
+                }                                                                                               \
+        })
+
+#define ASSERT_ERROR_ERRNO(expr1, expr2)                                                                        \
+        ({                                                                                                      \
+                int _expr1 = (expr1);                                                                           \
+                int _expr2 = (expr2);                                                                           \
+                if (_expr1 >= 0) {                                                                              \
+                        log_error("%s:%i: Assertion failed: expected \"%s\" to fail with error \"%s\", but it succeeded", \
+                                  PROJECT_FILE, __LINE__, #expr1, STRERROR(_expr2));                            \
+                        abort();                                                                                \
+                } else if (errno != _expr2) {                                                                   \
+                        log_error_errno(errno, "%s:%i: Assertion failed: expected \"%s\" to fail with error \"%s\", but got the following error: %m", \
+                                        PROJECT_FILE, __LINE__, #expr1, STRERROR(errno));                       \
+                        abort();                                                                                \
+                }                                                                                               \
+        })
+
 #define ASSERT_TRUE(expr)                                                                                       \
         ({                                                                                                      \
                 if (!(expr)) {                                                                                  \
index 42975cdb970e46bed36a055c51aedf6dcc181639..10a78adfaff41baa482e8aeeeaf441c44363ec78 100644 (file)
@@ -29,6 +29,7 @@
 #include "recurse-dir.h"
 #include "sha256.h"
 #include "sort-util.h"
+#include "sparse-endian.h"
 #include "stat-util.h"
 #include "string-table.h"
 #include "sync-util.h"
 #include "tpm2-util.h"
 #include "virt.h"
 
+#if HAVE_OPENSSL
+#  include <openssl/hmac.h>
+#endif
+
 #if HAVE_TPM2
 static void *libtss2_esys_dl = NULL;
 static void *libtss2_rc_dl = NULL;
@@ -65,6 +70,7 @@ static DLSYM_FUNCTION(Esys_PolicyAuthorizeNV);
 static DLSYM_FUNCTION(Esys_PolicyGetDigest);
 static DLSYM_FUNCTION(Esys_PolicyOR);
 static DLSYM_FUNCTION(Esys_PolicyPCR);
+static DLSYM_FUNCTION(Esys_PolicySigned);
 static DLSYM_FUNCTION(Esys_ReadPublic);
 static DLSYM_FUNCTION(Esys_StartAuthSession);
 static DLSYM_FUNCTION(Esys_Startup);
@@ -77,6 +83,7 @@ static DLSYM_FUNCTION(Esys_TR_GetTpmHandle);
 static DLSYM_FUNCTION(Esys_TR_Serialize);
 static DLSYM_FUNCTION(Esys_TR_SetAuth);
 static DLSYM_FUNCTION(Esys_TRSess_GetAttributes);
+static DLSYM_FUNCTION(Esys_TRSess_GetNonceTPM);
 static DLSYM_FUNCTION(Esys_TRSess_SetAttributes);
 static DLSYM_FUNCTION(Esys_Unseal);
 static DLSYM_FUNCTION(Esys_VerifySignature);
@@ -132,6 +139,7 @@ int dlopen_tpm2(void) {
                         DLSYM_ARG(Esys_PolicyGetDigest),
                         DLSYM_ARG(Esys_PolicyOR),
                         DLSYM_ARG(Esys_PolicyPCR),
+                        DLSYM_ARG(Esys_PolicySigned),
                         DLSYM_ARG(Esys_ReadPublic),
                         DLSYM_ARG(Esys_StartAuthSession),
                         DLSYM_ARG(Esys_Startup),
@@ -143,6 +151,7 @@ int dlopen_tpm2(void) {
                         DLSYM_ARG(Esys_TR_Serialize),
                         DLSYM_ARG(Esys_TR_SetAuth),
                         DLSYM_ARG(Esys_TRSess_GetAttributes),
+                        DLSYM_ARG(Esys_TRSess_GetNonceTPM),
                         DLSYM_ARG(Esys_TRSess_SetAttributes),
                         DLSYM_ARG(Esys_Unseal),
                         DLSYM_ARG(Esys_VerifySignature));
@@ -666,7 +675,7 @@ int tpm2_context_new(const char *device, Tpm2Context **ret_context) {
 
                 context->tcti_dl = dlopen(fn, RTLD_NOW);
                 if (!context->tcti_dl)
-                        return log_debug_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "Failed to load %s: %s", fn, dlerror());
+                        return log_debug_errno(SYNTHETIC_ERRNO(ENOPKG), "Failed to load %s: %s", fn, dlerror());
 
                 log_debug("Loaded '%s' via dlopen()", fn);
 
@@ -682,7 +691,7 @@ int tpm2_context_new(const char *device, Tpm2Context **ret_context) {
 
                 log_debug("Loaded TCTI module '%s' (%s) [Version %" PRIu32 "]", info->name, info->description, info->version);
 
-                rc = info->init(NULL, &sz, NULL);
+                rc = info->init(/* context= */ NULL, &sz, /* param= */ NULL);
                 if (rc != TPM2_RC_SUCCESS)
                         return log_debug_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
                                                "Failed to initialize TCTI context: %s", sym_Tss2_RC_Decode(rc));
@@ -717,19 +726,37 @@ int tpm2_context_new(const char *device, Tpm2Context **ret_context) {
 
         /* We require AES and CFB support for session encryption. */
         if (!tpm2_supports_alg(context, TPM2_ALG_AES))
-                return log_debug_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "TPM does not support AES.");
+                return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "TPM does not support AES.");
 
         if (!tpm2_supports_alg(context, TPM2_ALG_CFB))
-                return log_debug_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "TPM does not support CFB.");
+                return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "TPM does not support CFB.");
 
         if (!tpm2_supports_tpmt_sym_def(context, &SESSION_TEMPLATE_SYM_AES_128_CFB))
-                return log_debug_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "TPM does not support AES-128-CFB.");
+                return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "TPM does not support AES-128-CFB.");
 
         *ret_context = TAKE_PTR(context);
 
         return 0;
 }
 
+int tpm2_context_new_or_warn(const char *device, Tpm2Context **ret_context) {
+        int r;
+
+        assert(ret_context);
+
+        r = tpm2_context_new(device, ret_context);
+        if (r == -EOPNOTSUPP)
+                return log_error_errno(r, "TPM device not usable as it does not support the required functionality (AES-128-CFB missing?).");
+        if (r == -ENOPKG)
+                return log_error_errno(r, "TPM TCTI driver not available.");
+        if (r == -ENOENT)
+                return log_error_errno(r, "TPM device not found.");
+        if (r < 0)
+                return log_error_errno(r, "Failed to create TPM2 context: %m");
+
+        return 0;
+}
+
 static void tpm2_handle_cleanup(ESYS_CONTEXT *esys_context, ESYS_TR esys_handle, bool flush) {
         TSS2_RC rc;
 
@@ -2238,9 +2265,9 @@ static int tpm2_load_external(
 #if HAVE_TSS2_ESYS3
                         /* tpm2-tss >= 3.0.0 requires a ESYS_TR_RH_* constant specifying the requested
                          * hierarchy, older versions need TPM2_RH_* instead. */
-                        ESYS_TR_RH_OWNER,
+                        private ? ESYS_TR_RH_NULL : ESYS_TR_RH_OWNER,
 #else
-                        TPM2_RH_OWNER,
+                        private ? TPM2_RH_NULL : TPM2_RH_OWNER,
 #endif
                         &handle->esys_handle);
         if (rc != TSS2_RC_SUCCESS)
@@ -3058,7 +3085,7 @@ static void tpm2_trim_auth_value(TPM2B_AUTH *auth) {
                 log_debug("authValue ends in 0, trimming as required by the TPM2 specification Part 1 section 'HMAC Computation' authValue Note 2.");
 }
 
-int tpm2_get_pin_auth(TPMI_ALG_HASH hash, const char *pin, TPM2B_AUTH *ret_auth) {
+int tpm2_auth_value_from_pin(TPMI_ALG_HASH hash, const char *pin, TPM2B_AUTH *ret_auth) {
         TPM2B_AUTH auth = {};
         int r;
 
@@ -3105,7 +3132,7 @@ int tpm2_set_auth(Tpm2Context *c, const Tpm2Handle *handle, const char *pin) {
 
         CLEANUP_ERASE(auth);
 
-        r = tpm2_get_pin_auth(TPM2_ALG_SHA256, pin, &auth);
+        r = tpm2_auth_value_from_pin(TPM2_ALG_SHA256, pin, &auth);
         if (r < 0)
                 return r;
 
@@ -3392,7 +3419,7 @@ int tpm2_calculate_pubkey_name(const TPMT_PUBLIC *public, TPM2B_NAME *ret_name)
  *
  * The handle must reference a key already present in the TPM. It may be either a public key only, or a
  * public/private keypair. */
-static int tpm2_get_name(
+int tpm2_get_name(
                 Tpm2Context *c,
                 const Tpm2Handle *handle,
                 TPM2B_NAME **ret_name) {
@@ -3530,6 +3557,150 @@ int tpm2_policy_auth_value(
         return tpm2_get_policy_digest(c, session, ret_policy_digest);
 }
 
+/* Extend 'digest' with the PolicySigned calculated hash. */
+int tpm2_calculate_policy_signed(TPM2B_DIGEST *digest, const TPM2B_NAME *name) {
+        TPM2_CC command = TPM2_CC_PolicySigned;
+        TSS2_RC rc;
+        int r;
+
+        assert(digest);
+        assert(digest->size == SHA256_DIGEST_SIZE);
+        assert(name);
+
+        r = dlopen_tpm2();
+        if (r < 0)
+                return log_debug_errno(r, "TPM2 support not installed: %m");
+
+        uint8_t buf[sizeof(command)];
+        size_t offset = 0;
+
+        rc = sym_Tss2_MU_TPM2_CC_Marshal(command, buf, sizeof(buf), &offset);
+        if (rc != TSS2_RC_SUCCESS)
+                return log_debug_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
+                                       "Failed to marshal PolicySigned command: %s", sym_Tss2_RC_Decode(rc));
+
+        if (offset != sizeof(command))
+                return log_debug_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
+                                       "Offset 0x%zx wrong after marshalling PolicySigned command", offset);
+
+        struct iovec data[] = {
+                IOVEC_MAKE(buf, offset),
+                IOVEC_MAKE(name->name, name->size),
+        };
+
+        r = tpm2_digest_many(TPM2_ALG_SHA256, digest, data, ELEMENTSOF(data), /* extend= */ true);
+        if (r < 0)
+                return r;
+
+        const TPM2B_NONCE policyRef = {}; /* For now, we do not make use of the policyRef stuff */
+
+        r = tpm2_digest_buffer(TPM2_ALG_SHA256, digest, policyRef.buffer, policyRef.size, /* extend= */ true);
+        if (r < 0)
+                return r;
+
+        tpm2_log_debug_digest(digest, "PolicySigned calculated digest");
+
+        return 0;
+}
+
+int tpm2_policy_signed_hmac_sha256(
+                Tpm2Context *c,
+                const Tpm2Handle *session,
+                const Tpm2Handle *hmac_key_handle,
+                const struct iovec *hmac_key,
+                TPM2B_DIGEST **ret_policy_digest) {
+
+#if HAVE_OPENSSL
+        TSS2_RC rc;
+        int r;
+
+        assert(c);
+        assert(session);
+        assert(hmac_key_handle);
+        assert(iovec_is_set(hmac_key));
+
+        /* This sends a TPM2_PolicySigned command to the tpm. As signature key we use an HMAC-SHA256 key
+         * specified in the hmac_key parameter. The secret key must be loaded into the TPM already and
+         * referenced in hmac_key_handle. */
+
+        log_debug("Submitting PolicySigned policy for HMAC-SHA256.");
+
+        /* Acquire the nonce from the TPM that we shall sign */
+        _cleanup_(Esys_Freep) TPM2B_NONCE *nonce = NULL;
+        rc = sym_Esys_TRSess_GetNonceTPM(
+                        c->esys_context,
+                        session->esys_handle,
+                        &nonce);
+        if (rc != TSS2_RC_SUCCESS)
+                return log_debug_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
+                                       "Failed to determine NoneTPM of auth session: %s",
+                                       sym_Tss2_RC_Decode(rc));
+
+        be32_t expiration = htobe64(0);
+        const TPM2B_DIGEST cpHashA = {};  /* For now, we do not make use of the cpHashA stuff */
+        const TPM2B_NONCE policyRef = {}; /* ditto, we do not bother with policyRef */
+
+        /* Put together the data to sign, as per TPM2 Spec Part 3, 23.3.1 */
+        struct iovec data_to_sign[] = {
+                IOVEC_MAKE(nonce->buffer, nonce->size),
+                IOVEC_MAKE(&expiration, sizeof(expiration)),
+                IOVEC_MAKE(cpHashA.buffer, cpHashA.size),
+                IOVEC_MAKE(policyRef.buffer, policyRef.size),
+        };
+
+        /* Now calculate the digest of the data we put together */
+        TPM2B_DIGEST digest_to_sign;
+        r = tpm2_digest_many(TPM2_ALG_SHA256, &digest_to_sign, data_to_sign, ELEMENTSOF(data_to_sign), /* extend= */ false);
+        if (r < 0)
+                return r;
+
+        unsigned char hmac_signature[SHA256_DIGEST_SIZE];
+        unsigned hmac_signature_size = sizeof(hmac_signature);
+
+        /* And sign this with our key */
+        if (!HMAC(EVP_sha256(),
+                  hmac_key->iov_base,
+                  hmac_key->iov_len,
+                  digest_to_sign.buffer,
+                  digest_to_sign.size,
+                  hmac_signature,
+                  &hmac_signature_size))
+                return -ENOTRECOVERABLE;
+
+        /* Now bring the signature into a format that the TPM understands */
+        TPMT_SIGNATURE sig = {
+                .sigAlg = TPM2_ALG_HMAC,
+                .signature.hmac.hashAlg = TPM2_ALG_SHA256,
+        };
+        assert(hmac_signature_size == sizeof(sig.signature.hmac.digest.sha256));
+        memcpy(sig.signature.hmac.digest.sha256, hmac_signature, hmac_signature_size);
+
+        /* And submit the whole shebang to the TPM */
+        rc = sym_Esys_PolicySigned(
+                        c->esys_context,
+                        hmac_key_handle->esys_handle,
+                        session->esys_handle,
+                        /* shandle1= */ ESYS_TR_NONE,
+                        /* shandle2= */ ESYS_TR_NONE,
+                        /* shandle3= */ ESYS_TR_NONE,
+                        nonce,
+                        &cpHashA,
+                        &policyRef,
+                        expiration,
+                        &sig,
+                        /* timeout= */ NULL,
+                        /* policyTicket= */ NULL);
+        if (rc != TSS2_RC_SUCCESS)
+                return log_debug_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
+                                       "Failed to add PolicySigned policy to TPM: %s",
+                                       sym_Tss2_RC_Decode(rc));
+
+        return tpm2_get_policy_digest(c, session, ret_policy_digest);
+#else /* HAVE_OPENSSL */
+        return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "OpenSSL support is disabled.");
+#endif
+}
+
 int tpm2_calculate_policy_authorize_nv(
                 const TPM2B_NV_PUBLIC *public_info,
                 TPM2B_DIGEST *digest) {
@@ -4135,7 +4306,7 @@ static const struct {
 static int tpm2_ecc_curve_from_openssl_curve_id(int openssl_ecc_curve_id, TPM2_ECC_CURVE *ret) {
         assert(ret);
 
-        FOREACH_ARRAY(t, tpm2_openssl_ecc_curve_table, ELEMENTSOF(tpm2_openssl_ecc_curve_table))
+        FOREACH_ELEMENT(t, tpm2_openssl_ecc_curve_table)
                 if (t->openssl_ecc_curve_id == openssl_ecc_curve_id) {
                         *ret = t->tpm2_ecc_curve_id;
                         return 0;
@@ -4148,7 +4319,7 @@ static int tpm2_ecc_curve_from_openssl_curve_id(int openssl_ecc_curve_id, TPM2_E
 static int tpm2_ecc_curve_to_openssl_curve_id(TPM2_ECC_CURVE tpm2_ecc_curve_id, int *ret) {
         assert(ret);
 
-        FOREACH_ARRAY(t, tpm2_openssl_ecc_curve_table, ELEMENTSOF(tpm2_openssl_ecc_curve_table))
+        FOREACH_ELEMENT(t, tpm2_openssl_ecc_curve_table)
                 if (t->tpm2_ecc_curve_id == tpm2_ecc_curve_id) {
                         *ret = t->openssl_ecc_curve_id;
                         return 0;
@@ -4785,7 +4956,7 @@ static int tpm2_calculate_seal_private(
 
         TPM2B_AUTH auth = {};
         if (pin) {
-                r = tpm2_get_pin_auth(parent->publicArea.nameAlg, pin, &auth);
+                r = tpm2_auth_value_from_pin(parent->publicArea.nameAlg, pin, &auth);
                 if (r < 0)
                         return r;
         }
@@ -5250,7 +5421,7 @@ int tpm2_seal(Tpm2Context *c,
         CLEANUP_ERASE(hmac_sensitive);
 
         if (pin) {
-                r = tpm2_get_pin_auth(TPM2_ALG_SHA256, pin, &hmac_sensitive.userAuth);
+                r = tpm2_auth_value_from_pin(TPM2_ALG_SHA256, pin, &hmac_sensitive.userAuth);
                 if (r < 0)
                         return r;
         }
@@ -5622,8 +5793,6 @@ int tpm2_define_policy_nv_index(
                 const Tpm2Handle *session,
                 TPM2_HANDLE requested_nv_index,
                 const TPM2B_DIGEST *write_policy,
-                const char *pin,
-                const TPM2B_AUTH *auth,
                 TPM2_HANDLE *ret_nv_index,
                 Tpm2Handle **ret_nv_handle,
                 TPM2B_NV_PUBLIC *ret_nv_public) {
@@ -5633,7 +5802,10 @@ int tpm2_define_policy_nv_index(
         int r;
 
         assert(c);
-        assert(pin || auth);
+
+        /* Allocates an nvindex to store a policy for use in PolicyAuthorizeNV in. This is where pcrlock then
+         * stores its predicted PCR policies in. If 'requested_nv_index' will try to allocate the specified
+         * nvindex, otherwise will find a free one, and use that. */
 
         r = tpm2_handle_new(c, &new_handle);
         if (r < 0)
@@ -5641,17 +5813,6 @@ int tpm2_define_policy_nv_index(
 
         new_handle->flush = false; /* This is a persistent NV index, don't flush hence */
 
-        TPM2B_AUTH _auth = {};
-        CLEANUP_ERASE(_auth);
-
-        if (!auth) {
-                r = tpm2_get_pin_auth(TPM2_ALG_SHA256, pin, &_auth);
-                if (r < 0)
-                        return r;
-
-                auth = &_auth;
-        }
-
         for (unsigned try = 0; try < 25U; try++) {
                 TPM2_HANDLE nv_index;
 
@@ -5679,7 +5840,7 @@ int tpm2_define_policy_nv_index(
                                 /* shandle1= */ session ? session->esys_handle : ESYS_TR_PASSWORD,
                                 /* shandle2= */ ESYS_TR_NONE,
                                 /* shandle3= */ ESYS_TR_NONE,
-                                auth,
+                                /* auth= */ NULL,
                                 &public_info,
                                 &new_handle->esys_handle);
 
@@ -7031,6 +7192,75 @@ int tpm2_load_public_key_file(const char *path, TPM2B_PUBLIC *ret) {
         *ret = device_key_public;
         return 0;
 }
+
+int tpm2_hmac_key_from_pin(Tpm2Context *c, const Tpm2Handle *session, const TPM2B_AUTH *pin, Tpm2Handle **ret) {
+        int r;
+
+        assert(c);
+        assert(pin);
+        assert(ret);
+
+        log_debug("Converting PIN into TPM2 HMAC-SHA256 object.");
+
+        /* Load the PIN (which we have stored in the "auth" TPM2B_AUTH) into the TPM as an HMAC key so that
+         * we can use it in a TPM2_PolicySigned() to write to the nvindex. For that we'll prep a pair of
+         * TPM2B_PUBLIC and TPM2B_SENSITIVE that defines an HMAC-SHA256 keyed hash function, and initialize
+         * it based on on the provided PIN data. */
+
+        TPM2B_PUBLIC auth_hmac_public = {
+                .publicArea = {
+                        .type = TPM2_ALG_KEYEDHASH,
+                        .nameAlg = TPM2_ALG_SHA256,
+                        .objectAttributes = TPMA_OBJECT_SIGN_ENCRYPT,
+                        .parameters.keyedHashDetail.scheme = {
+                                .scheme = TPM2_ALG_HMAC,
+                                .details.hmac.hashAlg = TPM2_ALG_SHA256,
+                        },
+                        .unique.keyedHash.size = SHA256_DIGEST_SIZE,
+                },
+        };
+
+        TPM2B_SENSITIVE auth_hmac_private = {
+                .sensitiveArea = {
+                        .sensitiveType = TPM2_ALG_KEYEDHASH,
+                        .sensitive.bits.size = pin->size,
+                        .seedValue.size = SHA256_DIGEST_SIZE,
+                },
+        };
+
+        /* Copy in the key data */
+        memcpy_safe(auth_hmac_private.sensitiveArea.sensitive.bits.buffer, pin->buffer, pin->size);
+
+        /* NB: We initialize the seed of the TPMT_SENSITIVE structure to all zeroes, since we want a stable
+         * "name" of the PIN object */
+
+        /* Now calculate the "unique" field for the public area, based on the sensitive data, according to
+         * the algorithm in the TPM2 spec, part 1, Section 27.5.3.2 */
+        struct iovec sensitive_data[] = {
+                IOVEC_MAKE(auth_hmac_private.sensitiveArea.seedValue.buffer, auth_hmac_private.sensitiveArea.seedValue.size),
+                IOVEC_MAKE(auth_hmac_private.sensitiveArea.sensitive.bits.buffer, auth_hmac_private.sensitiveArea.sensitive.bits.size),
+        };
+        r = tpm2_digest_many(
+                        auth_hmac_public.publicArea.nameAlg,
+                        &auth_hmac_public.publicArea.unique.keyedHash,
+                        sensitive_data,
+                        ELEMENTSOF(sensitive_data),
+                        /* extend= */ false);
+        if (r < 0)
+                return r;
+
+        /* And now load the public/private parts into the TPM and get a handle back */
+        r = tpm2_load_external(
+                        c,
+                        session,
+                        &auth_hmac_public,
+                        &auth_hmac_private,
+                        ret);
+        if (r < 0)
+                return log_error_errno(r, "Failed to load PIN into TPM2: %m");
+
+        return 0;
+}
 #endif
 
 char *tpm2_pcr_mask_to_string(uint32_t mask) {
index f9f29e310d9c0f0e85d04c02433a7b3e90060a18..dc8aa88f09bb2aeabdc29b7cece7479cdcb27c08 100644 (file)
@@ -72,6 +72,7 @@ typedef struct {
 } Tpm2Context;
 
 int tpm2_context_new(const char *device, Tpm2Context **ret_context);
+int tpm2_context_new_or_warn(const char *device, Tpm2Context **ret_context);
 Tpm2Context *tpm2_context_ref(Tpm2Context *context);
 Tpm2Context *tpm2_context_unref(Tpm2Context *context);
 DEFINE_TRIVIAL_CLEANUP_FUNC(Tpm2Context*, tpm2_context_unref);
@@ -131,6 +132,7 @@ int tpm2_marshal_nv_public(const TPM2B_NV_PUBLIC *nv_public, void **ret, size_t
 int tpm2_unmarshal_nv_public(const void *data, size_t size, TPM2B_NV_PUBLIC *ret_nv_public);
 int tpm2_marshal_blob(const TPM2B_PUBLIC *public, const TPM2B_PRIVATE *private, const TPM2B_ENCRYPTED_SECRET *seed, void **ret_blob, size_t *ret_blob_size);
 int tpm2_unmarshal_blob(const void *blob, size_t blob_size, TPM2B_PUBLIC *ret_public, TPM2B_PRIVATE *ret_private, TPM2B_ENCRYPTED_SECRET *ret_seed);
+int tpm2_get_name(Tpm2Context *c, const Tpm2Handle *handle, TPM2B_NAME **ret_name);
 
 bool tpm2_supports_alg(Tpm2Context *c, TPM2_ALG_ID alg);
 bool tpm2_supports_command(Tpm2Context *c, TPM2_CC command);
@@ -256,7 +258,7 @@ int tpm2_index_from_handle(Tpm2Context *c, const Tpm2Handle *handle, TPM2_HANDLE
 int tpm2_pcr_read(Tpm2Context *c, const TPML_PCR_SELECTION *pcr_selection, Tpm2PCRValue **ret_pcr_values, size_t *ret_n_pcr_values);
 int tpm2_pcr_read_missing_values(Tpm2Context *c, Tpm2PCRValue *pcr_values, size_t n_pcr_values);
 
-int tpm2_get_pin_auth(TPMI_ALG_HASH hash, const char *pin, TPM2B_AUTH *ret_auth);
+int tpm2_auth_value_from_pin(TPMI_ALG_HASH hash, const char *pin, TPM2B_AUTH *ret_auth);
 int tpm2_set_auth(Tpm2Context *c, const Tpm2Handle *handle, const char *pin);
 int tpm2_set_auth_binary(Tpm2Context *c, const Tpm2Handle *handle, const TPM2B_AUTH *auth);
 
@@ -267,6 +269,7 @@ int tpm2_policy_authorize_nv(Tpm2Context *c, const Tpm2Handle *session, const Tp
 int tpm2_policy_pcr(Tpm2Context *c, const Tpm2Handle *session, const TPML_PCR_SELECTION *pcr_selection, TPM2B_DIGEST **ret_policy_digest);
 int tpm2_policy_or(Tpm2Context *c, const Tpm2Handle *session, const TPM2B_DIGEST *branches, size_t n_branches, TPM2B_DIGEST **ret_policy_digest);
 int tpm2_policy_super_pcr(Tpm2Context *c, const Tpm2Handle *session, const Tpm2PCRPrediction *prediction, uint16_t algorithm);
+int tpm2_policy_signed_hmac_sha256(Tpm2Context *c, const Tpm2Handle *session, const Tpm2Handle *hmac_key_handle, const struct iovec *hmac_key, TPM2B_DIGEST **ret_policy_digest);
 
 int tpm2_calculate_pubkey_name(const TPMT_PUBLIC *public, TPM2B_NAME *ret_name);
 int tpm2_calculate_nv_index_name(const TPMS_NV_PUBLIC *nvpublic, TPM2B_NAME *ret_name);
@@ -277,6 +280,7 @@ int tpm2_calculate_policy_authorize_nv(const TPM2B_NV_PUBLIC *public, TPM2B_DIGE
 int tpm2_calculate_policy_pcr(const Tpm2PCRValue *pcr_values, size_t n_pcr_values, TPM2B_DIGEST *digest);
 int tpm2_calculate_policy_or(const TPM2B_DIGEST *branches, size_t n_branches, TPM2B_DIGEST *digest);
 int tpm2_calculate_policy_super_pcr(Tpm2PCRPrediction *prediction, uint16_t algorithm, TPM2B_DIGEST *pcr_policy);
+int tpm2_calculate_policy_signed(TPM2B_DIGEST *digest, const TPM2B_NAME *name);
 int tpm2_calculate_serialize(TPM2_HANDLE handle, const TPM2B_NAME *name, const TPM2B_PUBLIC *public, void **ret_serialized, size_t *ret_serialized_size);
 int tpm2_calculate_sealing_policy(const Tpm2PCRValue *pcr_values, size_t n_pcr_values, const TPM2B_PUBLIC *public, bool use_pin, const Tpm2PCRLockPolicy *policy, TPM2B_DIGEST *digest);
 int tpm2_calculate_seal(TPM2_HANDLE parent_handle, const TPM2B_PUBLIC *parent_public, const TPMA_OBJECT *attributes, const struct iovec *secret, const TPM2B_DIGEST *policy, const char *pin, struct iovec *ret_secret, struct iovec *ret_blob, struct iovec *ret_serialized_parent);
@@ -298,7 +302,7 @@ int tpm2_tpm2b_public_from_openssl_pkey(const EVP_PKEY *pkey, TPM2B_PUBLIC *ret)
 int tpm2_tpm2b_public_from_pem(const void *pem, size_t pem_size, TPM2B_PUBLIC *ret);
 int tpm2_tpm2b_public_to_fingerprint(const TPM2B_PUBLIC *public, void **ret_fingerprint, size_t *ret_fingerprint_size);
 
-int tpm2_define_policy_nv_index(Tpm2Context *c, const Tpm2Handle *session, TPM2_HANDLE requested_nv_index, const TPM2B_DIGEST *write_policy, const char *pin, const TPM2B_AUTH *auth, TPM2_HANDLE *ret_nv_index, Tpm2Handle **ret_nv_handle, TPM2B_NV_PUBLIC *ret_nv_public);
+int tpm2_define_policy_nv_index(Tpm2Context *c, const Tpm2Handle *session, TPM2_HANDLE requested_nv_index, const TPM2B_DIGEST *write_policy, TPM2_HANDLE *ret_nv_index, Tpm2Handle **ret_nv_handle, TPM2B_NV_PUBLIC *ret_nv_public);
 int tpm2_write_policy_nv_index(Tpm2Context *c, const Tpm2Handle *policy_session, TPM2_HANDLE nv_index, const Tpm2Handle *nv_handle, const TPM2B_DIGEST *policy_digest);
 int tpm2_undefine_policy_nv_index(Tpm2Context *c, const Tpm2Handle *session, TPM2_HANDLE nv_index, const Tpm2Handle *nv_handle);
 
@@ -310,6 +314,8 @@ int tpm2_deserialize(Tpm2Context *c, const void *serialized, size_t serialized_s
 
 int tpm2_load_public_key_file(const char *path, TPM2B_PUBLIC *ret);
 
+int tpm2_hmac_key_from_pin(Tpm2Context *c, const Tpm2Handle *session, const TPM2B_AUTH *pin, Tpm2Handle **ret);
+
 /* The tpm2-tss library has many structs that are simply a combination of an array (or object) and
  * size. These macros allow easily initializing or assigning instances of such structs from an existing
  * buffer/object and size, while also checking the size for safety with the struct buffer/object size. If the
index 713aff289597929523a85a810f17c4653eed2950..f1c00a21b61a2f92b4daa254d837e81d3e03505b 100644 (file)
@@ -4124,7 +4124,7 @@ int varlink_error_to_errno(const char *error, JsonVariant *parameters) {
         if (!error)
                 return 0;
 
-        FOREACH_ARRAY(t, table, ELEMENTSOF(table))
+        FOREACH_ELEMENT(t, table)
                 if (streq(error, t->error))
                         return t->value;
 
index 1870fb97ef7ed6aad4ea5306680d423f6ac48fb9..4720e7448d8acfedd4492a8a022c1ecbe801ecfb 100644 (file)
@@ -34,6 +34,8 @@ static int format_fname(
 
         if (FLAGS_SET(flags, PICK_TRIES) || !filter->version) /* Underspecified? */
                 return -ENOEXEC;
+        if (strv_length(filter->suffix) > 1) /* suffix is not deterministic? */
+                return -ENOEXEC;
 
         /* The format for names we match goes like this:
          *
@@ -85,8 +87,9 @@ static int format_fname(
                         return -ENOMEM;
         }
 
-        if (filter->suffix && !strextend(&fn, filter->suffix))
-                return -ENOMEM;
+        if (!strv_isempty(filter->suffix))
+                if (!strextend(&fn, filter->suffix[0]))
+                        return -ENOMEM;
 
         if (!filename_is_valid(fn))
                 return -EINVAL;
@@ -180,11 +183,9 @@ static int pin_choice(
         if (!result.path)
                 return log_oom_debug();
 
-        if (filter->version) {
-                result.version = strdup(filter->version);
-                if (!result.version)
-                        return log_oom_debug();
-        }
+        r = strdup_to(&result.version, filter->version);
+        if (r < 0)
+                return r;
 
         *ret = TAKE_PICK_RESULT(result);
         return 1;
@@ -289,13 +290,12 @@ static int make_choice(
                         return log_oom_debug();
 
                 r = chaseat(toplevel_fd, p, CHASE_AT_RESOLVE_IN_ROOT, &object_path, &object_fd);
-                if (r < 0) {
-                        if (r != -ENOENT)
-                                return log_debug_errno(r, "Failed to open '%s': %m", prefix_roota(toplevel_path, p));
-
+                if (r == -ENOENT) {
                         *ret = PICK_RESULT_NULL;
                         return 0;
                 }
+                if (r < 0)
+                        return log_debug_errno(r, "Failed to open '%s': %m", prefix_roota(toplevel_path, p));
 
                 return pin_choice(
                                 toplevel_path,
@@ -352,8 +352,8 @@ static int make_choice(
                 } else
                         e = dname;
 
-                if (!isempty(filter->suffix)) {
-                        char *sfx = endswith(e, filter->suffix);
+                if (!strv_isempty(filter->suffix)) {
+                        char *sfx = endswith_strv(e, filter->suffix);
                         if (!sfx)
                                 continue;
 
@@ -493,7 +493,8 @@ int path_pick(
                 PickResult *ret) {
 
         _cleanup_free_ char *filter_bname = NULL, *dir = NULL, *parent = NULL, *fname = NULL;
-        const char *filter_suffix, *enumeration_path;
+        char * const *filter_suffix_strv = NULL;
+        const char *filter_suffix = NULL, *enumeration_path;
         uint32_t filter_type_mask;
         int r;
 
@@ -549,14 +550,12 @@ int path_pick(
                 if (!filter_bname)
                         return -ENOMEM;
 
-                if (filter->suffix) {
-                        /* Chop off suffix, if specified */
-                        char *f = endswith(filter_bname, filter->suffix);
-                        if (f)
-                                *f = 0;
-                }
+                /* Chop off suffix, if specified */
+                char *f = endswith_strv(filter_bname, filter->suffix);
+                if (f)
+                        *f = 0;
 
-                filter_suffix = filter->suffix;
+                filter_suffix_strv = filter->suffix;
                 filter_type_mask = filter->type_mask;
 
                 enumeration_path = path;
@@ -616,7 +615,7 @@ int path_pick(
                                 .basename = filter_bname,
                                 .version = filter->version,
                                 .architecture = filter->architecture,
-                                .suffix = filter_suffix,
+                                .suffix = filter_suffix_strv ?: STRV_MAKE(filter_suffix),
                         },
                         flags,
                         ret);
@@ -685,10 +684,16 @@ int path_pick_update_warn(
 const PickFilter pick_filter_image_raw = {
         .type_mask = (UINT32_C(1) << DT_REG) | (UINT32_C(1) << DT_BLK),
         .architecture = _ARCHITECTURE_INVALID,
-        .suffix = ".raw",
+        .suffix = STRV_MAKE(".raw"),
 };
 
 const PickFilter pick_filter_image_dir = {
         .type_mask = UINT32_C(1) << DT_DIR,
         .architecture = _ARCHITECTURE_INVALID,
 };
+
+const PickFilter pick_filter_image_any = {
+        .type_mask = (UINT32_C(1) << DT_REG) | (UINT32_C(1) << DT_BLK) | (UINT32_C(1) << DT_DIR),
+        .architecture = _ARCHITECTURE_INVALID,
+        .suffix = STRV_MAKE(".raw", ""),
+};
index 21ce6684041efcaa02e58b744fde5432bf47801f..38251c84e8576706012b1d829332bba12809eb1a 100644 (file)
@@ -16,7 +16,7 @@ typedef struct PickFilter {
         const char *basename;         /* Can be overridden by search pattern */
         const char *version;
         Architecture architecture;
-        const char *suffix;           /* Can be overridden by search pattern */
+        char * const *suffix;         /* Can be overridden by search pattern */
 } PickFilter;
 
 typedef struct PickResult {
@@ -58,3 +58,4 @@ int path_pick_update_warn(
 
 extern const PickFilter pick_filter_image_raw;
 extern const PickFilter pick_filter_image_dir;
+extern const PickFilter pick_filter_image_any;
index 79b016f9c3a517766be9db1fd96156aeabe08d8c..7c9707054676cb4380bb267945896d191572d1f8 100644 (file)
@@ -44,6 +44,7 @@
 #include "process-util.h"
 #include "rm-rf.h"
 #include "sort-util.h"
+#include "string-table.h"
 #include "string-util.h"
 #include "terminal-util.h"
 #include "user-util.h"
@@ -52,8 +53,8 @@
 #include "verbs.h"
 
 typedef enum MutableMode {
-        MUTABLE_YES,
         MUTABLE_NO,
+        MUTABLE_YES,
         MUTABLE_AUTO,
         MUTABLE_IMPORT,
         MUTABLE_EPHEMERAL,
@@ -62,6 +63,17 @@ typedef enum MutableMode {
         _MUTABLE_INVALID = -EINVAL,
 } MutableMode;
 
+static const char* const mutable_mode_table[_MUTABLE_MAX] = {
+        [MUTABLE_NO]               = "no",
+        [MUTABLE_YES]              = "yes",
+        [MUTABLE_AUTO]             = "auto",
+        [MUTABLE_IMPORT]           = "import",
+        [MUTABLE_EPHEMERAL]        = "ephemeral",
+        [MUTABLE_EPHEMERAL_IMPORT] = "ephemeral-import",
+};
+
+DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING_WITH_BOOLEAN(mutable_mode, MutableMode, MUTABLE_YES);
+
 static char **arg_hierarchies = NULL; /* "/usr" + "/opt" by default for sysext and /etc by default for confext */
 static char *arg_root = NULL;
 static JsonFormatFlags arg_json_format_flags = JSON_FORMAT_OFF;
@@ -77,7 +89,7 @@ static MutableMode arg_mutable = MUTABLE_NO;
 /* Is set to IMAGE_CONFEXT when systemd is called with the confext functionality instead of the default */
 static ImageClass arg_image_class = IMAGE_SYSEXT;
 
-static const char *mutable_extensions_base_dir = "/var/lib/extensions.mutable";
+#define MUTABLE_EXTENSIONS_BASE_DIR "/var/lib/extensions.mutable"
 
 STATIC_DESTRUCTOR_REGISTER(arg_hierarchies, strv_freep);
 STATIC_DESTRUCTOR_REGISTER(arg_root, freep);
@@ -127,27 +139,7 @@ static const struct {
 };
 
 static int parse_mutable_mode(const char *p) {
-        int r;
-
-        assert(p);
-
-        if (streq(p, "auto"))
-                return MUTABLE_AUTO;
-
-        if (streq(p, "import"))
-                return MUTABLE_IMPORT;
-
-        if (streq(p, "ephemeral"))
-                return MUTABLE_EPHEMERAL;
-
-        if (streq(p, "ephemeral-import"))
-                return MUTABLE_EPHEMERAL_IMPORT;
-
-        r = parse_boolean(p);
-        if (r < 0)
-                return r;
-
-        return r ? MUTABLE_YES : MUTABLE_NO;
+        return mutable_mode_from_string(p);
 }
 
 static int is_our_mount_point(
@@ -198,7 +190,7 @@ static int is_our_mount_point(
                 return log_error_errno(r, "Failed to parse device major/minor stored in '%s/dev' file on '%s': %m", image_class_info[image_class].dot_directory_name, p);
 
         if (lstat(p, &st) < 0)
-                return log_error_errno(r, "Failed to stat %s: %m", p);
+                return log_error_errno(errno, "Failed to stat %s: %m", p);
 
         if (st.st_dev != dev) {
                 log_debug("Hierarchy '%s' reports a different device major/minor than what we are seeing, assuming offline copy.", p);
@@ -538,7 +530,7 @@ static int verb_status(int argc, char **argv, void *userdata) {
                         return log_oom();
 
                 if (stat(*p, &st) < 0)
-                        return log_error_errno(r, "Failed to stat() '%s': %m", *p);
+                        return log_error_errno(errno, "Failed to stat() '%s': %m", *p);
 
                 r = table_add_many(
                                 t,
@@ -673,10 +665,10 @@ static int paths_on_same_fs(const char *path1, const char *path2) {
         assert(path1);
         assert(path2);
 
-        if (stat(path1, &st1))
+        if (stat(path1, &st1) < 0)
                 return log_error_errno(errno, "Failed to stat '%s': %m", path1);
 
-        if (stat(path2, &st2))
+        if (stat(path2, &st2) < 0)
                 return log_error_errno(errno, "Failed to stat '%s': %m", path2);
 
         return st1.st_dev == st2.st_dev;
@@ -725,6 +717,7 @@ static int work_dir_for_hierarchy(
 
 typedef struct OverlayFSPaths {
         char *hierarchy;
+        mode_t hierarchy_mode;
         char *resolved_hierarchy;
         char *resolved_mutable_directory;
 
@@ -767,13 +760,42 @@ static int resolve_hierarchy(const char *hierarchy, char **ret_resolved_hierarch
         return 0;
 }
 
+static int mutable_directory_mode_matches_hierarchy(
+                const char *root_or_null,
+                const char *path,
+                mode_t hierarchy_mode) {
+
+        _cleanup_free_ char *path_in_root = NULL;
+        struct stat st;
+        mode_t actual_mode;
+
+        assert(path);
+
+        path_in_root = path_join(root_or_null, path);
+        if (!path_in_root)
+                return log_oom();
+
+        if (stat(path_in_root, &st) < 0) {
+                if (errno == ENOENT)
+                        return 0;
+                return log_error_errno(errno, "Failed to stat mutable directory '%s': %m", path_in_root);
+        }
+
+        actual_mode = st.st_mode & 0777;
+        if (actual_mode != hierarchy_mode)
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Mutable directory '%s' has mode %04o, ought to have mode %04o", path_in_root, actual_mode, hierarchy_mode);
+
+        return 0;
+}
+
 static int resolve_mutable_directory(
                 const char *hierarchy,
+                mode_t hierarchy_mode,
                 const char *workspace,
                 char **ret_resolved_mutable_directory) {
 
         _cleanup_free_ char *path = NULL, *resolved_path = NULL, *dir_name = NULL;
-        const char *root = arg_root, *base = mutable_extensions_base_dir;
+        const char *root = arg_root, *base = MUTABLE_EXTENSIONS_BASE_DIR;
         int r;
 
         assert(hierarchy);
@@ -800,6 +822,15 @@ static int resolve_mutable_directory(
         if (!path)
                 return log_oom();
 
+        if (IN_SET(arg_mutable, MUTABLE_YES, MUTABLE_AUTO)) {
+                /* If there already is a mutable directory, check if its mode matches hierarchy. Merged
+                 * hierarchy will have the same mode as the mutable directory, so we want no surprising mode
+                 * changes here. */
+                r = mutable_directory_mode_matches_hierarchy(root, path, hierarchy_mode);
+                if (r < 0)
+                        return r;
+        }
+
         if (IN_SET(arg_mutable, MUTABLE_YES, MUTABLE_EPHEMERAL, MUTABLE_EPHEMERAL_IMPORT)) {
                 _cleanup_free_ char *path_in_root = NULL;
 
@@ -822,6 +853,8 @@ static int resolve_mutable_directory(
 
 static int overlayfs_paths_new(const char *hierarchy, const char *workspace_path, OverlayFSPaths **ret_op) {
         _cleanup_free_ char *hierarchy_copy = NULL, *resolved_hierarchy = NULL, *resolved_mutable_directory = NULL;
+        mode_t hierarchy_mode;
+
         int r;
 
         assert (hierarchy);
@@ -834,7 +867,17 @@ static int overlayfs_paths_new(const char *hierarchy, const char *workspace_path
         r = resolve_hierarchy(hierarchy, &resolved_hierarchy);
         if (r < 0)
                 return r;
-        r = resolve_mutable_directory(hierarchy, workspace_path, &resolved_mutable_directory);
+
+        if (resolved_hierarchy) {
+                struct stat st;
+
+                if (stat(resolved_hierarchy, &st) < 0)
+                        return log_error_errno(errno, "Failed to stat '%s': %m", resolved_hierarchy);
+                hierarchy_mode = st.st_mode & 0777;
+        } else
+                hierarchy_mode = 0755;
+
+        r = resolve_mutable_directory(hierarchy, hierarchy_mode, workspace_path, &resolved_mutable_directory);
         if (r < 0)
                 return r;
 
@@ -845,6 +888,7 @@ static int overlayfs_paths_new(const char *hierarchy, const char *workspace_path
 
         *op = (OverlayFSPaths) {
                 .hierarchy = TAKE_PTR(hierarchy_copy),
+                .hierarchy_mode = hierarchy_mode,
                 .resolved_hierarchy = TAKE_PTR(resolved_hierarchy),
                 .resolved_mutable_directory = TAKE_PTR(resolved_mutable_directory),
         };
@@ -853,16 +897,43 @@ static int overlayfs_paths_new(const char *hierarchy, const char *workspace_path
         return 0;
 }
 
-static int resolved_paths_equal(const char *resolved_a, const char *resolved_b) {
-        /* Returns true if paths are of the same entry, false if not, <0 on error. */
+static int determine_used_extensions(const char *hierarchy, char **paths, char ***ret_used_paths, size_t *ret_extensions_used) {
+        _cleanup_strv_free_ char **used_paths = NULL;
+        size_t n = 0;
+        int r;
+
+        assert(hierarchy);
+        assert(paths);
+        assert(ret_used_paths);
+        assert(ret_extensions_used);
 
-        if (path_equal(resolved_a, resolved_b))
-                return 1;
+        STRV_FOREACH(p, paths) {
+                _cleanup_free_ char *resolved = NULL;
 
-        if (!resolved_a || !resolved_b)
-                return 0;
+                r = chase(hierarchy, *p, CHASE_PREFIX_ROOT, &resolved, NULL);
+                if (r == -ENOENT) {
+                        log_debug_errno(r, "Hierarchy '%s' in extension '%s' doesn't exist, not merging.", hierarchy, *p);
+                        continue;
+                }
+                if (r < 0)
+                        return log_error_errno(r, "Failed to resolve hierarchy '%s' in extension '%s': %m", hierarchy, *p);
+
+                r = dir_is_empty(resolved, /* ignore_hidden_or_backup= */ false);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to check if hierarchy '%s' in extension '%s' is empty: %m", resolved, *p);
+                if (r > 0) {
+                        log_debug("Hierarchy '%s' in extension '%s' is empty, not merging.", hierarchy, *p);
+                        continue;
+                }
+
+                r = strv_consume_with_size (&used_paths, &n, TAKE_PTR(resolved));
+                if (r < 0)
+                        return log_oom();
+        }
 
-        return inode_same(resolved_a, resolved_b, 0);
+        *ret_used_paths = TAKE_PTR(used_paths);
+        *ret_extensions_used = n;
+        return 0;
 }
 
 static int maybe_import_mutable_directory(OverlayFSPaths *op) {
@@ -876,7 +947,7 @@ static int maybe_import_mutable_directory(OverlayFSPaths *op) {
         if (arg_mutable != MUTABLE_IMPORT || !op->resolved_mutable_directory)
                 return 0;
 
-        r = resolved_paths_equal(op->resolved_hierarchy, op->resolved_mutable_directory);
+        r = path_equal_or_inode_same_full(op->resolved_hierarchy, op->resolved_mutable_directory, 0);
         if (r < 0)
                 return log_error_errno(r, "Failed to check equality of hierarchy %s and its mutable directory %s: %m", op->resolved_hierarchy, op->resolved_mutable_directory);
         if (r > 0)
@@ -904,15 +975,19 @@ static int maybe_import_ignored_mutable_directory(OverlayFSPaths *op) {
         if (!dir_name)
                 return log_oom();
 
-        path = path_join(mutable_extensions_base_dir, dir_name);
+        path = path_join(MUTABLE_EXTENSIONS_BASE_DIR, dir_name);
         if (!path)
                 return log_oom();
 
         r = chase(path, arg_root, CHASE_PREFIX_ROOT, &resolved_path, NULL);
-        if (r < 0 && r != -ENOENT)
+        if (r == -ENOENT) {
+                log_debug("Mutable directory for %s does not exist, not importing", op->hierarchy);
+                return 0;
+        }
+        if (r < 0)
                 return log_error_errno(r, "Failed to resolve mutable directory '%s': %m", path);
 
-        r = resolved_paths_equal(op->resolved_hierarchy, resolved_path);
+        r = path_equal_or_inode_same_full(op->resolved_hierarchy, resolved_path, 0);
         if (r < 0)
                 return log_error_errno(r, "Failed to check equality of hierarchy %s and its mutable directory %s: %m", op->resolved_hierarchy, op->resolved_mutable_directory);
 
@@ -948,41 +1023,17 @@ static int determine_top_lower_dirs(OverlayFSPaths *op, const char *meta_path) {
         return 0;
 }
 
-static int determine_middle_lower_dirs(OverlayFSPaths *op, char **paths, size_t *ret_extensions_used) {
-        size_t n = 0;
+static int determine_middle_lower_dirs(OverlayFSPaths *op, char **paths) {
         int r;
 
         assert(op);
         assert(paths);
-        assert(ret_extensions_used);
-
-        /* Put the extensions in the middle */
-        STRV_FOREACH(p, paths) {
-                _cleanup_free_ char *resolved = NULL;
-
-                r = chase(op->hierarchy, *p, CHASE_PREFIX_ROOT, &resolved, NULL);
-                if (r == -ENOENT) {
-                        log_debug_errno(r, "Hierarchy '%s' in extension '%s' doesn't exist, not merging.", op->hierarchy, *p);
-                        continue;
-                }
-                if (r < 0)
-                        return log_error_errno(r, "Failed to resolve hierarchy '%s' in extension '%s': %m", op->hierarchy, *p);
 
-                r = dir_is_empty(resolved, /* ignore_hidden_or_backup= */ false);
-                if (r < 0)
-                        return log_error_errno(r, "Failed to check if hierarchy '%s' in extension '%s' is empty: %m", resolved, *p);
-                if (r > 0) {
-                        log_debug("Hierarchy '%s' in extension '%s' is empty, not merging.", op->hierarchy, *p);
-                        continue;
-                }
-
-                r = strv_consume(&op->lower_dirs, TAKE_PTR(resolved));
-                if (r < 0)
-                        return log_oom();
-                ++n;
-        }
+        /* The paths were already determined in determine_used_extensions, so we just take them as is. */
+        r = strv_extend_strv(&op->lower_dirs, paths, false);
+        if (r < 0)
+                return log_oom ();
 
-        *ret_extensions_used = n;
         return 0;
 }
 
@@ -1021,7 +1072,7 @@ static int hierarchy_as_lower_dir(OverlayFSPaths *op) {
                 return 0;
         }
 
-        r = resolved_paths_equal(op->resolved_hierarchy, op->resolved_mutable_directory);
+        r = path_equal_or_inode_same_full(op->resolved_hierarchy, op->resolved_mutable_directory, 0);
         if (r < 0)
                 return log_error_errno(r, "Failed to check equality of hierarchy %s and its mutable directory %s: %m", op->resolved_hierarchy, op->resolved_mutable_directory);
         if (r > 0) {
@@ -1052,21 +1103,19 @@ static int determine_bottom_lower_dirs(OverlayFSPaths *op) {
 static int determine_lower_dirs(
                 OverlayFSPaths *op,
                 char **paths,
-                const char *meta_path,
-                size_t *ret_extensions_used) {
+                const char *meta_path) {
 
         int r;
 
         assert(op);
         assert(paths);
         assert(meta_path);
-        assert(ret_extensions_used);
 
         r = determine_top_lower_dirs(op, meta_path);
         if (r < 0)
                 return r;
 
-        r = determine_middle_lower_dirs(op, paths, ret_extensions_used);
+        r = determine_middle_lower_dirs(op, paths);
         if (r < 0)
                 return r;
 
@@ -1137,6 +1186,7 @@ static int mount_overlayfs_with_op(
                 const char *meta_path) {
 
         int r;
+        const char *top_layer = NULL;
 
         assert(op);
         assert(overlay_path);
@@ -1153,8 +1203,19 @@ static int mount_overlayfs_with_op(
                 r = mkdir_p(op->work_dir, 0700);
                 if (r < 0)
                         return log_error_errno(r, "Failed to make directory '%s': %m", op->work_dir);
+                top_layer = op->upper_dir;
+        } else {
+                assert(!strv_isempty(op->lower_dirs));
+                top_layer = op->lower_dirs[0];
         }
 
+        /* Overlayfs merged directory has the same mode as the top layer (either first lowerdir in options in
+         * read-only case, or upperdir for mutable case. Set up top overlayfs layer to the same mode as the
+         * unmerged hierarchy, otherwise we might end up with merged hierarchy owned by root and with mode
+         * being 0700. */
+        if (chmod(top_layer, op->hierarchy_mode) < 0)
+                return log_error_errno(errno, "Failed to set permissions of '%s' to %04o: %m", top_layer, op->hierarchy_mode);
+
         r = mount_overlayfs(image_class, noexec, overlay_path, op->lower_dirs, op->upper_dir, op->work_dir);
         if (r < 0)
                 return r;
@@ -1327,6 +1388,7 @@ static int merge_hierarchy(
                 const char *workspace_path) {
 
         _cleanup_(overlayfs_paths_freep) OverlayFSPaths *op = NULL;
+        _cleanup_strv_free_ char **used_paths = NULL;
         size_t extensions_used = 0;
         int r;
 
@@ -1337,16 +1399,20 @@ static int merge_hierarchy(
         assert(overlay_path);
         assert(workspace_path);
 
-        r = overlayfs_paths_new(hierarchy, workspace_path, &op);
+        r = determine_used_extensions(hierarchy, paths, &used_paths, &extensions_used);
         if (r < 0)
                 return r;
 
-        r = determine_lower_dirs(op, paths, meta_path, &extensions_used);
+        if (extensions_used == 0) /* No extension with files in this hierarchy? Then don't do anything. */
+                return 0;
+
+        r = overlayfs_paths_new(hierarchy, workspace_path, &op);
         if (r < 0)
                 return r;
 
-        if (extensions_used == 0) /* No extension with files in this hierarchy? Then don't do anything. */
-                return 0;
+        r = determine_lower_dirs(op, used_paths, meta_path);
+        if (r < 0)
+                return r;
 
         r = determine_upper_dir(op);
         if (r < 0)
@@ -1740,7 +1806,7 @@ static int merge(ImageClass image_class,
         if (r == 123) /* exit code 123 means: didn't do anything */
                 return 0;
         if (r > 0)
-                return log_error_errno(SYNTHETIC_ERRNO(ENXIO), "Failed to merge hierarchies");
+                return log_error_errno(SYNTHETIC_ERRNO(EPROTO), "Failed to merge hierarchies");
 
         r = need_reload(image_class, hierarchies, no_reload);
         if (r < 0)
@@ -2272,7 +2338,7 @@ static int sysext_main(int argc, char *argv[]) {
 }
 
 static int run(int argc, char *argv[]) {
-        const charenv_var;
+        const char *env_var;
         int r;
 
         log_setup();
index 7926bfbecafc6933bb70b01ac2befbc917e4f593..aee839de4cae1d4f4fba64c767f3e09da62fc2e7 100644 (file)
@@ -196,6 +196,8 @@ typedef struct UnitStatusInfo {
 
         uint64_t runtime_max_sec;
 
+        sd_id128_t invocation_id;
+
         bool need_daemon_reload;
         bool transient;
 
@@ -468,6 +470,9 @@ static void print_status_info(
         } else
                 printf("\n");
 
+        if (!sd_id128_is_null(i->invocation_id))
+                printf(" Invocation: " SD_ID128_FORMAT_STR "\n", SD_ID128_FORMAT_VAL(i->invocation_id));
+
         STRV_FOREACH(t, i->triggered_by) {
                 UnitActiveState state = _UNIT_ACTIVE_STATE_INVALID;
 
@@ -2031,8 +2036,9 @@ static int show_one(
                 { "InactiveExitTimestampMonotonic", "t",               NULL,           offsetof(UnitStatusInfo, inactive_exit_timestamp_monotonic) },
                 { "ActiveEnterTimestamp",           "t",               NULL,           offsetof(UnitStatusInfo, active_enter_timestamp)            },
                 { "ActiveExitTimestamp",            "t",               NULL,           offsetof(UnitStatusInfo, active_exit_timestamp)             },
-                { "RuntimeMaxUSec",                 "t",               NULL,           offsetof(UnitStatusInfo, runtime_max_sec)                   },
                 { "InactiveEnterTimestamp",         "t",               NULL,           offsetof(UnitStatusInfo, inactive_enter_timestamp)          },
+                { "RuntimeMaxUSec",                 "t",               NULL,           offsetof(UnitStatusInfo, runtime_max_sec)                   },
+                { "InvocationID",                   "s",               bus_map_id128,  offsetof(UnitStatusInfo, invocation_id)                     },
                 { "NeedDaemonReload",               "b",               NULL,           offsetof(UnitStatusInfo, need_daemon_reload)                },
                 { "Transient",                      "b",               NULL,           offsetof(UnitStatusInfo, transient)                         },
                 { "ExecMainPID",                    "u",               NULL,           offsetof(UnitStatusInfo, main_pid)                          },
index de8873caaf56204af1d561d6c8d80f57c71544d2..8068d77d1cd5b5791684dcfd05068530e45b4ee5 100644 (file)
@@ -35,20 +35,24 @@ static const struct {
         { "force-reload",          "ReloadOrTryRestartUnit", "reload-or-try-restart" }, /* legacy alias */
 };
 
-static const char *verb_to_method(const char *verb) {
-       for (size_t i = 0; i < ELEMENTSOF(unit_actions); i++)
-                if (streq_ptr(unit_actions[i].verb, verb))
-                        return unit_actions[i].method;
+static const char* verb_to_method(const char *verb) {
+        assert(verb);
 
-       return "StartUnit";
+        FOREACH_ELEMENT(i, unit_actions)
+                if (streq(i->verb, verb))
+                        return i->method;
+
+        return "StartUnit";
 }
 
-static const char *verb_to_job_type(const char *verb) {
-       for (size_t i = 0; i < ELEMENTSOF(unit_actions); i++)
-                if (streq_ptr(unit_actions[i].verb, verb))
-                        return unit_actions[i].job_type;
+static const char* verb_to_job_type(const char *verb) {
+        assert(verb);
+
+        FOREACH_ELEMENT(i, unit_actions)
+                if (streq(i->verb, verb))
+                        return i->job_type;
 
-       return "start";
+        return "start";
 }
 
 static int start_unit_one(
@@ -240,6 +244,8 @@ const struct action_metadata action_table[_ACTION_MAX] = {
 };
 
 enum action verb_to_action(const char *verb) {
+        assert(verb);
+
         for (enum action i = 0; i < _ACTION_MAX; i++)
                 if (streq_ptr(action_table[i].verb, verb))
                         return i;
@@ -375,10 +381,6 @@ int verb_start(int argc, char *argv[], void *userdata) {
         }
 
         if (arg_wait) {
-                r = bus_call_method_async(bus, NULL, bus_systemd_mgr, "Subscribe", NULL, NULL, NULL);
-                if (r < 0)
-                        return log_error_errno(r, "Failed to enable subscription: %m");
-
                 r = bus_wait_for_units_new(bus, &wu);
                 if (r < 0)
                         return log_error_errno(r, "Failed to allocate unit watch context: %m");
@@ -402,7 +404,7 @@ int verb_start(int argc, char *argv[], void *userdata) {
                 }
 
         if (!arg_no_block) {
-                const charextra_args[4];
+                const char *extra_args[4];
                 WaitJobsFlags flags = 0;
 
                 SET_FLAG(flags, BUS_WAIT_JOBS_LOG_ERROR, !arg_quiet);
index 75c6c547f7ba51a80f8e0363ccd9673d6fe97ba7..2482b7ccb2460b93164d1b55c87de5a56ca7ccdb 100644 (file)
@@ -64,8 +64,8 @@ int acquire_bus(BusFocus focus, sd_bus **ret) {
 }
 
 void release_busses(void) {
-        for (BusFocus w = 0; w < _BUS_FOCUS_MAX; w++)
-                buses[w] = sd_bus_flush_close_unref(buses[w]);
+        FOREACH_ARRAY(w, buses, _BUS_FOCUS_MAX)
+                *w = sd_bus_flush_close_unref(*w);
 }
 
 void ask_password_agent_open_maybe(void) {
index 49d69759674c7780be7b3f62b092935bcecb2afa..a876add00c12c681711a270bafd71c0df1a059a7 100644 (file)
@@ -161,6 +161,7 @@ int sd_event_source_send_child_signal(sd_event_source *s, int sig, const siginfo
 int sd_event_source_send_child_signal(sd_event_source *s, int sig, const void *si, unsigned flags);
 #endif
 int sd_event_source_get_inotify_mask(sd_event_source *s, uint32_t *ret);
+int sd_event_source_get_inotify_path(sd_event_source *s, const char **ret);
 int sd_event_source_set_memory_pressure_type(sd_event_source *e, const char *ty);
 int sd_event_source_set_memory_pressure_period(sd_event_source *s, uint64_t threshold_usec, uint64_t window_usec);
 int sd_event_source_set_destroy_callback(sd_event_source *s, sd_event_destroy_t callback);
index a984a9d85e15ca37b989b83a2a0a46184d8b5c15..a9210526b6a4bf37b13f7d730c7709a1e5c5a11e 100644 (file)
@@ -53,6 +53,7 @@ int sd_id128_get_invocation(sd_id128_t *ret);
 int sd_id128_get_app_specific(sd_id128_t base, sd_id128_t app_id, sd_id128_t *ret);
 int sd_id128_get_machine_app_specific(sd_id128_t app_id, sd_id128_t *ret);
 int sd_id128_get_boot_app_specific(sd_id128_t app_id, sd_id128_t *ret);
+int sd_id128_get_invocation_app_specific(sd_id128_t app_id, sd_id128_t *ret);
 
 #define SD_ID128_ARRAY(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15) \
         { .bytes = { 0x##v0, 0x##v1, 0x##v2, 0x##v3, 0x##v4, 0x##v5, 0x##v6, 0x##v7, \
index 79cbb8975161eb9a09ed61a89bf696fe844097e5..f25622fceb5d844fc66a7bdfe15700a61d770727 100644 (file)
@@ -48,18 +48,20 @@ sd_event *sd_radv_get_event(sd_radv *ra);
 int sd_radv_start(sd_radv *ra);
 int sd_radv_stop(sd_radv *ra);
 int sd_radv_is_running(sd_radv *ra);
+int sd_radv_send(sd_radv *ra);
 
 int sd_radv_set_ifindex(sd_radv *ra, int interface_index);
 int sd_radv_set_ifname(sd_radv *ra, const char *interface_name);
 int sd_radv_get_ifname(sd_radv *ra, const char **ret);
+int sd_radv_set_link_local_address(sd_radv *ra, const struct in6_addr *addr);
 int sd_radv_set_mac(sd_radv *ra, const struct ether_addr *mac_addr);
 int sd_radv_set_mtu(sd_radv *ra, uint32_t mtu);
 int sd_radv_set_hop_limit(sd_radv *ra, uint8_t hop_limit);
 int sd_radv_set_retransmit(sd_radv *ra, uint64_t usec);
 int sd_radv_set_router_lifetime(sd_radv *ra, uint64_t usec);
-int sd_radv_set_managed_information(sd_radv *ra, int managed);
-int sd_radv_set_other_information(sd_radv *ra, int other);
-int sd_radv_set_preference(sd_radv *ra, unsigned preference);
+int sd_radv_set_managed_information(sd_radv *ra, int b);
+int sd_radv_set_other_information(sd_radv *ra, int b);
+int sd_radv_set_preference(sd_radv *ra, uint8_t preference);
 int sd_radv_add_prefix(sd_radv *ra, sd_radv_prefix *p);
 int sd_radv_add_route_prefix(sd_radv *ra, sd_radv_route_prefix *p);
 int sd_radv_add_pref64_prefix(sd_radv *ra, sd_radv_pref64_prefix *p);
index e18130b42352a30e919e83862cce2ed64b82f34d..cc677740d8f984017a5da1eb653f9f3734f12094 100644 (file)
@@ -50,6 +50,10 @@ int main(int argc, char *argv[]) {
         if (r == -ENOMEDIUM)
                 return log_tests_skipped("cgroupfs not available");
 
+        r = find_executable("ping", NULL);
+        if (r < 0)
+                return log_tests_skipped_errno(r, "Can't find ping binary: %m");
+
         _cleanup_free_ char *unit_dir = NULL;
         ASSERT_OK(get_testdata_dir("units", &unit_dir));
         ASSERT_OK(set_unit_path(unit_dir));
index c9962501db2cb335a65b397e2d53c5e0852693e5..f42ca4edb9ceaa2b04586620157b7c3019c23572 100644 (file)
@@ -90,12 +90,12 @@ int main(int argc, char *argv[]) {
         ASSERT_OK(manager_startup(m, NULL, NULL, NULL));
 
         /* We need to enable access to the filesystem where the binary is so we
-         * add @common-block */
-        ASSERT_LT(test_restrict_filesystems(m, "restrict_filesystems_test.service", "/sys/kernel/tracing/printk_formats", STRV_MAKE("@common-block")), 0);
-        ASSERT_OK(test_restrict_filesystems(m, "restrict_filesystems_test.service", "/sys/kernel/tracing/printk_formats", STRV_MAKE("tracefs", "@common-block")));
-        ASSERT_LT(test_restrict_filesystems(m, "restrict_filesystems_test.service", "/sys/kernel/tracing/printk_formats", STRV_MAKE("tracefs", "@common-block", "~tracefs")), 0);
-        ASSERT_LT(test_restrict_filesystems(m, "restrict_filesystems_test.service", "/sys/kernel/debug/sleep_time", STRV_MAKE("@common-block")), 0);
-        ASSERT_OK(test_restrict_filesystems(m, "restrict_filesystems_test.service", "/sys/kernel/debug/sleep_time", STRV_MAKE("debugfs", "@common-block")));
+         * add @common-block and @application */
+        ASSERT_LT(test_restrict_filesystems(m, "restrict_filesystems_test.service", "/sys/kernel/tracing/printk_formats", STRV_MAKE("@common-block", "@application")), 0);
+        ASSERT_OK(test_restrict_filesystems(m, "restrict_filesystems_test.service", "/sys/kernel/tracing/printk_formats", STRV_MAKE("tracefs", "@common-block", "@application")));
+        ASSERT_LT(test_restrict_filesystems(m, "restrict_filesystems_test.service", "/sys/kernel/tracing/printk_formats", STRV_MAKE("tracefs", "@common-block", "@application", "~tracefs")), 0);
+        ASSERT_LT(test_restrict_filesystems(m, "restrict_filesystems_test.service", "/sys/kernel/debug/sleep_time", STRV_MAKE("@common-block", "@application")), 0);
+        ASSERT_OK(test_restrict_filesystems(m, "restrict_filesystems_test.service", "/sys/kernel/debug/sleep_time", STRV_MAKE("debugfs", "@common-block", "@application")));
         ASSERT_LT(test_restrict_filesystems(m, "restrict_filesystems_test.service", "/sys/kernel/debug/sleep_time", STRV_MAKE("~debugfs")), 0);
 
         return 0;
index 84619c91de92e9ed60656e661456212d1f0356c1..cc9cc7377821bfe2d95bdf5cf21db96cc69fddae 100644 (file)
@@ -243,7 +243,7 @@ TEST(mime_type_matches) {
         };
 
         /* Generates the right <match/> expressions for these credentials according to the shared mime-info spec */
-        FOREACH_ARRAY(t, tags, ELEMENTSOF(tags)) {
+        FOREACH_ELEMENT(t, tags) {
                 _cleanup_free_ char *encoded = NULL;
 
                 assert_se(base64mem(t, sizeof(sd_id128_t), &encoded) >= 0);
index e9b922b42ef34d5b8bb7d665a4b7701aac0aab76..23eefcdf4b06cbd6aaf31671dac7a2ae897ab659 100644 (file)
@@ -59,8 +59,8 @@ static void wait_for_service_finish(Manager *m, Unit *unit) {
         usec_t ts;
         usec_t timeout = 2 * USEC_PER_MINUTE;
 
-        assert_se(m);
-        assert_se(unit);
+        ASSERT_NOT_NULL(m);
+        ASSERT_NOT_NULL(unit);
 
         /* Bump the timeout when running in plain QEMU, as some more involved tests might start hitting the
          * default 2m timeout (like exec-dynamicuser-statedir.service) */
@@ -76,7 +76,7 @@ static void wait_for_service_finish(Manager *m, Unit *unit) {
                 usec_t n;
 
                 r = sd_event_run(m->event, 100 * USEC_PER_MSEC);
-                assert_se(r >= 0);
+                ASSERT_OK(r);
 
                 n = now(CLOCK_MONOTONIC);
                 if (ts + timeout < n) {
@@ -93,8 +93,8 @@ static void check_main_result(const char *file, unsigned line, const char *func,
                               Manager *m, Unit *unit, int status_expected, int code_expected) {
         Service *service = NULL;
 
-        assert_se(m);
-        assert_se(unit);
+        ASSERT_NOT_NULL(m);
+        ASSERT_NOT_NULL(unit);
 
         wait_for_service_finish(m, unit);
 
@@ -120,8 +120,8 @@ static void check_service_result(const char *file, unsigned line, const char *fu
                                  Manager *m, Unit *unit, ServiceResult result_expected) {
         Service *service = NULL;
 
-        assert_se(m);
-        assert_se(unit);
+        ASSERT_NOT_NULL(m);
+        ASSERT_NOT_NULL(unit);
 
         wait_for_service_finish(m, unit);
 
@@ -185,7 +185,7 @@ static bool check_user_has_group_with_same_name(const char *name) {
         struct passwd *p;
         struct group *g;
 
-        assert_se(name);
+        ASSERT_NOT_NULL(name);
 
         p = getpwnam(name);
         if (!p ||
@@ -221,7 +221,8 @@ static void start_parent_slices(Unit *unit) {
         if (slice) {
                 start_parent_slices(slice);
                 int r = unit_start(slice, NULL);
-                assert_se(r >= 0 || r == -EALREADY);
+                if (r != -EALREADY)
+                        ASSERT_OK(r);
         }
 }
 
@@ -254,7 +255,7 @@ static bool have_userns_privileges(void) {
                       FORK_CLOSE_ALL_FDS |
                       FORK_DEATHSIG_SIGKILL,
                       &pid);
-        assert(r >= 0);
+        ASSERT_OK(r);
         if (r == 0) {
                 /* Keep CAP_SYS_ADMIN if we have it to ensure we give an
                  * accurate result to the caller. Some kernels have a
@@ -290,13 +291,13 @@ static void _test(const char *file, unsigned line, const char *func,
                   Manager *m, const char *unit_name, int status_expected, int code_expected) {
         Unit *unit;
 
-        assert_se(unit_name);
+        ASSERT_NOT_NULL(unit_name);
 
-        assert_se(manager_load_startable_unit_or_warn(m, unit_name, NULL, &unit) >= 0);
+        ASSERT_OK(manager_load_startable_unit_or_warn(m, unit_name, NULL, &unit));
         /* We need to start the slices as well otherwise the slice cgroups might be pruned
          * in on_cgroup_empty_event. */
         start_parent_slices(unit);
-        assert_se(unit_start(unit, NULL) >= 0);
+        ASSERT_OK(unit_start(unit, NULL));
         check_main_result(file, line, func, m, unit, status_expected, code_expected);
 
         ++n_ran_tests;
@@ -308,18 +309,18 @@ static void _test_service(const char *file, unsigned line, const char *func,
                           Manager *m, const char *unit_name, ServiceResult result_expected) {
         Unit *unit;
 
-        assert_se(unit_name);
+        ASSERT_NOT_NULL(unit_name);
 
-        assert_se(manager_load_startable_unit_or_warn(m, unit_name, NULL, &unit) >= 0);
-        assert_se(unit_start(unit, NULL) >= 0);
+        ASSERT_OK(manager_load_startable_unit_or_warn(m, unit_name, NULL, &unit));
+        ASSERT_OK(unit_start(unit, NULL));
         check_service_result(file, line, func, m, unit, result_expected);
 }
 #define test_service(m, unit_name, result_expected) \
         _test_service(PROJECT_FILE, __LINE__, __func__, m, unit_name, result_expected)
 
 static void test_exec_bindpaths(Manager *m) {
-        assert_se(mkdir_p("/tmp/test-exec-bindpaths", 0755) >= 0);
-        assert_se(mkdir_p("/tmp/test-exec-bindreadonlypaths", 0755) >= 0);
+        ASSERT_OK(mkdir_p("/tmp/test-exec-bindpaths", 0755));
+        ASSERT_OK(mkdir_p("/tmp/test-exec-bindreadonlypaths", 0755));
 
         test(m, "exec-bindpaths.service", can_unshare ? 0 : EXIT_NAMESPACE, CLD_EXITED);
 
@@ -330,8 +331,8 @@ static void test_exec_bindpaths(Manager *m) {
 static void test_exec_cpuaffinity(Manager *m) {
         _cleanup_(cpu_set_reset) CPUSet c = {};
 
-        assert_se(cpu_set_realloc(&c, 8192) >= 0); /* just allocate the maximum possible size */
-        assert_se(sched_getaffinity(0, c.allocated, c.set) >= 0);
+        ASSERT_OK(cpu_set_realloc(&c, 8192)); /* just allocate the maximum possible size */
+        ASSERT_OK_ERRNO(sched_getaffinity(0, c.allocated, c.set));
 
         if (!CPU_ISSET_S(0, c.allocated, c.set)) {
                 log_notice("Cannot use CPU 0, skipping %s", __func__);
@@ -357,7 +358,7 @@ static void test_exec_credentials(Manager *m) {
 }
 
 static void test_exec_workingdirectory(Manager *m) {
-        assert_se(mkdir_p("/tmp/test-exec_workingdirectory", 0755) >= 0);
+        ASSERT_OK(mkdir_p("/tmp/test-exec_workingdirectory", 0755));
 
         test(m, "exec-workingdirectory.service", 0, CLD_EXITED);
         test(m, "exec-workingdirectory-trailing-dot.service", 0, CLD_EXITED);
@@ -366,13 +367,13 @@ static void test_exec_workingdirectory(Manager *m) {
 }
 
 static void test_exec_execsearchpath(Manager *m) {
-        assert_se(mkdir_p("/tmp/test-exec_execsearchpath", 0755) >= 0);
+        ASSERT_OK(mkdir_p("/tmp/test-exec_execsearchpath", 0755));
 
-        assert_se(copy_file("/bin/ls", "/tmp/test-exec_execsearchpath/ls_temp", 0,  0777, COPY_REPLACE) >= 0);
+        ASSERT_OK(copy_file("/bin/ls", "/tmp/test-exec_execsearchpath/ls_temp", 0,  0777, COPY_REPLACE));
 
         test(m, "exec-execsearchpath.service", 0, CLD_EXITED);
 
-        assert_se(rm_rf("/tmp/test-exec_execsearchpath", REMOVE_ROOT|REMOVE_PHYSICAL) >= 0);
+        ASSERT_OK(rm_rf("/tmp/test-exec_execsearchpath", REMOVE_ROOT|REMOVE_PHYSICAL));
 
         test(m, "exec-execsearchpath.service", EXIT_EXEC, CLD_EXITED);
 }
@@ -415,8 +416,7 @@ static void test_exec_execsearchpath_environment_files(Manager *m) {
         int r;
 
         r = write_string_file("/tmp/test-exec_execsearchpath_environmentfile.conf", path_not_set, WRITE_STRING_FILE_CREATE);
-
-        assert_se(r == 0);
+        ASSERT_OK(r);
 
         test(m, "exec-execsearchpath-environmentfile.service", 0, CLD_EXITED);
 
@@ -424,8 +424,7 @@ static void test_exec_execsearchpath_environment_files(Manager *m) {
 
 
         r = write_string_file("/tmp/test-exec_execsearchpath_environmentfile-set.conf", path_set, WRITE_STRING_FILE_CREATE);
-
-        assert_se(r == 0);
+        ASSERT_OK(r);
 
         test(m, "exec-execsearchpath-environmentfile-set.service", 0, CLD_EXITED);
 
@@ -433,23 +432,23 @@ static void test_exec_execsearchpath_environment_files(Manager *m) {
 }
 
 static void test_exec_execsearchpath_passenvironment(Manager *m) {
-        assert_se(setenv("VAR1", "word1 word2", 1) == 0);
-        assert_se(setenv("VAR2", "word3", 1) == 0);
-        assert_se(setenv("VAR3", "$word 5 6", 1) == 0);
-        assert_se(setenv("VAR4", "new\nline", 1) == 0);
-        assert_se(setenv("VAR5", "passwordwithbackslashes", 1) == 0);
+        ASSERT_OK_ERRNO(setenv("VAR1", "word1 word2", 1));
+        ASSERT_OK_ERRNO(setenv("VAR2", "word3", 1));
+        ASSERT_OK_ERRNO(setenv("VAR3", "$word 5 6", 1));
+        ASSERT_OK_ERRNO(setenv("VAR4", "new\nline", 1));
+        ASSERT_OK_ERRNO(setenv("VAR5", "passwordwithbackslashes", 1));
 
         test(m, "exec-execsearchpath-passenvironment.service", 0, CLD_EXITED);
 
-        assert_se(setenv("PATH", "/usr", 1) == 0);
+        ASSERT_OK_ERRNO(setenv("PATH", "/usr", 1));
         test(m, "exec-execsearchpath-passenvironment-set.service", 0, CLD_EXITED);
 
-        assert_se(unsetenv("VAR1") == 0);
-        assert_se(unsetenv("VAR2") == 0);
-        assert_se(unsetenv("VAR3") == 0);
-        assert_se(unsetenv("VAR4") == 0);
-        assert_se(unsetenv("VAR5") == 0);
-        assert_se(unsetenv("PATH") == 0);
+        ASSERT_OK_ERRNO(unsetenv("VAR1"));
+        ASSERT_OK_ERRNO(unsetenv("VAR2"));
+        ASSERT_OK_ERRNO(unsetenv("VAR3"));
+        ASSERT_OK_ERRNO(unsetenv("VAR4"));
+        ASSERT_OK_ERRNO(unsetenv("VAR5"));
+        ASSERT_OK_ERRNO(unsetenv("PATH"));
 }
 
 static void test_exec_personality(Manager *m) {
@@ -487,7 +486,7 @@ static void test_exec_ignoresigpipe(Manager *m) {
 }
 
 static void test_exec_privatetmp(Manager *m) {
-        assert_se(touch("/tmp/test-exec_privatetmp") >= 0);
+        ASSERT_OK(touch("/tmp/test-exec_privatetmp"));
 
         if (MANAGER_IS_SYSTEM(m) || have_userns_privileges()) {
                 test(m, "exec-privatetmp-yes.service", can_unshare ? 0 : MANAGER_IS_SYSTEM(m) ? EXIT_FAILURE : EXIT_NAMESPACE, CLD_EXITED);
@@ -621,8 +620,8 @@ static int on_spawn_io(sd_event_source *s, int fd, uint32_t revents, void *userd
         char buf[4096];
         ssize_t l;
 
-        assert_se(s);
-        assert_se(fd >= 0);
+        ASSERT_NOT_NULL(s);
+        ASSERT_GT(fd, 0);
 
         l = read(fd, buf, sizeof(buf) - 1);
         if (l < 0) {
@@ -636,20 +635,20 @@ static int on_spawn_io(sd_event_source *s, int fd, uint32_t revents, void *userd
 
         buf[l] = '\0';
         if (result)
-                assert_se(strextend(result, buf));
+                ASSERT_NOT_NULL(strextend(result, buf));
         else
                 log_error("ldd: %s", buf);
 
 reenable:
         /* Re-enable the event source if we did not encounter EOF */
-        assert_se(sd_event_source_set_enabled(s, SD_EVENT_ONESHOT) >= 0);
+        ASSERT_OK(sd_event_source_set_enabled(s, SD_EVENT_ONESHOT));
         return 0;
 }
 
 static int on_spawn_timeout(sd_event_source *s, uint64_t usec, void *userdata) {
         pid_t *pid = userdata;
 
-        assert_se(pid);
+        ASSERT_NOT_NULL(pid);
 
         (void) kill(*pid, SIGKILL);
 
@@ -659,7 +658,7 @@ static int on_spawn_timeout(sd_event_source *s, uint64_t usec, void *userdata) {
 static int on_spawn_sigchld(sd_event_source *s, const siginfo_t *si, void *userdata) {
         int ret = -EIO;
 
-        assert_se(si);
+        ASSERT_NOT_NULL(si);
 
         if (si->si_code == CLD_EXITED)
                 ret = si->si_status;
@@ -679,19 +678,19 @@ static int find_libraries(const char *exec, char ***ret) {
         pid_t pid;
         int r;
 
-        assert_se(exec);
-        assert_se(ret);
+        ASSERT_NOT_NULL(exec);
+        ASSERT_NOT_NULL(ret);
 
-        assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGCHLD) >= 0);
+        ASSERT_OK(sigprocmask_many(SIG_BLOCK, NULL, SIGCHLD));
 
-        assert_se(pipe2(outpipe, O_NONBLOCK|O_CLOEXEC) == 0);
-        assert_se(pipe2(errpipe, O_NONBLOCK|O_CLOEXEC) == 0);
+        ASSERT_OK_ERRNO(pipe2(outpipe, O_NONBLOCK|O_CLOEXEC));
+        ASSERT_OK_ERRNO(pipe2(errpipe, O_NONBLOCK|O_CLOEXEC));
 
         r = safe_fork_full("(spawn-ldd)",
                            (int[]) { -EBADF, outpipe[1], errpipe[1] },
                            NULL, 0,
                            FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG_SIGTERM|FORK_REARRANGE_STDIO|FORK_LOG, &pid);
-        assert_se(r >= 0);
+        ASSERT_OK(r);
         if (r == 0) {
                 execlp("ldd", "ldd", exec, NULL);
                 _exit(EXIT_FAILURE);
@@ -700,40 +699,40 @@ static int find_libraries(const char *exec, char ***ret) {
         outpipe[1] = safe_close(outpipe[1]);
         errpipe[1] = safe_close(errpipe[1]);
 
-        assert_se(sd_event_new(&e) >= 0);
+        ASSERT_OK(sd_event_new(&e));
 
-        assert_se(sd_event_add_time_relative(e, NULL, CLOCK_MONOTONIC,
-                                             10 * USEC_PER_SEC, USEC_PER_SEC, on_spawn_timeout, &pid) >= 0);
-        assert_se(sd_event_add_io(e, &stdout_source, outpipe[0], EPOLLIN, on_spawn_io, &result) >= 0);
-        assert_se(sd_event_source_set_enabled(stdout_source, SD_EVENT_ONESHOT) >= 0);
-        assert_se(sd_event_add_io(e, &stderr_source, errpipe[0], EPOLLIN, on_spawn_io, NULL) >= 0);
-        assert_se(sd_event_source_set_enabled(stderr_source, SD_EVENT_ONESHOT) >= 0);
-        assert_se(sd_event_add_child(e, &sigchld_source, pid, WEXITED, on_spawn_sigchld, NULL) >= 0);
+        ASSERT_OK(sd_event_add_time_relative(e, NULL, CLOCK_MONOTONIC,
+                                             10 * USEC_PER_SEC, USEC_PER_SEC, on_spawn_timeout, &pid));
+        ASSERT_OK(sd_event_add_io(e, &stdout_source, outpipe[0], EPOLLIN, on_spawn_io, &result));
+        ASSERT_OK(sd_event_source_set_enabled(stdout_source, SD_EVENT_ONESHOT));
+        ASSERT_OK(sd_event_add_io(e, &stderr_source, errpipe[0], EPOLLIN, on_spawn_io, NULL));
+        ASSERT_OK(sd_event_source_set_enabled(stderr_source, SD_EVENT_ONESHOT));
+        ASSERT_OK(sd_event_add_child(e, &sigchld_source, pid, WEXITED, on_spawn_sigchld, NULL));
         /* SIGCHLD should be processed after IO is complete */
-        assert_se(sd_event_source_set_priority(sigchld_source, SD_EVENT_PRIORITY_NORMAL + 1) >= 0);
+        ASSERT_OK(sd_event_source_set_priority(sigchld_source, SD_EVENT_PRIORITY_NORMAL + 1));
 
-        assert_se(sd_event_loop(e) >= 0);
+        ASSERT_OK(sd_event_loop(e));
 
         _cleanup_strv_free_ char **v = NULL;
-        assert_se(strv_split_newlines_full(&v, result, 0) >= 0);
+        ASSERT_OK(strv_split_newlines_full(&v, result, 0));
 
         STRV_FOREACH(q, v) {
                 _cleanup_free_ char *word = NULL;
                 const char *p = *q;
 
                 r = extract_first_word(&p, &word, NULL, 0);
-                assert_se(r >= 0);
+                ASSERT_OK(r);
                 if (r == 0)
                         continue;
 
                 if (path_is_absolute(word)) {
-                        assert_se(strv_consume(&libraries, TAKE_PTR(word)) >= 0);
+                        ASSERT_OK(strv_consume(&libraries, TAKE_PTR(word)));
                         continue;
                 }
 
                 word = mfree(word);
                 r = extract_first_word(&p, &word, NULL, 0);
-                assert_se(r >= 0);
+                ASSERT_OK(r);
                 if (r == 0)
                         continue;
 
@@ -742,12 +741,12 @@ static int find_libraries(const char *exec, char ***ret) {
 
                 word = mfree(word);
                 r = extract_first_word(&p, &word, NULL, 0);
-                assert_se(r >= 0);
+                ASSERT_OK(r);
                 if (r == 0)
                         continue;
 
                 if (path_is_absolute(word)) {
-                        assert_se(strv_consume(&libraries, TAKE_PTR(word)) >= 0);
+                        ASSERT_OK(strv_consume(&libraries, TAKE_PTR(word)));
                         continue;
                 }
         }
@@ -761,7 +760,7 @@ static void test_exec_mount_apivfs(Manager *m) {
         _cleanup_strv_free_ char **libraries = NULL, **libraries_test = NULL;
         int r;
 
-        assert_se(user_runtime_unit_dir);
+        ASSERT_NOT_NULL(user_runtime_unit_dir);
 
         r = find_executable("ldd", NULL);
         if (r < 0) {
@@ -782,22 +781,22 @@ static void test_exec_mount_apivfs(Manager *m) {
         if (MANAGER_IS_USER(m) && !have_userns_privileges())
                 return (void)log_notice("Skipping %s, do not have user namespace privileges", __func__);
 
-        assert_se(find_libraries(fullpath_touch, &libraries) >= 0);
-        assert_se(find_libraries(fullpath_test, &libraries_test) >= 0);
-        assert_se(strv_extend_strv(&libraries, libraries_test, true) >= 0);
+        ASSERT_OK(find_libraries(fullpath_touch, &libraries));
+        ASSERT_OK(find_libraries(fullpath_test, &libraries_test));
+        ASSERT_OK(strv_extend_strv(&libraries, libraries_test, true));
 
-        assert_se(strextend(&data, "[Service]\n"));
-        assert_se(strextend(&data, "ExecStart=", fullpath_touch, " /aaa\n"));
-        assert_se(strextend(&data, "ExecStart=", fullpath_test, " -f /aaa\n"));
-        assert_se(strextend(&data, "BindReadOnlyPaths=", fullpath_touch, "\n"));
-        assert_se(strextend(&data, "BindReadOnlyPaths=", fullpath_test, "\n"));
+        ASSERT_NOT_NULL(strextend(&data, "[Service]\n"));
+        ASSERT_NOT_NULL(strextend(&data, "ExecStart=", fullpath_touch, " /aaa\n"));
+        ASSERT_NOT_NULL(strextend(&data, "ExecStart=", fullpath_test, " -f /aaa\n"));
+        ASSERT_NOT_NULL(strextend(&data, "BindReadOnlyPaths=", fullpath_touch, "\n"));
+        ASSERT_NOT_NULL(strextend(&data, "BindReadOnlyPaths=", fullpath_test, "\n"));
 
         STRV_FOREACH(p, libraries)
-                assert_se(strextend(&data, "BindReadOnlyPaths=", *p, "\n"));
+                ASSERT_NOT_NULL(strextend(&data, "BindReadOnlyPaths=", *p, "\n"));
 
-        assert_se(write_drop_in(user_runtime_unit_dir, "exec-mount-apivfs-no.service", 10, "bind-mount", data) >= 0);
+        ASSERT_OK(write_drop_in(user_runtime_unit_dir, "exec-mount-apivfs-no.service", 10, "bind-mount", data));
 
-        assert_se(mkdir_p("/tmp/test-exec-mount-apivfs-no/root", 0755) >= 0);
+        ASSERT_OK(mkdir_p("/tmp/test-exec-mount-apivfs-no/root", 0755));
 
         test(m, "exec-mount-apivfs-no.service", can_unshare || !MANAGER_IS_SYSTEM(m) ? 0 : EXIT_NAMESPACE, CLD_EXITED);
 
@@ -977,7 +976,7 @@ static char* private_directory_bad(Manager *m) {
                 _cleanup_free_ char *p = NULL;
                 struct stat st;
 
-                assert_se(p = path_join(m->prefix[dt], "private"));
+                ASSERT_NOT_NULL(p = path_join(m->prefix[dt], "private"));
 
                 if (stat(p, &st) >= 0 &&
                     (st.st_mode & (S_IRWXG|S_IRWXO)))
@@ -1055,7 +1054,7 @@ static void test_exec_environmentfile(Manager *m) {
         int r;
 
         r = write_string_file("/tmp/test-exec_environmentfile.conf", e, WRITE_STRING_FILE_CREATE);
-        assert_se(r == 0);
+        ASSERT_OK(r);
 
         test(m, "exec-environmentfile.service", 0, CLD_EXITED);
 
@@ -1074,19 +1073,19 @@ static void test_exec_passenvironment(Manager *m) {
          * This is still a good approximation of how a test for MANAGER_SYSTEM
          * would work.
          */
-        assert_se(setenv("VAR1", "word1 word2", 1) == 0);
-        assert_se(setenv("VAR2", "word3", 1) == 0);
-        assert_se(setenv("VAR3", "$word 5 6", 1) == 0);
-        assert_se(setenv("VAR4", "new\nline", 1) == 0);
-        assert_se(setenv("VAR5", "passwordwithbackslashes", 1) == 0);
+        ASSERT_OK_ERRNO(setenv("VAR1", "word1 word2", 1));
+        ASSERT_OK_ERRNO(setenv("VAR2", "word3", 1));
+        ASSERT_OK_ERRNO(setenv("VAR3", "$word 5 6", 1));
+        ASSERT_OK_ERRNO(setenv("VAR4", "new\nline", 1));
+        ASSERT_OK_ERRNO(setenv("VAR5", "passwordwithbackslashes", 1));
         test(m, "exec-passenvironment.service", 0, CLD_EXITED);
         test(m, "exec-passenvironment-repeated.service", 0, CLD_EXITED);
         test(m, "exec-passenvironment-empty.service", 0, CLD_EXITED);
-        assert_se(unsetenv("VAR1") == 0);
-        assert_se(unsetenv("VAR2") == 0);
-        assert_se(unsetenv("VAR3") == 0);
-        assert_se(unsetenv("VAR4") == 0);
-        assert_se(unsetenv("VAR5") == 0);
+        ASSERT_OK_ERRNO(unsetenv("VAR1"));
+        ASSERT_OK_ERRNO(unsetenv("VAR2"));
+        ASSERT_OK_ERRNO(unsetenv("VAR3"));
+        ASSERT_OK_ERRNO(unsetenv("VAR4"));
+        ASSERT_OK_ERRNO(unsetenv("VAR5"));
         test(m, "exec-passenvironment-absent.service", 0, CLD_EXITED);
 }
 
@@ -1368,34 +1367,34 @@ static void run_tests(RuntimeScope scope, char **patterns) {
                 {},
         };
 
-        assert_se(unsetenv("USER") == 0);
-        assert_se(unsetenv("LOGNAME") == 0);
-        assert_se(unsetenv("SHELL") == 0);
-        assert_se(unsetenv("HOME") == 0);
-        assert_se(unsetenv("TMPDIR") == 0);
+        ASSERT_OK_ERRNO(unsetenv("USER"));
+        ASSERT_OK_ERRNO(unsetenv("LOGNAME"));
+        ASSERT_OK_ERRNO(unsetenv("SHELL"));
+        ASSERT_OK_ERRNO(unsetenv("HOME"));
+        ASSERT_OK_ERRNO(unsetenv("TMPDIR"));
 
         /* Unset VARx, especially, VAR1, VAR2 and VAR3, which are used in the PassEnvironment test cases,
          * otherwise (and if they are present in the environment), `manager_default_environment` will copy
          * them into the default environment which is passed to each created job, which will make the tests
          * that expect those not to be present to fail. */
-        assert_se(unsetenv("VAR1") == 0);
-        assert_se(unsetenv("VAR2") == 0);
-        assert_se(unsetenv("VAR3") == 0);
-        assert_se(unsetenv("VAR4") == 0);
-        assert_se(unsetenv("VAR5") == 0);
+        ASSERT_OK_ERRNO(unsetenv("VAR1"));
+        ASSERT_OK_ERRNO(unsetenv("VAR2"));
+        ASSERT_OK_ERRNO(unsetenv("VAR3"));
+        ASSERT_OK_ERRNO(unsetenv("VAR4"));
+        ASSERT_OK_ERRNO(unsetenv("VAR5"));
 
-        assert_se(runtime_dir = setup_fake_runtime_dir());
-        assert_se(user_runtime_unit_dir = path_join(runtime_dir, "systemd/user"));
-        assert_se(unit_paths = strjoin(PRIVATE_UNIT_DIR, ":", user_runtime_unit_dir));
-        assert_se(set_unit_path(unit_paths) >= 0);
+        ASSERT_NOT_NULL(runtime_dir = setup_fake_runtime_dir());
+        ASSERT_NOT_NULL(user_runtime_unit_dir = path_join(runtime_dir, "systemd/user"));
+        ASSERT_NOT_NULL(unit_paths = strjoin(PRIVATE_UNIT_DIR, ":", user_runtime_unit_dir));
+        ASSERT_OK(set_unit_path(unit_paths));
 
         r = manager_new(scope, MANAGER_TEST_RUN_BASIC, &m);
         if (manager_errno_skip_test(r))
                 return (void) log_tests_skipped_errno(r, "manager_new");
-        assert_se(r >= 0);
+        ASSERT_OK(r);
 
         m->defaults.std_output = EXEC_OUTPUT_INHERIT; /* don't rely on host journald */
-        assert_se(manager_startup(m, NULL, NULL, NULL) >= 0);
+        ASSERT_OK(manager_startup(m, NULL, NULL, NULL));
 
         /* Uncomment below if you want to make debugging logs stored to journal. */
         //manager_override_log_target(m, LOG_TARGET_AUTO);
@@ -1434,64 +1433,66 @@ static int prepare_ns(const char *process_name) {
                       FORK_NEW_MOUNTNS |
                       FORK_MOUNTNS_SLAVE,
                       NULL);
-        assert_se(r >= 0);
+        ASSERT_OK(r);
         if (r == 0) {
                 _cleanup_free_ char *unit_dir = NULL, *build_dir = NULL, *build_dir_mount = NULL;
                 int ret;
 
                 /* Make "/" read-only. */
-                assert_se(mount_nofollow_verbose(LOG_DEBUG, NULL, "/", NULL, MS_BIND|MS_REMOUNT|MS_RDONLY, NULL) >= 0);
+                ASSERT_OK(mount_nofollow_verbose(LOG_DEBUG, NULL, "/", NULL, MS_BIND|MS_REMOUNT|MS_RDONLY, NULL));
 
                 /* Creating a new user namespace in the above means all MS_SHARED mounts become MS_SLAVE.
                  * Let's put them back to MS_SHARED here, since that's what we want as defaults. (This will
                  * not reconnect propagation, but simply create new peer groups for all our mounts). */
-                assert_se(mount_follow_verbose(LOG_DEBUG, NULL, "/", NULL, MS_SHARED|MS_REC, NULL) >= 0);
+                ASSERT_OK(mount_follow_verbose(LOG_DEBUG, NULL, "/", NULL, MS_SHARED|MS_REC, NULL));
 
-                assert_se(mkdir_p(PRIVATE_UNIT_DIR, 0755) >= 0);
-                assert_se(mount_nofollow_verbose(LOG_DEBUG, "tmpfs", PRIVATE_UNIT_DIR, "tmpfs", MS_NOSUID|MS_NODEV, NULL) >= 0);
+                ASSERT_OK(mkdir_p(PRIVATE_UNIT_DIR, 0755));
+                ASSERT_OK(mount_nofollow_verbose(LOG_DEBUG, "tmpfs", PRIVATE_UNIT_DIR, "tmpfs", MS_NOSUID|MS_NODEV, NULL));
                 /* Mark our test "playground" as MS_SLAVE, so we can MS_MOVE mounts underneath it. */
-                assert_se(mount_nofollow_verbose(LOG_DEBUG, NULL, PRIVATE_UNIT_DIR, NULL, MS_SLAVE, NULL) >= 0);
+                ASSERT_OK(mount_nofollow_verbose(LOG_DEBUG, NULL, PRIVATE_UNIT_DIR, NULL, MS_SLAVE, NULL));
 
                 /* Copy unit files to make them accessible even when unprivileged. */
-                assert_se(get_testdata_dir("test-execute/", &unit_dir) >= 0);
-                assert_se(copy_directory_at(AT_FDCWD, unit_dir, AT_FDCWD, PRIVATE_UNIT_DIR, COPY_MERGE_EMPTY) >= 0);
+                ASSERT_OK(get_testdata_dir("test-execute/", &unit_dir));
+                ASSERT_OK(copy_directory_at(AT_FDCWD, unit_dir, AT_FDCWD, PRIVATE_UNIT_DIR, COPY_MERGE_EMPTY));
 
                 /* Mount tmpfs on the following directories to make not StateDirectory= or friends disturb the host. */
                 ret = get_build_exec_dir(&build_dir);
-                assert_se(ret >= 0 || ret == -ENOEXEC);
+                if (ret != -ENOEXEC)
+                        ASSERT_OK(ret);
 
                 if (build_dir) {
                         /* Account for a build directory being in one of the soon-to-be-tmpfs directories. If we
                          * overmount it with an empty tmpfs, manager_new() will pin the wrong systemd-executor binary,
                          * which can then lead to unexpected (and painful to debug) test fails. */
-                        assert_se(access(build_dir, F_OK) >= 0);
-                        assert_se(build_dir_mount = path_join(PRIVATE_UNIT_DIR, "build_dir"));
-                        assert_se(mkdir_p(build_dir_mount, 0755) >= 0);
-                        assert_se(mount_nofollow_verbose(LOG_DEBUG, build_dir, build_dir_mount, NULL, MS_BIND, NULL) >= 0);
+                        ASSERT_OK_ERRNO(access(build_dir, F_OK));
+                        ASSERT_NOT_NULL(build_dir_mount = path_join(PRIVATE_UNIT_DIR, "build_dir"));
+                        ASSERT_OK(mkdir_p(build_dir_mount, 0755));
+                        ASSERT_OK(mount_nofollow_verbose(LOG_DEBUG, build_dir, build_dir_mount, NULL, MS_BIND, NULL));
                 }
 
                 FOREACH_STRING(p, "/dev/shm", "/root", "/tmp", "/var/tmp", "/var/lib")
-                        assert_se(mount_nofollow_verbose(LOG_DEBUG, "tmpfs", p, "tmpfs", MS_NOSUID|MS_NODEV, NULL) >= 0);
+                        ASSERT_OK(mount_nofollow_verbose(LOG_DEBUG, "tmpfs", p, "tmpfs", MS_NOSUID|MS_NODEV, NULL));
 
                 if (build_dir_mount) {
                         ret = RET_NERRNO(access(build_dir, F_OK));
-                        assert_se(ret >= 0 || ret == -ENOENT);
+                        if (ret != -ENOENT)
+                                ASSERT_OK(ret);
 
                         if (ret == -ENOENT) {
                                 /* The build directory got overmounted by tmpfs, so let's use the "backup" bind mount to
                                  * bring it back. */
-                                assert_se(mkdir_p(build_dir, 0755) >= 0);
-                                assert_se(mount_nofollow_verbose(LOG_DEBUG, build_dir_mount, build_dir, NULL, MS_MOVE, NULL) >= 0);
+                                ASSERT_OK(mkdir_p(build_dir, 0755));
+                                ASSERT_OK(mount_nofollow_verbose(LOG_DEBUG, build_dir_mount, build_dir, NULL, MS_MOVE, NULL));
                         }
                 }
 
                 /* Prepare credstore like tmpfiles.d/credstore.conf for LoadCredential= tests. */
                 FOREACH_STRING(p, "/run/credstore", "/run/credstore.encrypted") {
-                        assert_se(mkdir_p(p, 0) >= 0);
-                        assert_se(mount_nofollow_verbose(LOG_DEBUG, "tmpfs", p, "tmpfs", MS_NOSUID|MS_NODEV, "mode=0000") >= 0);
+                        ASSERT_OK(mkdir_p(p, 0));
+                        ASSERT_OK(mount_nofollow_verbose(LOG_DEBUG, "tmpfs", p, "tmpfs", MS_NOSUID|MS_NODEV, "mode=0000"));
                 }
 
-                assert_se(write_string_file("/run/credstore/test-execute.load-credential", "foo", WRITE_STRING_FILE_CREATE) >= 0);
+                ASSERT_OK(write_string_file("/run/credstore/test-execute.load-credential", "foo", WRITE_STRING_FILE_CREATE));
         }
 
         return r;
@@ -1504,7 +1505,7 @@ TEST(run_tests_root) {
                 return (void) log_tests_skipped("unshare() is disabled");
 
         /* safe_fork() clears saved_argv in the child process. Let's copy it. */
-        assert_se(filters = strv_copy(strv_skip(saved_argv, 1)));
+        ASSERT_NOT_NULL(filters = strv_copy(strv_skip(saved_argv, 1)));
 
         if (prepare_ns("(test-execute-root)") == 0) {
                 can_unshare = true;
@@ -1530,19 +1531,18 @@ TEST(run_tests_without_unshare) {
                 return (void) log_tests_skipped("Seccomp not available, cannot run unshare() filtered tests");
 
         /* safe_fork() clears saved_argv in the child process. Let's copy it. */
-        assert_se(filters = strv_copy(strv_skip(saved_argv, 1)));
+        ASSERT_NOT_NULL(filters = strv_copy(strv_skip(saved_argv, 1)));
 
         if (prepare_ns("(test-execute-without-unshare)") == 0) {
                 _cleanup_hashmap_free_ Hashmap *s = NULL;
 
                 r = seccomp_syscall_resolve_name("unshare");
-                assert_se(r != __NR_SCMP_ERROR);
-                assert_se(hashmap_ensure_put(&s, NULL, UINT32_TO_PTR(r + 1), INT_TO_PTR(-1)) >= 0);
-                assert_se(seccomp_load_syscall_filter_set_raw(SCMP_ACT_ALLOW, s, SCMP_ACT_ERRNO(EOPNOTSUPP), true) >= 0);
+                ASSERT_NE(r, __NR_SCMP_ERROR);
+                ASSERT_OK(hashmap_ensure_put(&s, NULL, UINT32_TO_PTR(r + 1), INT_TO_PTR(-1)));
+                ASSERT_OK(seccomp_load_syscall_filter_set_raw(SCMP_ACT_ALLOW, s, SCMP_ACT_ERRNO(EOPNOTSUPP), true));
 
                 /* Check unshare() is actually filtered. */
-                assert_se(unshare(CLONE_NEWNS) < 0);
-                assert_se(errno == EOPNOTSUPP);
+                ASSERT_ERROR_ERRNO(unshare(CLONE_NEWNS), EOPNOTSUPP);
 
                 can_unshare = false;
                 run_tests(RUNTIME_SCOPE_SYSTEM, filters);
@@ -1560,10 +1560,10 @@ TEST(run_tests_unprivileged) {
                 return (void) log_tests_skipped("unshare() is disabled");
 
         /* safe_fork() clears saved_argv in the child process. Let's copy it. */
-        assert_se(filters = strv_copy(strv_skip(saved_argv, 1)));
+        ASSERT_NOT_NULL(filters = strv_copy(strv_skip(saved_argv, 1)));
 
         if (prepare_ns("(test-execute-unprivileged)") == 0) {
-                assert_se(capability_bounding_set_drop(0, /* right_now = */ true) >= 0);
+                ASSERT_OK(capability_bounding_set_drop(0, /* right_now = */ true));
 
                 can_unshare = false;
                 run_tests(RUNTIME_SCOPE_USER, filters);
index 8f00e598fdd676728843bac01c488e1be80a7fa8..cfbd8e270a8792051fc133e9e065cfda00a0a62a 100644 (file)
@@ -116,9 +116,18 @@ TEST(fdset_close_others) {
         copyfd = fdset_put_dup(fdset, fd);
         assert_se(copyfd >= 0);
 
+        /* fdset_close_others() will close any logging file descriptors as well, so close them beforehand
+         * and reopen them again afterwards. */
+        log_close();
         assert_se(fdset_close_others(fdset) >= 0);
+
         flags = fcntl(fd, F_GETFD);
         assert_se(flags < 0);
+
+        /* Open log again after checking that fd is invalid, since reopening the log might make fd a valid
+         * file descriptor again. */
+        (void) log_open();
+
         flags = fcntl(copyfd, F_GETFD);
         assert_se(flags >= 0);
 }
index 3ddbeec0fc78c509cc0f302b8468d33b4ab627d6..48fdbba6c7de971a1919217610d315f6d450ff0f 100644 (file)
@@ -199,7 +199,7 @@ TEST(id128) {
 }
 
 TEST(sd_id128_get_invocation) {
-        sd_id128_t id;
+        sd_id128_t id = SD_ID128_NULL;
         int r;
 
         /* Query the invocation ID */
@@ -208,6 +208,36 @@ TEST(sd_id128_get_invocation) {
                 log_warning_errno(r, "Failed to get invocation ID, ignoring: %m");
         else
                 log_info("Invocation ID: " SD_ID128_FORMAT_STR, SD_ID128_FORMAT_VAL(id));
+
+        sd_id128_t appid = SD_ID128_NULL;
+        r = sd_id128_get_invocation_app_specific(SD_ID128_MAKE(59,36,e9,92,fd,11,42,fe,87,c9,e9,b5,6c,9e,4f,04), &appid);
+        if (r < 0)
+                log_warning_errno(r, "Failed to get invocation ID, ignoring: %m");
+        else {
+                assert(!sd_id128_equal(id, appid));
+                log_info("Per-App Invocation ID: " SD_ID128_FORMAT_STR, SD_ID128_FORMAT_VAL(appid));
+        }
+
+        sd_id128_t appid2 = SD_ID128_NULL;
+        r = sd_id128_get_invocation_app_specific(SD_ID128_MAKE(59,36,e9,92,fd,11,42,fe,87,c9,e9,b5,6c,9e,4f,05), &appid2); /* slightly different appid */
+        if (r < 0)
+                log_warning_errno(r, "Failed to get invocation ID, ignoring: %m");
+        else {
+                assert(!sd_id128_equal(id, appid2));
+                assert(!sd_id128_equal(appid, appid2));
+                log_info("Per-App Invocation ID 2: " SD_ID128_FORMAT_STR, SD_ID128_FORMAT_VAL(appid2));
+        }
+
+        sd_id128_t appid3 = SD_ID128_NULL;
+        r = sd_id128_get_invocation_app_specific(SD_ID128_MAKE(59,36,e9,92,fd,11,42,fe,87,c9,e9,b5,6c,9e,4f,04), &appid3); /* same appid as before */
+        if (r < 0)
+                log_warning_errno(r, "Failed to get invocation ID, ignoring: %m");
+        else {
+                assert(!sd_id128_equal(id, appid3));
+                assert(sd_id128_equal(appid, appid3));
+                assert(!sd_id128_equal(appid2, appid3));
+                log_info("Per-App Invocation ID 3: " SD_ID128_FORMAT_STR, SD_ID128_FORMAT_VAL(appid3));
+        }
 }
 
 TEST(benchmark_sd_id128_get_machine_app_specific) {
index 05e15c8bdcfae01e54e5433cedaf8ad821829a63..9e2875d8a71ffae55e3df5fc990afea92c5e8b80 100644 (file)
@@ -1,7 +1,9 @@
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 
 #include <stddef.h>
+#include <sys/stat.h>
 
+#include "errno-util.h"
 #include "log.h"
 #include "macro.h"
 #include "tests.h"
@@ -1120,6 +1122,19 @@ TEST(ASSERT) {
         ASSERT_SIGNAL(ASSERT_OK_ERRNO(-1), SIGABRT);
         ASSERT_SIGNAL(ASSERT_OK_ERRNO(-ENOANO), SIGABRT);
 
+        ASSERT_ERROR(-ENOENT, ENOENT);
+        ASSERT_ERROR(RET_NERRNO(mkdir("/i/will/fail/with/enoent", 666)), ENOENT);
+        ASSERT_SIGNAL(ASSERT_ERROR(0, ENOENT), SIGABRT);
+        ASSERT_SIGNAL(ASSERT_ERROR(RET_NERRNO(mkdir("/i/will/fail/with/enoent", 666)), ENOANO), SIGABRT);
+
+        errno = ENOENT;
+        ASSERT_ERROR_ERRNO(-1, ENOENT);
+        errno = 0;
+        ASSERT_ERROR_ERRNO(mkdir("/i/will/fail/with/enoent", 666), ENOENT);
+        ASSERT_SIGNAL(ASSERT_ERROR_ERRNO(0, ENOENT), SIGABRT);
+        errno = 0;
+        ASSERT_SIGNAL(ASSERT_ERROR_ERRNO(mkdir("/i/will/fail/with/enoent", 666), ENOANO), SIGABRT);
+
         ASSERT_TRUE(true);
         ASSERT_TRUE(255);
         ASSERT_TRUE(getpid());
index a2711f059e393b6a1ccbc2f8f3fee039805d3dac..4f6da39f48e99b47d587463fdbe623f0112b1623 100644 (file)
@@ -305,7 +305,7 @@ TEST(make_mount_switch_root) {
                 { "/", true  },
         };
 
-        FOREACH_ARRAY(i, table, ELEMENTSOF(table)) {
+        FOREACH_ELEMENT(i, table) {
                 r = safe_fork("(switch-root)",
                               FORK_RESET_SIGNALS |
                               FORK_CLOSE_ALL_FDS |
@@ -358,7 +358,7 @@ TEST(umount_recursive) {
 
         int r;
 
-        FOREACH_ARRAY(t, test_table, ELEMENTSOF(test_table)) {
+        FOREACH_ELEMENT(t, test_table) {
 
                 r = safe_fork("(umount-rec)",
                               FORK_RESET_SIGNALS |
index 51dc492e965dee9bfb1155fe24d42066208983bb..d0ea0f0215570cf2a720baf304a3368735094e94 100644 (file)
@@ -136,7 +136,7 @@ static const struct {
 TEST(digest_size) {
         size_t size;
 
-        FOREACH_ARRAY(t, digest_size_table, ELEMENTSOF(digest_size_table)) {
+        FOREACH_ELEMENT(t, digest_size_table) {
                 assert(openssl_digest_size(t->alg, &size) >= 0);
                 assert_se(size == t->size);
 
index bcb3dfd05586afcc48ad7e526baac0f142bf43e1..e34aa10e10d78e282ca5759a03e7dfd5a87b70c9 100644 (file)
@@ -290,7 +290,7 @@ TEST(passfd_contents_read) {
         /* Parent */
         char buf[64];
         struct iovec iov = IOVEC_MAKE(buf, sizeof(buf)-1);
-        _cleanup_close_ int fd;
+        _cleanup_close_ int fd = -EBADF;
         ssize_t k;
 
         pair[1] = safe_close(pair[1]);
index a69f6f06e0a7fda0dea6308c7dbc8974159d42fc..6f274c917c5ae17f6b36d56984d82a4a926044bf 100644 (file)
@@ -192,7 +192,7 @@ TEST(inode_type_from_string) {
                 S_IFSOCK,
         };
 
-        FOREACH_ARRAY(m, types, ELEMENTSOF(types))
+        FOREACH_ELEMENT(m, types)
                 assert_se(inode_type_from_string(inode_type_to_string(*m)) == *m);
 }
 
index 1a9c998818828c6eaefa6ef432e579b7112fcec0..3b5a375c883eeba43ccadf374598873a80a0cbd0 100644 (file)
@@ -1266,7 +1266,7 @@ static void check_seal_unseal(Tpm2Context *c) {
         check_seal_unseal_for_handle(c, 0);
         check_seal_unseal_for_handle(c, TPM2_SRK_HANDLE);
 
-        FOREACH_ARRAY(template, test_templates, ELEMENTSOF(test_templates)) {
+        FOREACH_ELEMENT(template, test_templates) {
                 TPM2B_PUBLIC public = {
                         .publicArea = **template,
                         .size = sizeof(**template),
index feb16b61a464487aa97150e055340e9d3d9bd3e5..88646ec053eb64cb762138a5faa39ec89315286a 100644 (file)
@@ -47,7 +47,7 @@ TEST(path_pick) {
 
         PickFilter filter = {
                 .architecture = _ARCHITECTURE_INVALID,
-                .suffix = ".raw",
+                .suffix = STRV_MAKE(".raw"),
         };
 
         if (IN_SET(native_architecture(), ARCHITECTURE_X86, ARCHITECTURE_X86_64)) {
index 4840ba47b1c1b386b8623a2ec0e5c19fe79b6e4b..e3b4367ec0536180d4e6dc5920ab62b47e4dc72c 100644 (file)
@@ -596,6 +596,8 @@ static int property_get_rtc_time(
                 log_warning("/dev/rtc is busy. Is somebody keeping it open continuously? That's not a good idea... Returning a bogus RTC timestamp.");
         else if (r == -ENOENT)
                 log_debug("/dev/rtc not found.");
+        else if (r == -ENODATA)
+                log_debug("/dev/rtc has no valid time, power loss probably occurred?");
         else if (r < 0)
                 return sd_bus_error_set_errnof(error, r, "Failed to read RTC: %m");
         else
index 846d5b8ac8389005e5a62967983327712f604304..35628fc02a88bfaf2783f194f74b89ec90dff92a 100644 (file)
@@ -212,9 +212,9 @@ static int load_public_key_tpm2(struct public_key_data *ret) {
 
         assert(ret);
 
-        r = tpm2_context_new(arg_tpm2_device, &c);
+        r = tpm2_context_new_or_warn(arg_tpm2_device, &c);
         if (r < 0)
-                return log_error_errno(r, "Failed to create TPM2 context: %m");
+                return r;
 
         r = tpm2_get_or_create_srk(
                         c,
index 686377200f4026a0ea72e5ec1cce4b87e9b41b5f..50d921b7a80e88151d79ae68e4ff90e82273c003 100755 (executable)
@@ -303,6 +303,7 @@ class Uname:
 DEFAULT_SECTIONS_TO_SHOW = {
         '.linux'    : 'binary',
         '.initrd'   : 'binary',
+        '.ucode'    : 'binary',
         '.splash'   : 'binary',
         '.dtb'      : 'binary',
         '.cmdline'  : 'text',
@@ -855,6 +856,7 @@ def make_uki(opts):
         ('.splash',  opts.splash,     True ),
         ('.pcrpkey', pcrpkey,         True ),
         ('.initrd',  initrd,          True ),
+        ('.ucode',   opts.microcode,  True ),
 
         # linux shall be last to leave breathing room for decompression.
         # We'll add it later.
@@ -1279,6 +1281,14 @@ CONFIG_ITEMS = [
         config_push = ConfigItem.config_list_prepend,
     ),
 
+    ConfigItem(
+        '--microcode',
+        metavar = 'UCODE',
+        type = pathlib.Path,
+        help = 'microcode file [.ucode section]',
+        config_key = 'UKI/Microcode',
+    ),
+
     ConfigItem(
         ('--config', '-c'),
         metavar = 'PATH',
index 4ee935e0441335914f6fb2c871543c75b7fde718..c376676e8d0a521ffea2e3a54d0029e6ee6ee0cc 100644 (file)
@@ -82,7 +82,7 @@ static int get_current_runlevel(Context *c) {
         assert(c);
 
         for (unsigned n_attempts = 0;;) {
-                FOREACH_ARRAY(e, table, ELEMENTSOF(table)) {
+                FOREACH_ELEMENT(e, table) {
                         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
                         _cleanup_free_ char *state = NULL, *path = NULL;
 
index 918b4d772f28ea126783e0fad82945a2ff32880e..1718419407bc0089e379ef76174fd820f3eeb3dc 100644 (file)
@@ -174,7 +174,7 @@ static int table_add_uid_boundaries(Table *table, const UIDRange *p) {
 
         assert(table);
 
-        FOREACH_ARRAY(i, uid_range_table, ELEMENTSOF(uid_range_table)) {
+        FOREACH_ELEMENT(i, uid_range_table) {
                 _cleanup_free_ char *name = NULL, *comment = NULL;
 
                 if (!uid_range_covers(p, i->first, i->last - i->first + 1))
@@ -532,7 +532,7 @@ static int table_add_gid_boundaries(Table *table, const UIDRange *p) {
 
         assert(table);
 
-        FOREACH_ARRAY(i, uid_range_table, ELEMENTSOF(uid_range_table)) {
+        FOREACH_ELEMENT(i, uid_range_table) {
                 _cleanup_free_ char *name = NULL, *comment = NULL;
 
                 if (!uid_range_covers(p, i->first, i->last - i->first + 1))
index 0eba4569715140e207f4d32e7ad95c87b5243279..9366ce111da4dadc164524e1402a042d4cee96fd 100644 (file)
@@ -1248,7 +1248,7 @@ static int run_virtual_machine(int kvm_device_fd, int vhost_device_fd) {
                 log_warning("Couldn't find OVMF firmware blob with Secure Boot support, "
                             "falling back to OVMF firmware blobs without Secure Boot support.");
 
-        shm = arg_directory ? ",memory-backend=mem" : "";
+        shm = arg_directory || arg_runtime_mounts.n_mounts != 0 ? ",memory-backend=mem" : "";
         if (ARCHITECTURE_SUPPORTS_SMM)
                 machine = strjoin("type=" QEMU_MACHINE_TYPE ",smm=", on_off(ovmf_config->supports_sb), shm);
         else
@@ -1294,6 +1294,24 @@ static int run_virtual_machine(int kvm_device_fd, int vhost_device_fd) {
                 if (strv_extend_many(&cmdline, "-uuid", SD_ID128_TO_UUID_STRING(arg_uuid)) < 0)
                         return log_oom();
 
+        /* Derive a vmgenid automatically from the invocation ID, in a deterministic way. */
+        sd_id128_t vmgenid;
+        r = sd_id128_get_invocation_app_specific(SD_ID128_MAKE(bd,84,6d,e3,e4,7d,4b,6c,a6,85,4a,87,0f,3c,a3,a0), &vmgenid);
+        if (r < 0) {
+                log_debug_errno(r, "Failed to get invocation ID, making up randomized vmgenid: %m");
+
+                r = sd_id128_randomize(&vmgenid);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to make up randomized vmgenid: %m");
+        }
+
+        _cleanup_free_ char *vmgenid_device = NULL;
+        if (asprintf(&vmgenid_device, "vmgenid,guid=" SD_ID128_UUID_FORMAT_STR, SD_ID128_FORMAT_VAL(vmgenid)) < 0)
+                return log_oom();
+
+        if (strv_extend_many(&cmdline, "-device", vmgenid_device) < 0)
+                return log_oom();
+
         /* if we are going to be starting any units with state then create our runtime dir */
         if (arg_tpm != 0 || arg_directory || arg_runtime_mounts.n_mounts != 0) {
                 r = runtime_directory(&arg_runtime_directory, arg_privileged ? RUNTIME_SCOPE_SYSTEM : RUNTIME_SCOPE_USER, "systemd/vmspawn");
@@ -1421,7 +1439,13 @@ static int run_virtual_machine(int kvm_device_fd, int vhost_device_fd) {
                 pass_fds[n_pass_fds++] = device_fd;
         }
 
-        r = strv_extend_many(&cmdline, "-cpu", "max");
+        r = strv_extend_many(&cmdline, "-cpu",
+#ifdef __x86_64__
+                             "max,hv_relaxed,hv-vapic,hv-time"
+#else
+                             "max"
+#endif
+        );
         if (r < 0)
                 return log_oom();
 
@@ -1875,6 +1899,18 @@ static int run_virtual_machine(int kvm_device_fd, int vhost_device_fd) {
                         return log_error_errno(r, "Failed to call getsockname on VSOCK: %m");
         }
 
+        const char *e = secure_getenv("SYSTEMD_VMSPAWN_QEMU_EXTRA");
+        if (e) {
+                _cleanup_strv_free_ char **extra = NULL;
+
+                r = strv_split_full(&extra, e, /* separator= */ NULL, EXTRACT_CUNESCAPE|EXTRACT_UNQUOTE);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to split $SYSTEMD_VMSPAWN_QEMU_EXTRA environment variable: %m");
+
+                if (strv_extend_strv(&cmdline, extra, /* filter_duplicates= */ false) < 0)
+                        return log_oom();
+        }
+
         if (DEBUG_LOGGING) {
                 _cleanup_free_ char *joined = quote_command_line(cmdline, SHELL_ESCAPE_EMPTY);
                 if (!joined)
index f3afc33a5897efb74e08c5780c59173ec88abc4b..0504b7733b7e91a9214c844b1dacbb32aca6bf9d 100644 (file)
@@ -241,7 +241,7 @@ static int run(int argc, char *argv[]) {
                                       .basename = arg_filter_basename,
                                       .version = arg_filter_version,
                                       .architecture = arg_filter_architecture,
-                                      .suffix = arg_filter_suffix,
+                                      .suffix = STRV_MAKE(arg_filter_suffix),
                                       .type_mask = arg_filter_type_mask,
                               },
                               arg_flags,
index 0c04e2d4a6a40549c5d0d8dc2975f267438c2f57..44e59ea951dbfc398087caef6e32b510ef3b7b0a 100644 (file)
@@ -28,6 +28,22 @@ To run just one of the cases:
 
 $ sudo make -C test/TEST-01-BASIC clean setup run
 
+To run the meson-based integration test config
+enable integration tests and options for required commands with the following:
+
+$ meson configure build -Dintegration-tests=true -Dremote=enabled -Dopenssl=enabled -Dblkid=enabled -Dtpm2=enabled
+
+Once enabled the integration tests can be run with:
+
+$ sudo meson test -C build/ --suite integration-tests --num-processes "$((nproc / 2))"
+
+As usual, specific tests can be run in meson by appending the name of the test
+which is usually the name of the directory e.g.
+
+$ sudo meson test -C build/ --suite integration-tests --num-processes "$((nproc / 2))" TEST-01-BASIC
+
+See `meson introspect build --tests` for a list of tests.
+
 Specifying the build directory
 ==============================
 
diff --git a/test/TEST-02-UNITTESTS/meson.build b/test/TEST-02-UNITTESTS/meson.build
new file mode 100644 (file)
index 0000000..6bc0483
--- /dev/null
@@ -0,0 +1,13 @@
+test_params += {
+        'mkosi_args': test_params['mkosi_args'] + [
+                '--kernel-command-line-extra=' + ' '.join([
+                        '''
+frobnicate!
+
+systemd.setenv=TEST_CMDLINE_NEWLINE=foo
+systemd.setenv=TEST_CMDLINE_NEWLINE=bar
+
+''',
+                ]),
+        ],
+}
diff --git a/test/integration_test_wrapper.py b/test/integration_test_wrapper.py
new file mode 100755 (executable)
index 0000000..138b6af
--- /dev/null
@@ -0,0 +1,134 @@
+#!/usr/bin/python3
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+'''Test wrapper command for driving integration tests.
+
+Note: This is deliberately rough and only intended to drive existing tests
+with the expectation that as part of formally defining the API it will be tidy.
+
+'''
+
+import argparse
+import logging
+import os
+from pathlib import Path
+import shlex
+import subprocess
+
+
+TEST_EXIT_DROPIN = """\
+[Unit]
+SuccessAction=exit
+FailureAction=exit
+"""
+
+
+EMERGENCY_EXIT_DROPIN = """\
+[Unit]
+Wants=emergency-exit.service
+"""
+
+
+EMERGENCY_EXIT_SERVICE = """\
+[Unit]
+DefaultDependencies=no
+Conflicts=shutdown.target
+Conflicts=rescue.service
+Before=shutdown.target
+Before=rescue.service
+FailureAction=exit
+
+[Service]
+ExecStart=false
+"""
+
+
+parser = argparse.ArgumentParser(description=__doc__)
+parser.add_argument('--test-name', required=True)
+parser.add_argument('--mkosi-image-name', required=True)
+parser.add_argument('--mkosi-output-path', required=True, type=Path)
+parser.add_argument('--test-number', required=True)
+parser.add_argument('--no-emergency-exit',
+                    dest='emergency_exit', default=True, action='store_false',
+                    help="Disable emergency exit drop-ins for interactive debugging")
+parser.add_argument('mkosi_args', nargs="*")
+
+def main():
+    logging.basicConfig(level=logging.DEBUG)
+    args = parser.parse_args()
+
+    test_unit_name = f"testsuite-{args.test_number}.service"
+    # Machine names shouldn't have / since it's used as a file name
+    # and it must be a valid hostname so 64 chars max
+    machine_name = args.test_name.replace('/', '_')[:64]
+
+    logging.debug(f"test name: {args.test_name}\n"
+                  f"test number: {args.test_number}\n"
+                  f"image: {args.mkosi_image_name}\n"
+                  f"mkosi output path: {args.mkosi_output_path}\n"
+                  f"mkosi args: {args.mkosi_args}\n"
+                  f"emergency exit: {args.emergency_exit}")
+
+    journal_file = Path(f"{machine_name}.journal").absolute()
+    logging.info(f"Capturing journal to {journal_file}")
+
+    mkosi_args = [
+        'mkosi',
+        '--directory', Path('..').resolve(),
+        '--output-dir', args.mkosi_output_path.absolute(),
+        '--machine', machine_name,
+        '--image', args.mkosi_image_name,
+        '--format=disk',
+        '--runtime-build-sources=no',
+        '--ephemeral',
+        '--forward-journal', journal_file,
+        *(
+            [
+                '--credential',
+                f"systemd.extra-unit.emergency-exit.service={shlex.quote(EMERGENCY_EXIT_SERVICE)} "
+                f"systemd.unit-dropin.emergency.target={shlex.quote(EMERGENCY_EXIT_DROPIN)}",
+            ]
+            if args.emergency_exit
+            else []
+        ),
+        f"--credential=systemd.unit-dropin.{test_unit_name}={shlex.quote(TEST_EXIT_DROPIN)}",
+        '--append',
+        '--kernel-command-line-extra',
+        ' '.join([
+            'systemd.hostname=H',
+            f"SYSTEMD_UNIT_PATH=/usr/lib/systemd/tests/testdata/testsuite-{args.test_number}.units:/usr/lib/systemd/tests/testdata/units:",
+            'systemd.unit=testsuite.target',
+            f"systemd.wants={test_unit_name}",
+        ]),
+        *args.mkosi_args,
+    ]
+
+    mkosi_args += ['qemu']
+
+    logging.debug(f"Running {shlex.join(os.fspath(a) for a in mkosi_args)}")
+
+    try:
+        subprocess.run(mkosi_args, check=True)
+    except subprocess.CalledProcessError as e:
+        if e.returncode not in (0, 77):
+            suggested_command = [
+                'journalctl',
+                '--all',
+                '--no-hostname',
+                '-o', 'short-monotonic',
+                '--file', journal_file,
+                f"_SYSTEMD_UNIT={test_unit_name}",
+                '+', f"SYSLOG_IDENTIFIER=testsuite-{args.test_number}.sh",
+                '+', 'PRIORITY=4',
+                '+', 'PRIORITY=3',
+                '+', 'PRIORITY=2',
+                '+', 'PRIORITY=1',
+                '+', 'PRIORITY=0',
+            ]
+            logging.info("Test failed, relevant logs can be viewed with: "
+                         f"{shlex.join(os.fspath(a) for a in suggested_command)}")
+        exit(e.returncode)
+
+
+if __name__ == '__main__':
+    main()
index aadf37a139ea610050bdb9dc1b437782d5c513cf..4009eeece2caed383020f22d09343eb9d3430a1b 100644 (file)
@@ -331,3 +331,44 @@ if want_tests != 'false' and conf.get('ENABLE_KERNEL_INSTALL') == 1
              depends : deps,
              suite : 'kernel-install')
 endif
+
+############################################################
+
+if get_option('integration-tests') != false
+        integration_test_wrapper = find_program('integration_test_wrapper.py')
+        integration_tests = {
+                '01': 'TEST-01-BASIC',
+                '02': 'TEST-02-UNITTESTS',
+        }
+        foreach test_number, dirname : integration_tests
+                test_unit_name = f'testsuite-@test_number@.service'
+                test_params = {
+                        'test_name' : dirname,
+                        'mkosi_image_name' : 'system',
+                        'mkosi_output_path' : system_mkosi,
+                        'test_number' : test_number,
+                        'mkosi_args' : [],
+                        'depends' : [system_mkosi],
+                        'timeout' : 600,
+                }
+
+                # TODO: This fs.exists call isn't included in rebuild logic
+                # so if you add a new meson.build in a subdir
+                # you need to touch another build file to get it to reparse.
+                if fs.exists(dirname / 'meson.build')
+                        subdir(dirname)
+                endif
+                args = ['--test-name', test_params['test_name'],
+                        '--mkosi-image-name', test_params['mkosi_image_name'],
+                        '--mkosi-output-path', test_params['mkosi_output_path'],
+                        '--test-number', test_params['test_number']]
+                args += ['--'] + test_params['mkosi_args']
+                test(test_params['test_name'],
+                     integration_test_wrapper,
+                     env: test_env,
+                     args : args,
+                     depends : test_params['depends'],
+                     timeout : test_params['timeout'],
+                     suite : 'integration-tests')
+        endforeach
+endif
index cf601c84afc8b8b208cbeb3967549b12abd23cd5..17b45a433914b904b67baf50c52f2d571d9a7fc7 100755 (executable)
@@ -1182,6 +1182,14 @@ class Utilities():
             print(output)
             self.assertRegex(output, r'.*elements = { [^}]*' + contents + r'[^}]* }.*')
 
+    def check_networkd_log(self, contents, since=None, trial=20):
+        for _ in range(trial):
+            if contents in read_networkd_log(since=since):
+                break
+            time.sleep(0.5)
+        else:
+            self.fail(f'"{contents}" not found in journal.')
+
 class NetworkctlTests(unittest.TestCase, Utilities):
 
     def setUp(self):
@@ -1373,8 +1381,7 @@ class NetworkctlTests(unittest.TestCase, Utilities):
         self.assertIn('Network File: /run/systemd/network/11-test-unit-file.network', output)
         self.assertIn('/run/systemd/network/11-test-unit-file.network.d/dropin.conf', output)
 
-        output = read_networkd_log()
-        self.assertIn('test1: Configuring with /run/systemd/network/11-test-unit-file.network (dropins: /run/systemd/network/11-test-unit-file.network.d/dropin.conf).', output)
+        self.check_networkd_log('test1: Configuring with /run/systemd/network/11-test-unit-file.network (dropins: /run/systemd/network/11-test-unit-file.network.d/dropin.conf).')
 
         # This test may be run on the system that has older udevd than 70f32a260b5ebb68c19ecadf5d69b3844896ba55 (v249).
         # In that case, the udev DB for the loopback network interface may already have ID_NET_LINK_FILE property.
@@ -1624,16 +1631,7 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities):
         self.wait_online('bond99:off')
         self.wait_operstate('vlan99', operstate='off', setup_state='configuring', setup_timeout=10)
 
-        # The commit b05e52000b4eee764b383cc3031da0a3739e996e adds ", ignoring". To make it easily confirmed
-        # that the issue is fixed by the commit, let's allow to match both string.
-        log_re = re.compile('vlan99: Could not bring up interface(, ignoring|): Network is down$', re.MULTILINE)
-        for i in range(20):
-            if i > 0:
-                time.sleep(0.5)
-            if log_re.search(read_networkd_log()):
-                break
-        else:
-            self.fail()
+        self.check_networkd_log('vlan99: Could not bring up interface, ignoring: Network is down')
 
         copy_network_unit('11-dummy.netdev', '12-dummy.netdev', '21-dummy-bond-slave.network')
         networkctl_reload()
@@ -5593,6 +5591,12 @@ class NetworkdRATests(unittest.TestCase, Utilities):
         self.assertIn('2002:da8:1:1:1a:2b:3c:4d via fe80::1 proto redirect', output)
         self.assertIn('2002:da8:1:2:1a:2b:3c:4d via fe80::2 proto redirect', output)
 
+        # Check if sd-radv refuses RS from the same interface.
+        # See https://github.com/systemd/systemd/pull/32267#discussion_r1566721306
+        since = datetime.datetime.now()
+        check_output(f'{test_ndisc_send} --interface veth-peer --type rs --dest {veth_peer_ipv6ll}')
+        self.check_networkd_log('veth-peer: RADV: Received RS from the same interface, ignoring.', since=since)
+
     def check_ndisc_mtu(self, mtu):
         for _ in range(20):
             output = read_ipv6_sysctl_attr('veth99', 'mtu')
@@ -5612,13 +5616,7 @@ class NetworkdRATests(unittest.TestCase, Utilities):
         start_networkd()
         self.wait_online('veth-peer:degraded')
 
-        for _ in range(20):
-            output = read_networkd_log()
-            if 'veth99: NDISC: Started IPv6 Router Solicitation client' in output:
-                break
-            time.sleep(0.5)
-        else:
-            self.fail('sd-ndisc does not started on veth99.')
+        self.check_networkd_log('veth99: NDISC: Started IPv6 Router Solicitation client')
 
         check_output(f'{test_ndisc_send} --interface veth-peer --type ra --lifetime 1hour --mtu 1400')
         self.check_ndisc_mtu(1400)
@@ -6032,13 +6030,7 @@ class NetworkdDHCPServerRelayAgentTests(unittest.TestCase, Utilities):
         self.wait_online('bridge-relay:routable', 'client-peer:enslaved')
 
         # For issue #30763.
-        expect = 'bridge-relay: DHCPv4 server: STARTED'
-        for _ in range(20):
-            if expect in read_networkd_log():
-                break
-            time.sleep(0.5)
-        else:
-            self.fail()
+        self.check_networkd_log('bridge-relay: DHCPv4 server: STARTED')
 
 class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
 
@@ -6510,37 +6502,19 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
         since = datetime.datetime.now()
         start_dnsmasq()
 
-        expect = 'veth99: DHCPv4 server IP address 192.168.5.1 not found in allow-list, ignoring offer.'
-        for _ in range(20):
-            if expect in read_networkd_log(since=since):
-                break
-            time.sleep(0.5)
-        else:
-            self.fail()
+        self.check_networkd_log('veth99: DHCPv4 server IP address 192.168.5.1 not found in allow-list, ignoring offer.', since=since)
 
         copy_network_unit('25-dhcp-client-allow-list.network.d/00-allow-list.conf')
         since = datetime.datetime.now()
         networkctl_reload()
 
-        expect = 'veth99: DHCPv4 server IP address 192.168.5.1 not found in allow-list, ignoring offer.'
-        for _ in range(20):
-            if expect in read_networkd_log(since=since):
-                break
-            time.sleep(0.5)
-        else:
-            self.fail()
+        self.check_networkd_log('veth99: DHCPv4 server IP address 192.168.5.1 not found in allow-list, ignoring offer.', since=since)
 
         copy_network_unit('25-dhcp-client-allow-list.network.d/10-deny-list.conf')
         since = datetime.datetime.now()
         networkctl_reload()
 
-        expect = 'veth99: DHCPv4 server IP address 192.168.5.1 found in deny-list, ignoring offer.'
-        for _ in range(20):
-            if expect in read_networkd_log(since=since):
-                break
-            time.sleep(0.5)
-        else:
-            self.fail()
+        self.check_networkd_log('veth99: DHCPv4 server IP address 192.168.5.1 found in deny-list, ignoring offer.', since=since)
 
     @unittest.skipUnless("--dhcp-rapid-commit" in run("dnsmasq --help").stdout, reason="dnsmasq is missing dhcp-rapid-commit support")
     def test_dhcp_client_rapid_commit(self):
@@ -6933,16 +6907,18 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
         check(self, True, False)
         check(self, False, True)
         check(self, False, False)
-    
-    def test_dhcp_client_default_use_domains(self):        
-        def check(self, ipv4, ipv6):
+
+    def test_dhcp_client_default_use_domains(self):
+        def check(self, common, ipv4, ipv6):
             mkdir_p(networkd_conf_dropin_dir)
             with open(os.path.join(networkd_conf_dropin_dir, 'default_use_domains.conf'), mode='w', encoding='utf-8') as f:
+                f.write('[Network]\nUseDomains=')
+                f.write('yes\n' if common else 'no\n')
                 f.write('[DHCPv4]\nUseDomains=')
                 f.write('yes\n' if ipv4 else 'no\n')
                 f.write('[DHCPv6]\nUseDomains=')
                 f.write('yes\n' if ipv6 else 'no\n')
-            
+
             restart_networkd()
             self.wait_online('veth-peer:carrier')
             start_dnsmasq('--dhcp-option=option:dns-server,192.168.5.1',
@@ -6958,7 +6934,7 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
 
             for _ in range(20):
                 output = resolvectl('domain', 'veth99')
-                if ipv4 or ipv6:
+                if common or ipv4 or ipv6:
                     if 'example.com' in output:
                         break
                 else:
@@ -6967,16 +6943,18 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
                 time.sleep(0.5)
             else:
                 print(output)
+                print(read_link_state_file('veth99'))
                 self.fail('unexpected domain setting in resolved...')
-            
+
             stop_dnsmasq()
             remove_networkd_conf_dropin('default_use_domains.conf')
 
         copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client.network', copy_dropins=False)
-        check(self, True, True)
-        check(self, True, False)
-        check(self, False, True)
-        check(self, False, False)
+        check(self, True, False, False)
+        check(self, False, True, True)
+        check(self, False, True, False)
+        check(self, False, False, True)
+        check(self, False, False, False)
 
     def test_dhcp_client_use_captive_portal(self):
         def check(self, ipv4, ipv6):
index 7af0ed45786bbe4d6c7f4d0f06372ad9c8e9dc74..4c0f1ba3293cf8770284534465e6bd1e0ba34fcc 100755 (executable)
@@ -183,6 +183,26 @@ status="$(portablectl is-attached --extension app1 minimal_0)"
 
 portablectl detach --now --runtime --extension /usr/share/app1.raw /usr/share/minimal_0.raw app1
 
+# Ensure vpick works, including reattaching to a new image
+mkdir -p /tmp/app1.v/
+cp /usr/share/app1.raw /tmp/app1.v/app1_1.0.raw
+cp /tmp/app1_2.raw /tmp/app1.v/app1_2.0.raw
+portablectl "${ARGS[@]}" attach --now --runtime --extension /tmp/app1.v/ /usr/share/minimal_1.raw app1
+
+systemctl is-active app1.service
+status="$(portablectl is-attached --extension app1_2.0.raw minimal_1)"
+[[ "${status}" == "running-runtime" ]]
+
+rm -f /tmp/app1.v/app1_2.0.raw
+portablectl "${ARGS[@]}" reattach --now --runtime --extension /tmp/app1.v/ /usr/share/minimal_1.raw app1
+
+systemctl is-active app1.service
+status="$(portablectl is-attached --extension app1_1.0.raw minimal_1)"
+[[ "${status}" == "running-runtime" ]]
+
+portablectl detach --now --runtime --extension /tmp/app1.v/ /usr/share/minimal_0.raw app1
+rm -f /tmp/app1.v/app1_1.0.raw
+
 # Ensure that the combination of read-only images, state directory and dynamic user works, and that
 # state is retained. Check after detaching, as on slow systems (eg: sanitizers) it might take a while
 # after the service is attached before the file appears.
@@ -304,7 +324,11 @@ grep -q -F "LogExtraFields=PORTABLE_EXTENSION_NAME_AND_VERSION=app" /run/systemd
 grep -q -F "LogExtraFields=PORTABLE_EXTENSION=app1" /run/systemd/system.attached/app1.service.d/20-portable.conf
 grep -q -F "LogExtraFields=PORTABLE_EXTENSION_NAME_AND_VERSION=app_1" /run/systemd/system.attached/app1.service.d/20-portable.conf
 
-portablectl detach --now --runtime --extension /tmp/app0 --extension /tmp/app1 /tmp/rootdir app0 app1
+portablectl detach --clean --now --runtime --extension /tmp/app0 --extension /tmp/app1 /tmp/rootdir app0 app1
+
+# Ensure --clean remove state and other directories belonging to the portable image being detached
+test ! -d /var/lib/app0
+test ! -d /run/app0
 
 # Ensure that mixed mode copies the images and units (client-owned) but symlinks the profile (OS owned)
 portablectl "${ARGS[@]}" attach --copy=mixed --runtime --extension /tmp/app0 --extension /tmp/app1 /tmp/rootdir app0 app1
index f3077f580d0e806f7eee9d7a9c961c0f7e1295cf..8cfa0a485c2c4f368b8405efc6811420751137d4 100755 (executable)
@@ -5,6 +5,8 @@ set -o pipefail
 
 FAKE_ROOTS_DIR="$(mktemp -d --tmpdir="" fake-roots-XXX)"
 
+shopt -s nullglob
+
 # shellcheck disable=SC2317
 at_exit() {
     set +ex
@@ -25,29 +27,82 @@ at_exit() {
 
 trap at_exit EXIT
 
+# Clears the trap command - it needs to be invoked for every test-case subshell
+# so prepending commands with prepend_trap inside the subshell won't preserve
+# the trap commands from outer shell.
+init_trap() {
+    trap - EXIT
+}
+
+prepend_trap() {
+    set +x
+
+    local command=${1}; shift
+    local previous_commands
+
+    previous_commands=$(trap -p EXIT)
+    if [[ -z $previous_commands ]]; then
+        previous_commands=':'
+    else
+        previous_commands=${previous_commands#'trap -- '}
+        previous_commands=${previous_commands%' EXIT'}
+        previous_commands=$(xargs <<<"$previous_commands")
+    fi
+
+    # shellcheck disable=SC2064 # We use double quotes on purpose here.
+    trap "${command}; ${previous_commands}" EXIT
+
+    set -x
+}
+
 prepare_root() {
-    local root=${1:?}
+    local root=${1:-}
     local hierarchy=${2:?}
     local dir
 
-    if [[ -d $root ]]; then
+    if [[ -n $root ]] && [[ -d $root ]]; then
         echo >&2 "Directory $root already exists, possible copy-paste error?"
         exit 1
     fi
 
+    local -a leftovers=( "$root/var/lib/extensions/"* "$root/var/lib/extensions.mutable/"* )
+    if [[ ${#leftovers[@]} -gt 0 ]]; then
+        echo >&2 "Leftovers remained, make sure to clean them up in the test case: ${leftovers[*]}"
+        exit 1
+    fi
+
     for dir in "$hierarchy" "/usr/lib" "/var/lib/extensions/" "/var/lib/extensions.mutable"; do
         mkdir -p "$root$dir"
     done
 
+    if [[ -e $root/usr/lib/os-release ]]; then
+        mv "$root/usr/lib/os-release" "$root/usr/lib/os-release.orig"
+    fi
+
     {
         echo "ID=testtest"
         echo "VERSION=1.2.3"
     } >"$root/usr/lib/os-release"
+
+    prepend_trap "cleanup_os_release ${root@Q}"
+}
+
+cleanup_os_release() {
+    # shellcheck disable=SC2317 # It is not unreachable, used in a trap couple lines above.
+    local root=${1:-}
+
+    # shellcheck disable=SC2317 # It is not unreachable, used in a trap couple lines above.
+    rm -f "$root/usr/lib/os-release"
+    # shellcheck disable=SC2317 # It is not unreachable, used in a trap couple lines above.
+    if [[ -e $root/usr/lib/os-release.orig ]]; then
+        # shellcheck disable=SC2317 # It is not unreachable, used in a trap couple lines above.
+        mv "$root/usr/lib/os-release.orig" "$root/usr/lib/os-release"
+    fi
 }
 
 prepare_extension_image() {
-    local root="${1:?}"
-    local hierarchy="${2:?}"
+    local root=${1:-}
+    local hierarchy=${2:?}
     local ext_dir ext_release name
 
     name="test-extension"
@@ -57,6 +112,8 @@ prepare_extension_image() {
     echo "ID=_any" >"$ext_release"
     mkdir -p "$ext_dir/$hierarchy"
     touch "$ext_dir$hierarchy/preexisting-file-in-extension-image"
+
+    prepend_trap "rm -rf ${ext_dir@Q}"
 }
 
 prepare_extension_mutable_dir() {
@@ -64,38 +121,60 @@ prepare_extension_mutable_dir() {
 
     mkdir -p "$dir"
     touch "$dir/preexisting-file-in-extensions-mutable"
+    prepend_trap "rm -rf ${dir@Q}"
 }
 
 make_read_only() {
-    local root="${1:?}"
-    local hierarchy="${2:?}"
+    local root=${1:-}
+    local hierarchy=${2:?}
 
     mount -o bind,ro "$root$hierarchy" "$root$hierarchy"
+    prepend_trap "umount ${root@Q}${hierarchy@Q}"
 }
 
 prepare_hierarchy() {
-    local root="${1:?}"
-    local hierarchy="${2:?}"
+    local root=${1:-}
+    local hierarchy=${2:?}
+    local file
 
-    touch "$root$hierarchy/preexisting-file-in-hierarchy"
+    file="$root$hierarchy/preexisting-file-in-hierarchy"
+    touch "$file"
+    prepend_trap "rm -f ${file@Q}"
 }
 
 prepare_read_only_hierarchy() {
-    local root="${1:?}"
-    local hierarchy="${2:?}"
+    local root=${1:-}
+    local hierarchy=${2:?}
 
     prepare_hierarchy "$root" "$hierarchy"
     make_read_only "$root" "$hierarchy"
 }
 
+move_existing_hierarchy_aside() {
+    local root=${1:-}
+    local hierarchy=${2:?}
+
+    if [[ -z $root ]] && [[ $hierarchy = /usr ]]; then
+        echo >&2 "Hell no, not moving /usr aside"
+        exit 1
+    fi
+
+    local path=$root$hierarchy
+
+    if [[ -e $path ]]; then
+        mv "$path" "$path.orig"
+        prepend_trap "mv ${path@Q}.orig ${path@Q}"
+    fi
+}
+
 # Extra arguments:
 #   -e: check for a preexisting file in extension
 #   -h: check for a preexisting file in hierarchy
 #   -u: check for a preexisting file in upperdir
 extension_verify() {
-    local root="${1:?}"
-    local hierarchy="${2:?}"
-    local message="${3:?}"
+    local root=${1:-}
+    local hierarchy=${2:?}
+    local message=${3:?}
     shift 3
     # Map each option to a pre-defined file name
     local -A option_files_map=(
@@ -108,12 +187,12 @@ extension_verify() {
         [h]=0
         [u]=0
     )
-    local file full_path option
+    local file full_path opt option
 
     while getopts "ehu" opt; do
         case "$opt" in
             e|h|u)
-                args[$opt]=1
+                args["$opt"]=1
                 ;;
             *)
                 echo >&2 "Unxexpected option: $opt"
@@ -121,20 +200,18 @@ extension_verify() {
         esac
     done
 
-    echo "${args[@]}"
-
     for option in "${!option_files_map[@]}"; do
-        file="${option_files_map[$option]}"
+        file=${option_files_map["$option"]}
         full_path="$root$hierarchy/$file"
 
-        if [[ ${args[$option]} -ne 0 ]]; then
-            if [[ ! -f "$full_path" ]]; then
+        if [[ ${args["$option"]} -ne 0 ]]; then
+            if [[ ! -f $full_path ]]; then
                 ls -la "$root$hierarchy"
                 echo >&2 "Expected file '$file' to exist under $root$hierarchy $message"
                 exit 1
             fi
         else
-            if [[ -f "$full_path" ]]; then
+            if [[ -f $full_path ]]; then
                 ls -la "$root$hierarchy"
                 echo >&2 "Expected file '$file' to not exist under $root$hierarchy $message"
                 exit 1
@@ -146,8 +223,8 @@ extension_verify() {
 extension_verify_after_merge() (
     set +x
 
-    local root="${1:?}"
-    local hierarchy="${2:?}"
+    local root=${1:-}
+    local hierarchy=${2:?}
     shift 2
 
     extension_verify "$root" "$hierarchy" "after merge" "$@"
@@ -156,87 +233,120 @@ extension_verify_after_merge() (
 extension_verify_after_unmerge() (
     set +x
 
-    local root="${1:?}"
-    local hierarchy="${2:?}"
+    local root=${1:-}
+    local hierarchy=${2:?}
     shift 2
 
     extension_verify "$root" "$hierarchy" "after unmerge" "$@"
 )
 
+run_systemd_sysext() {
+    local root=${1:-}
+    shift
+
+    local -a sysext_args
+    sysext_args=()
+
+    if [[ -n $root ]]; then
+        sysext_args+=( "--root=$root" )
+    fi
+    sysext_args+=( "$@" )
+    systemd-sysext "${sysext_args[@]}"
+}
+
 # General systemd-sysext tests
 
+run_sysext_tests() {
+    # The roots_dir variable may be empty - in such case all the tests will run
+    # on /, otherwise they will run on $roots_dir/<SEPARATE_DIR_FOR_TEST>.
+    local roots_dir=${1}; shift
+
+    # Each test runs in a subshell, so we can use traps for cleanups without
+    # clobbering toplevel traps, and we can do skips by invoking "exit 0".
+
+( init_trap
 : "No extension data in /var/lib/extensions.mutable/…, R/O hierarchy, mutability disabled by default, read-only merged"
-fake_root="$FAKE_ROOTS_DIR/simple-read-only-with-read-only-hierarchy"
-hierarchy=/usr
+fake_root=${roots_dir:+"$roots_dir/simple-read-only-with-read-only-hierarchy"}
+hierarchy=/opt
 
 prepare_root "$fake_root" "$hierarchy"
 prepare_extension_image "$fake_root" "$hierarchy"
 prepare_read_only_hierarchy "$fake_root" "$hierarchy"
 (! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
 
-SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" merge
+run_systemd_sysext "$fake_root" merge
 (! touch "$fake_root$hierarchy/should-still-fail-on-read-only-fs")
 extension_verify_after_merge "$fake_root" "$hierarchy" -e -h
 
-SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" unmerge
+run_systemd_sysext "$fake_root" unmerge
 extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
 (! touch "$fake_root$hierarchy/should-still-fail-on-read-only-fs")
+)
 
 
+( init_trap
 : "No extension data in /var/lib/extensions.mutable/…, mutable hierarchy, mutability disabled by default, read-only merged"
-fake_root="$FAKE_ROOTS_DIR/simple-read-only-with-mutable-hierarchy"
-hierarchy=/usr
+fake_root=${roots_dir:+"$roots_dir/simple-read-only-with-mutable-hierarchy"}
+hierarchy=/opt
 
 prepare_root "$fake_root" "$hierarchy"
 prepare_extension_image "$fake_root" "$hierarchy"
 prepare_hierarchy "$fake_root" "$hierarchy"
 touch "$fake_root$hierarchy/should-succeed-on-mutable-fs"
 
-SYSTEMD_SYSEXT_HIERARCHIEe="$hierarchy" systemd-sysext --root="$fake_root" merge
+run_systemd_sysext "$fake_root" merge
 (! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
 extension_verify_after_merge "$fake_root" "$hierarchy" -e -h
 
-SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" unmerge
+run_systemd_sysext "$fake_root" unmerge
 extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
 touch "$fake_root$hierarchy/should-succeed-on-mutable-fs-again"
+)
 
 
+( init_trap
 : "No extension data in /var/lib/extensions.mutable/…, no hierarchy either, mutability disabled by default, read-only merged"
-fake_root="$FAKE_ROOTS_DIR/simple-read-only-with-missing-hierarchy"
+fake_root=${roots_dir:+"$roots_dir/simple-read-only-with-missing-hierarchy"}
 hierarchy=/opt
 
+move_existing_hierarchy_aside "$fake_root" "$hierarchy"
 prepare_root "$fake_root" "$hierarchy"
 rmdir "$fake_root/$hierarchy"
 prepare_extension_image "$fake_root" "$hierarchy"
 
-SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" merge
+run_systemd_sysext "$fake_root" merge
 (! touch "$fake_root$hierarchy/should-still-fail-on-read-only-fs")
 extension_verify_after_merge "$fake_root" "$hierarchy" -e
 
-SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" unmerge
+run_systemd_sysext "$fake_root" unmerge
 extension_verify_after_unmerge "$fake_root" "$hierarchy"
+)
 
 
+( init_trap
 : "No extension data in /var/lib/extensions.mutable/…, empty hierarchy, mutability disabled by default, read-only merged"
-fake_root="$FAKE_ROOTS_DIR/simple-read-only-with-empty-hierarchy"
+fake_root=${roots_dir:+"$roots_dir/simple-read-only-with-empty-hierarchy"}
 hierarchy=/opt
 
+move_existing_hierarchy_aside "$fake_root" "$hierarchy"
 prepare_root "$fake_root" "$hierarchy"
 prepare_extension_image "$fake_root" "$hierarchy"
 make_read_only "$fake_root" "$hierarchy"
 (! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
 
-SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" merge
+run_systemd_sysext "$fake_root" merge
 (! touch "$fake_root$hierarchy/should-still-fail-on-read-only-fs")
 extension_verify_after_merge "$fake_root" "$hierarchy" -e
 
-SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" unmerge
+run_systemd_sysext "$fake_root" unmerge
 extension_verify_after_unmerge "$fake_root" "$hierarchy"
+)
 
 
+( init_trap
 : "Extension data in /var/lib/extensions.mutable/…, R/O hierarchy, mutability disabled by default, read-only merged"
-fake_root="$FAKE_ROOTS_DIR/simple-mutable-with-read-only-hierarchy-disabled"
-hierarchy=/usr
+fake_root=${roots_dir:+"$roots_dir/simple-mutable-with-read-only-hierarchy-disabled"}
+hierarchy=/opt
 extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
 
 prepare_root "$fake_root" "$hierarchy"
@@ -245,16 +355,19 @@ prepare_extension_mutable_dir "$extension_data_dir"
 prepare_read_only_hierarchy "$fake_root" "$hierarchy"
 (! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
 
-SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" merge
+run_systemd_sysext "$fake_root" merge
 (! touch "$fake_root$hierarchy/should-be-read-only")
 extension_verify_after_merge "$fake_root" "$hierarchy" -e -h
 
-SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" unmerge
+run_systemd_sysext "$fake_root" unmerge
 extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
+)
+
 
+( init_trap
 : "Extension data in /var/lib/extensions.mutable/…, R/O hierarchy, auto-mutability, mutable merged"
-fake_root="$FAKE_ROOTS_DIR/simple-mutable-with-read-only-hierarchy"
-hierarchy=/usr
+fake_root=${roots_dir:+"$roots_dir/simple-mutable-with-read-only-hierarchy"}
+hierarchy=/opt
 extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
 
 prepare_root "$fake_root" "$hierarchy"
@@ -263,63 +376,71 @@ prepare_extension_mutable_dir "$extension_data_dir"
 prepare_read_only_hierarchy "$fake_root" "$hierarchy"
 (! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
 
-SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" --mutable=auto merge
+run_systemd_sysext "$fake_root" --mutable=auto merge
 touch "$fake_root$hierarchy/now-is-mutable"
 extension_verify_after_merge "$fake_root" "$hierarchy" -e -h -u
 test -f "$extension_data_dir/now-is-mutable"
 
-SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" unmerge
+run_systemd_sysext "$fake_root" unmerge
 extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
 test -f "$extension_data_dir/now-is-mutable"
 test ! -f "$fake_root$hierarchy/now-is-mutable"
+)
 
 
+( init_trap
 : "Extension data in /var/lib/extensions.mutable/…, missing hierarchy, auto-mutability, mutable merged"
-fake_root="$FAKE_ROOTS_DIR/simple-mutable-with-missing-hierarchy"
+fake_root=${roots_dir:+"$roots_dir/simple-mutable-with-missing-hierarchy"}
 hierarchy=/opt
 extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
 
+move_existing_hierarchy_aside "$fake_root" "$hierarchy"
 prepare_root "$fake_root" "$hierarchy"
 rmdir "$fake_root/$hierarchy"
 prepare_extension_image "$fake_root" "$hierarchy"
 prepare_extension_mutable_dir "$extension_data_dir"
 
-SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" --mutable=auto merge
+run_systemd_sysext "$fake_root" --mutable=auto merge
 touch "$fake_root$hierarchy/now-is-mutable"
 extension_verify_after_merge "$fake_root" "$hierarchy" -e -u
 test -f "$extension_data_dir/now-is-mutable"
 
-SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" unmerge
+run_systemd_sysext "$fake_root" unmerge
 extension_verify_after_unmerge "$fake_root" "$hierarchy"
 test -f "$extension_data_dir/now-is-mutable"
 test ! -f "$fake_root$hierarchy/now-is-mutable"
+)
 
 
+( init_trap
 : "Extension data in /var/lib/extensions.mutable/…, empty hierarchy, auto-mutability, mutable merged"
-fake_root="$FAKE_ROOTS_DIR/simple-mutable-with-empty-hierarchy"
+fake_root=${roots_dir:+"$roots_dir/simple-mutable-with-empty-hierarchy"}
 hierarchy=/opt
 extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
 
+move_existing_hierarchy_aside "$fake_root" "$hierarchy"
 prepare_root "$fake_root" "$hierarchy"
 prepare_extension_image "$fake_root" "$hierarchy"
 prepare_extension_mutable_dir "$extension_data_dir"
 make_read_only "$fake_root" "$hierarchy"
 (! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
 
-SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" --mutable=auto merge
+run_systemd_sysext "$fake_root" --mutable=auto merge
 touch "$fake_root$hierarchy/now-is-mutable"
 extension_verify_after_merge "$fake_root" "$hierarchy" -e -u
 test -f "$extension_data_dir/now-is-mutable"
 
-SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" unmerge
+run_systemd_sysext "$fake_root" unmerge
 extension_verify_after_unmerge "$fake_root" "$hierarchy"
 test -f "$extension_data_dir/now-is-mutable"
 test ! -f "$fake_root$hierarchy/now-is-mutable"
+)
 
 
+( init_trap
 : "/var/lib/extensions.mutable/… is a symlink to other dir, R/O hierarchy, auto-mutability, mutable merged"
-fake_root="$FAKE_ROOTS_DIR/mutable-symlink-with-read-only-hierarchy"
-hierarchy=/usr
+fake_root=${roots_dir:+"$roots_dir/mutable-symlink-with-read-only-hierarchy"}
+hierarchy=/opt
 extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
 extension_real_dir="$fake_root/upperdir"
 
@@ -327,25 +448,28 @@ prepare_root "$fake_root" "$hierarchy"
 prepare_extension_image "$fake_root" "$hierarchy"
 prepare_extension_mutable_dir "$extension_real_dir"
 ln -sfTr "$extension_real_dir" "$extension_data_dir"
+prepend_trap "rm -f ${extension_data_dir@Q}"
 prepare_read_only_hierarchy "$fake_root" "$hierarchy"
 (! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
 
-SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" --mutable=auto merge
+run_systemd_sysext "$fake_root" --mutable=auto merge
 touch "$fake_root$hierarchy/now-is-mutable"
 extension_verify_after_merge "$fake_root" "$hierarchy" -e -h -u
 test -f "$extension_data_dir/now-is-mutable"
 test -f "$extension_real_dir/now-is-mutable"
 
-SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" unmerge
+run_systemd_sysext "$fake_root" unmerge
 extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
 test -f "$extension_data_dir/now-is-mutable"
 test -f "$extension_real_dir/now-is-mutable"
 test ! -f "$fake_root$hierarchy/now-is-mutable"
+)
 
 
+( init_trap
 : "/var/lib/extensions.mutable/… is a symlink to the hierarchy itself, mutable hierarchy, auto-mutability, mutable merged"
-fake_root="$FAKE_ROOTS_DIR/mutable-self-upper"
-hierarchy=/usr
+fake_root=${roots_dir:+"$roots_dir/mutable-self-upper"}
+hierarchy=/opt
 extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
 extension_real_dir="$fake_root$hierarchy"
 
@@ -353,23 +477,26 @@ prepare_root "$fake_root" "$hierarchy"
 prepare_extension_image "$fake_root" "$hierarchy"
 prepare_extension_mutable_dir "$extension_real_dir"
 ln -sfTr "$extension_real_dir" "$extension_data_dir"
+prepend_trap "rm -f ${extension_data_dir@Q}"
 touch "$fake_root$hierarchy/preexisting-file-in-hierarchy"
 
-SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" --mutable=auto merge
+run_systemd_sysext "$fake_root" --mutable=auto merge
 touch "$fake_root$hierarchy/now-is-mutable"
 extension_verify_after_merge "$fake_root" "$hierarchy" -e -h -u
 test -f "$extension_data_dir/now-is-mutable"
 test -f "$extension_real_dir/now-is-mutable"
 
-SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" unmerge
+run_systemd_sysext "$fake_root" unmerge
 extension_verify_after_unmerge "$fake_root" "$hierarchy" -h -u
 test -f "$extension_data_dir/now-is-mutable"
 test -f "$extension_real_dir/now-is-mutable"
+)
 
 
+( init_trap
 : "/var/lib/extensions.mutable/… is a symlink to the hierarchy itself, R/O hierarchy, auto-mutability, expected fail"
-fake_root="$FAKE_ROOTS_DIR/failure-self-upper-ro"
-hierarchy=/usr
+fake_root=${roots_dir:+"$roots_dir/failure-self-upper-ro"}
+hierarchy=/opt
 extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
 extension_real_dir="$fake_root$hierarchy"
 
@@ -377,34 +504,39 @@ prepare_root "$fake_root" "$hierarchy"
 prepare_extension_image "$fake_root" "$hierarchy"
 prepare_extension_mutable_dir "$extension_real_dir"
 ln -sfTr "$extension_real_dir" "$extension_data_dir"
+prepend_trap "rm -f ${extension_data_dir@Q}"
 prepare_read_only_hierarchy "$fake_root" "$hierarchy"
 
-(! SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" --mutable=auto merge)
+(! run_systemd_sysext "$fake_root" --mutable=auto merge)
+)
 
 
+( init_trap
 : "/var/lib/extensions.mutable/… is a dangling symlink, auto-mutability, read-only merged"
-fake_root="$FAKE_ROOTS_DIR/read-only-mutable-dangling-symlink"
-hierarchy=/usr
+fake_root=${roots_dir:+"$roots_dir/read-only-mutable-dangling-symlink"}
+hierarchy=/opt
 extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
 
 prepare_root "$fake_root" "$hierarchy"
 prepare_extension_image "$fake_root" "$hierarchy"
 ln -sfTr "/should/not/exist/" "$extension_data_dir"
+prepend_trap "rm -f ${extension_data_dir@Q}"
 prepare_read_only_hierarchy "$fake_root" "$hierarchy"
 (! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
 
-# run systemd-sysext
-SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" --mutable=auto merge
+run_systemd_sysext "$fake_root" --mutable=auto merge
 (! touch "$fake_root$hierarchy/should-still-fail-on-read-only-fs")
 extension_verify_after_merge "$fake_root" "$hierarchy" -e -h
 
-SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" unmerge
+run_systemd_sysext "$fake_root" unmerge
 extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
+)
 
 
+( init_trap
 : "/var/lib/extensions.mutable/… exists but ignored, mutability disabled explicitly, read-only merged"
-fake_root="$FAKE_ROOTS_DIR/disabled"
-hierarchy=/usr
+fake_root=${roots_dir:+"$roots_dir/disabled"}
+hierarchy=/opt
 extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
 
 prepare_root "$fake_root" "$hierarchy"
@@ -413,18 +545,19 @@ prepare_extension_mutable_dir "$extension_data_dir"
 prepare_read_only_hierarchy "$fake_root" "$hierarchy"
 (! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
 
-# run systemd-sysext
-SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" --mutable=no merge
+run_systemd_sysext "$fake_root" --mutable=no merge
 (! touch "$fake_root$hierarchy/should-still-fail-on-read-only-fs")
 extension_verify_after_merge "$fake_root" "$hierarchy" -e -h
 
-SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" unmerge
+run_systemd_sysext "$fake_root" unmerge
 extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
+)
 
 
+( init_trap
 : "/var/lib/extensions.mutable/… exists but is imported instead, read-only merged"
-fake_root="$FAKE_ROOTS_DIR/imported"
-hierarchy=/usr
+fake_root=${roots_dir:+"$roots_dir/imported"}
+hierarchy=/opt
 extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
 
 prepare_root "$fake_root" "$hierarchy"
@@ -433,19 +566,21 @@ prepare_extension_mutable_dir "$extension_data_dir"
 prepare_read_only_hierarchy "$fake_root" "$hierarchy"
 (! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
 
-# run systemd-sysext
-SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" --mutable=import merge
+run_systemd_sysext "$fake_root" --mutable=import merge
 (! touch "$fake_root$hierarchy/should-still-fail-on-read-only-fs")
 extension_verify_after_merge "$fake_root" "$hierarchy" -e -h -u
 
-SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" unmerge
+run_systemd_sysext "$fake_root" unmerge
 extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
+)
 
 
-: "/var/lib/extensions.mutable/… doesn't exists, mutability enabled, mutable merged"
-fake_root="$FAKE_ROOTS_DIR/enabled"
-hierarchy=/usr
+( init_trap
+: "/var/lib/extensions.mutable/… does not exist, mutability enabled, mutable merged"
+fake_root=${roots_dir:+"$roots_dir/enabled"}
+hierarchy=/opt
 extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
+extension_data_dir_usr="$fake_root/var/lib/extensions.mutable/usr"
 
 prepare_root "$fake_root" "$hierarchy"
 prepare_extension_image "$fake_root" "$hierarchy"
@@ -453,39 +588,52 @@ prepare_read_only_hierarchy "$fake_root" "$hierarchy"
 (! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
 test ! -d "$extension_data_dir"
 
-SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" --mutable=yes merge
+run_systemd_sysext "$fake_root" --mutable=yes merge
+# systemd-sysext with --mutable=yes creates extensions.mutable directory for
+# the hierarchy, so delete it after the test
+prepend_trap "rm -rf ${extension_data_dir@Q}"
+# systemd-sysext with --mutable=yes creates extensions.mutable directory also
+# for the /usr hierarchy, because the image needs to have
+# /usr/lib/extension-release.d/extension-release.<NAME> file - this causes the
+# /usr hierarchy to also become mutable
+prepend_trap "rm -rf ${extension_data_dir_usr@Q}"
 test -d "$extension_data_dir"
 touch "$fake_root$hierarchy/now-is-mutable"
 extension_verify_after_merge "$fake_root" "$hierarchy" -e -h
 test -f "$extension_data_dir/now-is-mutable"
 
-SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" unmerge
+run_systemd_sysext "$fake_root" unmerge
 extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
 test -f "$extension_data_dir/now-is-mutable"
 test ! -f "$fake_root$hierarchy/now-is-mutable"
+)
 
 
-: "/var/lib/extensions.mutable/… doesn't exists, auto-mutability, read-only merged"
-fake_root="$FAKE_ROOTS_DIR/simple-read-only-explicit"
-hierarchy=/usr
+( init_trap
+: "/var/lib/extensions.mutable/… does not exist, auto-mutability, read-only merged"
+fake_root=${roots_dir:+"$roots_dir/simple-read-only-explicit"}
+hierarchy=/opt
 
 prepare_root "$fake_root" "$hierarchy"
 prepare_extension_image "$fake_root" "$hierarchy"
 prepare_read_only_hierarchy "$fake_root" "$hierarchy"
 (! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
 
-SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" --mutable=auto merge
+run_systemd_sysext "$fake_root" --mutable=auto merge
 (! touch "$fake_root$hierarchy/should-still-fail-on-read-only-fs")
 extension_verify_after_merge "$fake_root" "$hierarchy" -e -h
 
-SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" unmerge
+run_systemd_sysext "$fake_root" unmerge
 extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
+)
 
 
-: "/var/lib/extensions.mutable/… doesn't exists, mutability enabled through env var, mutable merged"
-fake_root="$FAKE_ROOTS_DIR/enabled-env-var"
-hierarchy=/usr
+( init_trap
+: "/var/lib/extensions.mutable/… does not exist, mutability enabled through env var, mutable merged"
+fake_root=${roots_dir:+"$roots_dir/enabled-env-var"}
+hierarchy=/opt
 extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
+extension_data_dir_usr="$fake_root/var/lib/extensions.mutable/usr"
 
 prepare_root "$fake_root" "$hierarchy"
 prepare_extension_image "$fake_root" "$hierarchy"
@@ -493,38 +641,50 @@ prepare_read_only_hierarchy "$fake_root" "$hierarchy"
 (! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
 test ! -d "$extension_data_dir"
 
-SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" SYSTEMD_SYSEXT_MUTABLE_MODE=yes systemd-sysext --root="$fake_root" merge
+SYSTEMD_SYSEXT_MUTABLE_MODE=yes run_systemd_sysext "$fake_root" merge
+# systemd-sysext with --mutable=yes creates extensions.mutable directory for
+# the hierarchy, so delete it after the test
+prepend_trap "rm -rf ${extension_data_dir@Q}"
+# systemd-sysext with --mutable=yes creates extensions.mutable directory also
+# for the /usr hierarchy, because the image needs to have
+# /usr/lib/extension-release.d/extension-release.<NAME> file - this causes the
+# /usr hierarchy to also become mutable
+prepend_trap "rm -rf ${extension_data_dir_usr@Q}"
 test -d "$extension_data_dir"
 touch "$fake_root$hierarchy/now-is-mutable"
 extension_verify_after_merge "$fake_root" "$hierarchy" -e -h
 test -f "$extension_data_dir/now-is-mutable"
 
-SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" SYSTEMD_SYSEXT_MUTABLE_MODE=yes systemd-sysext --root="$fake_root" unmerge
+SYSTEMD_SYSEXT_MUTABLE_MODE=yes run_systemd_sysext "$fake_root" unmerge
 extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
 test -f "$extension_data_dir/now-is-mutable"
 test ! -f "$fake_root$hierarchy/now-is-mutable"
+)
 
 
-: "/var/lib/extensions.mutable/… doesn't exists, auto-mutability enabled through env var, read-only merged"
-fake_root="$FAKE_ROOTS_DIR/read-only-auto-env-var"
-hierarchy=/usr
+( init_trap
+: "/var/lib/extensions.mutable/… does not exist, auto-mutability enabled through env var, read-only merged"
+fake_root=${roots_dir:+"$roots_dir/read-only-auto-env-var"}
+hierarchy=/opt
 
 prepare_root "$fake_root" "$hierarchy"
 prepare_extension_image "$fake_root" "$hierarchy"
 prepare_read_only_hierarchy "$fake_root" "$hierarchy"
 (! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
 
-SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" SYSTEMD_SYSEXT_MUTABLE_MODE=auto systemd-sysext --root="$fake_root" --mutable=auto merge
+SYSTEMD_SYSEXT_MUTABLE_MODE=auto run_systemd_sysext "$fake_root" --mutable=auto merge
 (! touch "$fake_root$hierarchy/should-still-fail-on-read-only-fs")
 extension_verify_after_merge "$fake_root" "$hierarchy" -e -h
 
-SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" SYSTEMD_SYSEXT_MUTABLE_MODE=auto systemd-sysext --root="$fake_root" unmerge
+SYSTEMD_SYSEXT_MUTABLE_MODE=auto run_systemd_sysext "$fake_root" unmerge
 extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
+)
 
 
+( init_trap
 : "Extension data in /var/lib/extensions.mutable/…, R/O hierarchy, auto-mutability enabled through env var, mutable merged"
-fake_root="$FAKE_ROOTS_DIR/auto-mutable-env-var"
-hierarchy=/usr
+fake_root=${roots_dir:+"$roots_dir/auto-mutable-env-var"}
+hierarchy=/opt
 extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
 
 prepare_root "$fake_root" "$hierarchy"
@@ -533,20 +693,22 @@ prepare_extension_mutable_dir "$extension_data_dir"
 prepare_read_only_hierarchy "$fake_root" "$hierarchy"
 (! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
 
-SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" SYSTEMD_SYSEXT_MUTABLE_MODE=auto systemd-sysext --root="$fake_root" merge
+SYSTEMD_SYSEXT_MUTABLE_MODE=auto run_systemd_sysext "$fake_root" merge
 touch "$fake_root$hierarchy/now-is-mutable"
 extension_verify_after_merge "$fake_root" "$hierarchy" -e -h -u
 test -f "$extension_data_dir/now-is-mutable"
 
-SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" SYSTEMD_SYSEXT_MUTABLE_MODE=auto systemd-sysext --root="$fake_root" unmerge
+SYSTEMD_SYSEXT_MUTABLE_MODE=auto run_systemd_sysext "$fake_root" unmerge
 extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
 test -f "$extension_data_dir/now-is-mutable"
 test ! -f "$fake_root$hierarchy/now-is-mutable"
+)
 
 
+( init_trap
 : "Extension data in /var/lib/extensions.mutable/…, R/O hierarchy, mutability disabled through env var, read-only merged"
-fake_root="$FAKE_ROOTS_DIR/env-var-disabled"
-hierarchy=/usr
+fake_root=${roots_dir:+"$roots_dir/env-var-disabled"}
+hierarchy=/opt
 extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
 
 prepare_root "$fake_root" "$hierarchy"
@@ -555,17 +717,19 @@ prepare_extension_mutable_dir "$extension_data_dir"
 prepare_read_only_hierarchy "$fake_root" "$hierarchy"
 (! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
 
-SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" SYSTEMD_SYSEXT_MUTABLE_MODE=no systemd-sysext --root="$fake_root" merge
+SYSTEMD_SYSEXT_MUTABLE_MODE=no run_systemd_sysext "$fake_root" merge
 (! touch "$fake_root$hierarchy/should-be-read-only")
 extension_verify_after_merge "$fake_root" "$hierarchy" -e -h
 
-SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" SYSTEMD_SYSEXT_MUTABLE_MODE=no systemd-sysext --root="$fake_root" unmerge
+SYSTEMD_SYSEXT_MUTABLE_MODE=no run_systemd_sysext "$fake_root" unmerge
 extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
+)
 
 
+( init_trap
 : "/var/lib/extensions.mutable/… exists but is imported through env var, read-only merged"
-fake_root="$FAKE_ROOTS_DIR/imported-env-var"
-hierarchy=/usr
+fake_root=${roots_dir:+"$roots_dir/imported-env-var"}
+hierarchy=/opt
 extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
 
 prepare_root "$fake_root" "$hierarchy"
@@ -574,17 +738,19 @@ prepare_extension_mutable_dir "$extension_data_dir"
 prepare_read_only_hierarchy "$fake_root" "$hierarchy"
 (! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
 
-SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" SYSTEMD_SYSEXT_MUTABLE_MODE=import systemd-sysext --root="$fake_root" merge
+SYSTEMD_SYSEXT_MUTABLE_MODE=import run_systemd_sysext "$fake_root" merge
 (! touch "$fake_root$hierarchy/should-still-fail-on-read-only-fs")
 extension_verify_after_merge "$fake_root" "$hierarchy" -e -h -u
 
-SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" SYSTEMD_SYSEXT_MUTABLE_MODE=import systemd-sysext --root="$fake_root" unmerge
+SYSTEMD_SYSEXT_MUTABLE_MODE=import run_systemd_sysext "$fake_root" unmerge
 extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
+)
 
 
+( init_trap
 : "Extension data in /var/lib/extensions.mutable/…, R/O hierarchy, mutability enabled through env var but overridden via CLI option, read-only merged"
-fake_root="$FAKE_ROOTS_DIR/env-var-overridden"
-hierarchy=/usr
+fake_root=${roots_dir:+"$roots_dir/env-var-overridden"}
+hierarchy=/opt
 extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
 
 prepare_root "$fake_root" "$hierarchy"
@@ -593,17 +759,19 @@ prepare_extension_mutable_dir "$extension_data_dir"
 prepare_read_only_hierarchy "$fake_root" "$hierarchy"
 (! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
 
-SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" SYSTEMD_SYSEXT_MUTABLE_MODE=yes systemd-sysext --root="$fake_root" --mutable=no merge
+SYSTEMD_SYSEXT_MUTABLE_MODE=yes run_systemd_sysext "$fake_root" --mutable=no merge
 (! touch "$fake_root$hierarchy/should-be-read-only")
 extension_verify_after_merge "$fake_root" "$hierarchy" -e -h
 
-SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" SYSTEMD_SYSEXT_MUTABLE_MODE=yes systemd-sysext --root="$fake_root" unmerge
+SYSTEMD_SYSEXT_MUTABLE_MODE=yes run_systemd_sysext "$fake_root" unmerge
 extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
+)
 
 
+( init_trap
 : "Extension data in /var/lib/extensions.mutable/…, R/O hierarchy, ephemeral mutability, mutable merged"
-fake_root="$FAKE_ROOTS_DIR/ephemeral"
-hierarchy=/usr
+fake_root=${roots_dir:+"$roots_dir/ephemeral"}
+hierarchy=/opt
 extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
 
 prepare_root "$fake_root" "$hierarchy"
@@ -612,20 +780,22 @@ prepare_extension_mutable_dir "$extension_data_dir"
 prepare_read_only_hierarchy "$fake_root" "$hierarchy"
 (! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
 
-SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" --mutable=ephemeral merge
+run_systemd_sysext "$fake_root" --mutable=ephemeral merge
 touch "$fake_root$hierarchy/now-is-mutable"
 extension_verify_after_merge "$fake_root" "$hierarchy" -e -h
 test ! -f "$extension_data_dir/now-is-mutable"
 
-SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" unmerge
+run_systemd_sysext "$fake_root" unmerge
 extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
 test ! -f "$extension_data_dir/now-is-mutable"
 test ! -f "$fake_root$hierarchy/now-is-mutable"
+)
 
 
+( init_trap
 : "Extension data in /var/lib/extensions.mutable/…, R/O hierarchy, ephemeral mutability through env var, mutable merged"
-fake_root="$FAKE_ROOTS_DIR/ephemeral-env-var"
-hierarchy=/usr
+fake_root=${roots_dir:+"$roots_dir/ephemeral-env-var"}
+hierarchy=/opt
 extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
 
 prepare_root "$fake_root" "$hierarchy"
@@ -634,20 +804,22 @@ prepare_extension_mutable_dir "$extension_data_dir"
 prepare_read_only_hierarchy "$fake_root" "$hierarchy"
 (! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
 
-SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" SYSTEMD_SYSEXT_MUTABLE_MODE=ephemeral systemd-sysext --root="$fake_root" merge
+SYSTEMD_SYSEXT_MUTABLE_MODE=ephemeral run_systemd_sysext "$fake_root" merge
 touch "$fake_root$hierarchy/now-is-mutable"
 extension_verify_after_merge "$fake_root" "$hierarchy" -e -h
 test ! -f "$extension_data_dir/now-is-mutable"
 
-SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" SYSTEMD_SYSEXT_MUTABLE_MODE=ephemeral systemd-sysext --root="$fake_root" unmerge
+SYSTEMD_SYSEXT_MUTABLE_MODE=ephemeral run_systemd_sysext "$fake_root" unmerge
 extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
 test ! -f "$extension_data_dir/now-is-mutable"
 test ! -f "$fake_root$hierarchy/now-is-mutable"
+)
 
 
+( init_trap
 : "Extension data in /var/lib/extensions.mutable/…, R/O hierarchy, ephemeral import mutability, mutable merged"
-fake_root="$FAKE_ROOTS_DIR/ephemeral-import"
-hierarchy=/usr
+fake_root=${roots_dir:+"$roots_dir/ephemeral-import"}
+hierarchy=/opt
 extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
 
 prepare_root "$fake_root" "$hierarchy"
@@ -656,21 +828,22 @@ prepare_extension_mutable_dir "$extension_data_dir"
 prepare_read_only_hierarchy "$fake_root" "$hierarchy"
 (! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
 
-# run systemd-sysext
-SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" --mutable=ephemeral-import merge
+run_systemd_sysext "$fake_root" --mutable=ephemeral-import merge
 touch "$fake_root$hierarchy/now-is-mutable"
 extension_verify_after_merge "$fake_root" "$hierarchy" -e -h -u
 test ! -f "$extension_data_dir/now-is-mutable"
 
-SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" unmerge
+run_systemd_sysext "$fake_root" unmerge
 extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
 test ! -f "$extension_data_dir/now-is-mutable"
 test ! -f "$fake_root$hierarchy/now-is-mutable"
+)
 
 
+( init_trap
 : "Extension data in /var/lib/extensions.mutable/…, R/O hierarchy, ephemeral import mutability through env var, mutable merged"
-fake_root="$FAKE_ROOTS_DIR/ephemeral-import-env-var"
-hierarchy=/usr
+fake_root=${roots_dir:+"$roots_dir/ephemeral-import-env-var"}
+hierarchy=/opt
 extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
 
 prepare_root "$fake_root" "$hierarchy"
@@ -679,20 +852,22 @@ prepare_extension_mutable_dir "$extension_data_dir"
 prepare_read_only_hierarchy "$fake_root" "$hierarchy"
 (! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
 
-SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" SYSTEMD_SYSEXT_MUTABLE_MODE=ephemeral-import systemd-sysext --root="$fake_root" merge
+SYSTEMD_SYSEXT_MUTABLE_MODE=ephemeral-import run_systemd_sysext "$fake_root" merge
 touch "$fake_root$hierarchy/now-is-mutable"
 extension_verify_after_merge "$fake_root" "$hierarchy" -e -h -u
 test ! -f "$extension_data_dir/now-is-mutable"
 
-SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" SYSTEMD_SYSEXT_MUTABLE_MODE=ephemeral-import systemd-sysext --root="$fake_root" unmerge
+SYSTEMD_SYSEXT_MUTABLE_MODE=ephemeral-import run_systemd_sysext "$fake_root" unmerge
 extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
 test ! -f "$extension_data_dir/now-is-mutable"
 test ! -f "$fake_root$hierarchy/now-is-mutable"
+)
 
 
+( init_trap
 : "Extension data pointing to mutable hierarchy, ephemeral import mutability, expected fail"
-fake_root="$FAKE_ROOTS_DIR/ephemeral-import-self"
-hierarchy=/usr
+fake_root=${roots_dir:+"$roots_dir/ephemeral-import-self"}
+hierarchy=/opt
 extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
 extension_real_dir="$fake_root$hierarchy"
 
@@ -700,15 +875,18 @@ prepare_root "$fake_root" "$hierarchy"
 prepare_extension_image "$fake_root" "$hierarchy"
 prepare_extension_mutable_dir "$extension_real_dir"
 ln -sfTr "$extension_real_dir" "$extension_data_dir"
+prepend_trap "rm -f ${extension_data_dir@Q}"
 prepare_hierarchy "$fake_root" "$hierarchy"
 touch "$fake_root$hierarchy/should-succeed-on-read-only-fs"
 
-(! SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" --mutable=ephemeral-import merge)
+(! run_systemd_sysext "$fake_root" --mutable=ephemeral-import merge)
+)
 
 
-: "Extension data pointing to mutable hierarchy,  import mutability, expected fail"
-fake_root="$FAKE_ROOTS_DIR/import-self"
-hierarchy=/usr
+( init_trap
+: "Extension data pointing to mutable hierarchy, import mutability, expected fail"
+fake_root=${roots_dir:+"$roots_dir/import-self"}
+hierarchy=/opt
 extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
 extension_real_dir="$fake_root$hierarchy"
 
@@ -716,9 +894,88 @@ prepare_root "$fake_root" "$hierarchy"
 prepare_extension_image "$fake_root" "$hierarchy"
 prepare_extension_mutable_dir "$extension_real_dir"
 ln -sfTr "$extension_real_dir" "$extension_data_dir"
+prepend_trap "rm -f ${extension_data_dir@Q}"
 prepare_hierarchy "$fake_root" "$hierarchy"
 touch "$fake_root$hierarchy/should-succeed-on-read-only-fs"
 
-(! SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" --mutable=import merge)
+(! run_systemd_sysext "$fake_root" --mutable=import merge)
+)
+
+
+for mutable_mode in no yes ephemeral; do
+    ( init_trap
+    : "Check if merging the hierarchy does not change its permissions, checking with --mutable=${mutable_mode}"
+
+    fake_root=${roots_dir:+"$roots_dir/perm-checks-mutable-$mutable_mode"}
+    hierarchy=/opt
+    extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
+
+    prepare_root "$fake_root" "$hierarchy"
+    prepare_extension_image "$fake_root" "$hierarchy"
+    prepare_extension_mutable_dir "$extension_data_dir"
+    prepare_read_only_hierarchy "${fake_root}" "${hierarchy}"
+
+    full_path="$fake_root$hierarchy"
+    permissions_before_merge=$(stat --format=%A "$full_path")
+
+    run_systemd_sysext "$fake_root" "--mutable=$mutable_mode" merge
+    if [[ $mutable_mode = yes ]]; then
+        # systemd-sysext with --mutable=yes creates extensions.mutable
+        # directory also for the /usr hierarchy, because the image needs to
+        # have /usr/lib/extension-release.d/extension-release.<NAME> file -
+        # this causes the /usr hierarchy to also become mutable
+        extension_data_dir_usr="$fake_root/var/lib/extensions.mutable/usr"
+        prepend_trap "rm -rf ${extension_data_dir_usr@Q}"
+    fi
+
+    permissions_after_merge=$(stat --format=%A "$full_path")
+
+    run_systemd_sysext "$fake_root" unmerge
+
+    permissions_after_unmerge=$(stat --format=%A "$full_path")
+
+    if [[ "$permissions_before_merge" != "$permissions_after_merge" ]]; then
+        echo >&2 "Broken hierarchy permissions after merging with mutable mode ${mutable_mode@Q}, expected ${permissions_before_merge@Q}, got ${permissions_after_merge@Q}"
+        exit 1
+    fi
+
+    if [[ "$permissions_before_merge" != "$permissions_after_unmerge" ]]; then
+        echo >&2 "Broken hierarchy permissions after unmerging with mutable mode ${mutable_mode@Q}, expected ${permissions_before_merge@Q}, got ${permissions_after_unmerge@Q}"
+        exit 1
+    fi
+    )
+done
+
+
+( init_trap
+: "Check if merging fails in case of invalid mutable directory permissions"
+
+fake_root=${roots_dir:+"$roots_dir/mutable-directory-with-invalid-permissions"}
+hierarchy=/opt
+extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
+
+prepare_root "$fake_root" "$hierarchy"
+prepare_extension_image "$fake_root" "$hierarchy"
+prepare_extension_mutable_dir "$extension_data_dir"
+prepare_hierarchy "$fake_root" "$hierarchy"
+
+old_mode=$(stat --format '%#a' "$fake_root$hierarchy")
+chmod 0755 "$fake_root$hierarchy"
+prepend_trap "chmod ${old_mode@Q} ${fake_root@Q}${hierarchy@Q}"
+chmod 0700 "$extension_data_dir"
+
+(! run_systemd_sysext "$fake_root" --mutable=yes merge)
+)
+
+} # End of run_sysext_tests
+
+
+# For preparing /, we need mutable /usr/. If it is read only, skip running the
+# sysext tests on /.
+if [[ -w /usr ]]; then
+    run_sysext_tests ''
+fi
+run_sysext_tests "$FAKE_ROOTS_DIR"
+
 
 exit 0
index ecdd910c28c9ae3d6169fe28db366390fc5e259f..fbb93738b240bc27f77249ff72a3330a04898ce1 100755 (executable)
@@ -74,7 +74,7 @@ if [[ -n "$SD_STUB" ]]; then
     "$SD_PCRLOCK" lock-uki <"$SD_STUB"
 fi
 
-PIN=huhu "$SD_PCRLOCK" make-policy --pcr="$PCRS" --recovery-pin=yes
+PIN=huhu "$SD_PCRLOCK" make-policy --pcr="$PCRS" --recovery-pin=query
 # Repeat immediately (this call will have to reuse the nvindex, rather than create it)
 "$SD_PCRLOCK" make-policy --pcr="$PCRS"
 "$SD_PCRLOCK" make-policy --pcr="$PCRS" --force
@@ -102,7 +102,7 @@ systemd-cryptsetup detach pcrlock
 # work.
 echo -n test70 | "$SD_PCRLOCK" lock-raw --pcrlock=/var/lib/pcrlock.d/910-test70.pcrlock --pcr=16
 (! "$SD_PCRLOCK" make-policy --pcr="$PCRS")
-PIN=huhu "$SD_PCRLOCK" make-policy --pcr="$PCRS" --recovery-pin=yes
+PIN=huhu "$SD_PCRLOCK" make-policy --pcr="$PCRS" --recovery-pin=query
 
 systemd-cryptsetup attach pcrlock "$img" - tpm2-device=auto,tpm2-pcrlock=/var/lib/systemd/pcrlock.json,headless
 systemd-cryptsetup detach pcrlock
@@ -110,6 +110,10 @@ systemd-cryptsetup detach pcrlock
 # And now let's do it the clean way, and generate the right policy ahead of time.
 echo -n test70-take-two | "$SD_PCRLOCK" lock-raw --pcrlock=/var/lib/pcrlock.d/920-test70.pcrlock --pcr=16
 "$SD_PCRLOCK" make-policy --pcr="$PCRS"
+# the next one should be skipped because redundant
+"$SD_PCRLOCK" make-policy --pcr="$PCRS"
+# but this one should not be skipped, even if redundant, because we force it
+"$SD_PCRLOCK" make-policy --pcr="$PCRS" --force --recovery-pin=show
 
 "$SD_PCREXTEND" --pcr=16 test70-take-two