]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
Merge pull request #30284 from YHNdnzj/fstab-wantedby-defaultdeps
authorLennart Poettering <lennart@poettering.net>
Thu, 21 Dec 2023 21:47:54 +0000 (22:47 +0100)
committerGitHub <noreply@github.com>
Thu, 21 Dec 2023 21:47:54 +0000 (22:47 +0100)
fstab-generator: disable default deps if x-systemd.{wanted,required}-by= is used

321 files changed:
NEWS
TODO
docs/CREDENTIALS.md
docs/ENVIRONMENT.md
hwdb.d/60-evdev.hwdb
hwdb.d/60-sensor.hwdb
man/binfmt.d.xml
man/coredump.conf.xml
man/crypttab.xml
man/dnssec-trust-anchors.d.xml
man/environment.d.xml
man/homectl.xml
man/homed.conf.xml
man/journal-remote.conf.xml
man/journald.conf.xml
man/kernel-command-line.xml
man/logind.conf.xml
man/modules-load.d.xml
man/networkctl.xml
man/networkd.conf.xml
man/oomd.conf.xml
man/org.freedesktop.systemd1.xml
man/os-release.xml
man/repart.d.xml
man/resolved.conf.xml
man/rules/meson.build
man/sd-bus.xml
man/sd-device.xml
man/smbios-type-11.xml
man/sysctl.d.xml
man/systemctl.xml
man/systemd-ask-password-console.service.xml
man/systemd-cryptenroll.xml
man/systemd-fsck@.service.xml
man/systemd-journald.service.xml
man/systemd-run.xml
man/systemd-sleep.conf.xml
man/systemd-stub.xml
man/systemd-suspend.service.xml
man/systemd-sysext.xml
man/systemd-tmpfiles.xml
man/systemd.environment-generator.xml
man/systemd.exec.xml
man/systemd.generator.xml
man/systemd.net-naming-scheme.xml
man/systemd.network.xml
man/systemd.nspawn.xml
man/systemd.pcrlock.xml
man/systemd.preset.xml
man/systemd.system-credentials.xml
man/systemd.unit.xml
man/sysupdate.d.xml
man/sysusers.d.xml
man/timesyncd.conf.xml
man/tmpfiles.d.xml
man/uid0.xml [new file with mode: 0644]
man/ukify.xml
meson.build
mkosi.conf
mkosi.images/base/mkosi.build.chroot
mkosi.images/base/mkosi.conf
mkosi.images/system/mkosi.conf
mkosi.images/system/mkosi.conf.d/10-centos-fedora.conf
mkosi.images/system/mkosi.conf.d/10-centos/mkosi.conf
mkosi.images/system/mkosi.conf.d/10-centos/mkosi.conf.d/10-centos8.conf [deleted file]
mkosi.images/system/mkosi.conf.d/10-centos/mkosi.conf.d/10-centos9.conf [deleted file]
mkosi.images/system/mkosi.conf.d/10-fedora.conf
mkosi.images/system/mkosi.postinst.chroot
po/tr.po
rules.d/50-udev-default.rules.in
rules.d/75-net-description.rules
src/analyze/analyze-verify-util.c
src/basic/build.c
src/basic/cgroup-util.c
src/basic/env-util.c
src/basic/env-util.h
src/basic/ether-addr-util.c
src/basic/ether-addr-util.h
src/basic/iovec-util.c
src/basic/iovec-util.h
src/basic/parse-util.c
src/basic/pidref.c
src/basic/pidref.h
src/basic/rlimit-util.c
src/basic/rlimit-util.h
src/basic/signal-util.c
src/basic/siphash24.h
src/basic/socket-util.c
src/basic/terminal-util.c
src/basic/terminal-util.h
src/basic/time-util.h
src/basic/uid-range.c
src/basic/uid-range.h
src/basic/virt.c
src/boot/bootctl-install.c
src/boot/bootctl-status.c
src/core/dbus-cgroup.c
src/core/dbus-execute.c
src/core/dbus-service.c
src/core/dbus-unit.c
src/core/dbus-util.c
src/core/dbus-util.h
src/core/dbus.c
src/core/device.c
src/core/exec-invoke.c
src/core/execute.c
src/core/execute.h
src/core/job.c
src/core/load-fragment.c
src/core/manager.c
src/core/unit.c
src/creds/creds.c
src/cryptenroll/cryptenroll-pkcs11.c
src/dissect/dissect.c
src/firstboot/firstboot.c
src/fsck/fsck.c
src/fundamental/macro-fundamental.h
src/hibernate-resume/hibernate-resume-generator.c
src/home/homectl-pkcs11.c
src/home/homectl.c
src/home/homed-home-bus.c
src/home/homed-manager-bus.c
src/home/homework-luks.c
src/hostname/hostnamed.c
src/hwdb/hwdb.c
src/import/importd.c
src/journal/journalctl.c
src/journal/journald-context.c
src/journal/journald-server.c
src/kernel-install/60-ukify.install.in
src/libsystemd/sd-bus/bus-track.c
src/libsystemd/sd-bus/test-bus-creds.c
src/libsystemd/sd-device/test-sd-device-monitor.c
src/libsystemd/sd-device/test-sd-device-thread.c
src/libsystemd/sd-device/test-sd-device.c
src/libsystemd/sd-journal/catalog.c
src/libsystemd/sd-journal/journal-file.c
src/libsystemd/sd-journal/sd-journal.c
src/libsystemd/sd-login/test-login.c
src/libsystemd/sd-netlink/netlink-message-rtnl.c
src/libsystemd/sd-netlink/sd-netlink.c
src/libudev/test-udev-device-thread.c
src/locale/localed.c
src/login/logind-dbus.c
src/login/logind-polkit.c
src/login/logind-seat-dbus.c
src/login/logind-session-dbus.c
src/login/logind-user-dbus.c
src/machine/image-dbus.c
src/machine/machine-dbus.c
src/machine/machine.c
src/machine/machined-dbus.c
src/mount/mount-tool.c
src/network/meson.build
src/network/networkctl-config-file.c [new file with mode: 0644]
src/network/networkctl-config-file.h [new file with mode: 0644]
src/network/networkctl.c
src/network/networkctl.h [new file with mode: 0644]
src/network/networkd-address.c
src/network/networkd-bridge-vlan.c
src/network/networkd-gperf.gperf
src/network/networkd-json.c
src/network/networkd-link-bus.c
src/network/networkd-link.c
src/network/networkd-link.h
src/network/networkd-manager-bus.c
src/network/networkd-manager.c
src/network/networkd-manager.h
src/network/networkd-neighbor.c
src/network/networkd-nexthop.c
src/network/networkd-nexthop.h
src/network/networkd-queue.c
src/network/networkd-route.c
src/network/networkd-setlink.c
src/network/networkd-sysctl.c
src/network/networkd.conf
src/nspawn/nspawn-cgroup.c
src/nspawn/nspawn-network.c
src/nspawn/nspawn-register.c
src/nspawn/nspawn.c
src/oom/oomd-util.c
src/partition/growfs.c
src/partition/repart.c
src/pcrextend/pcrextend.c
src/pcrlock/pcrlock.c
src/portable/portabled-bus.c
src/portable/portabled-image-bus.c
src/resolve/dns-type.c
src/resolve/dns-type.h
src/resolve/resolved-bus.c
src/resolve/resolved-dns-cache.c
src/resolve/resolved-dns-dnssec.c
src/resolve/resolved-dns-packet.c
src/resolve/resolved-dns-scope.c
src/resolve/resolved-dns-stub.c
src/resolve/resolved-dns-transaction.c
src/resolve/resolved-dns-trust-anchor.c
src/resolve/resolved-dns-zone.c
src/resolve/resolved-dnssd-bus.c
src/resolve/resolved-link-bus.c
src/resolve/resolved-manager.c
src/resolve/resolved-util.c
src/run/meson.build
src/run/run.c
src/run/systemd-uid0.in [new file with mode: 0644]
src/shared/ask-password-api.c
src/shared/blockdev-util.c
src/shared/blockdev-util.h
src/shared/bus-polkit.c
src/shared/bus-polkit.h
src/shared/bus-wait-for-jobs.c
src/shared/bus-wait-for-jobs.h
src/shared/cgroup-setup.c
src/shared/color-util.c [new file with mode: 0644]
src/shared/color-util.h [new file with mode: 0644]
src/shared/creds-util.c
src/shared/creds-util.h
src/shared/discover-image.c
src/shared/dissect-image.c
src/shared/dissect-image.h
src/shared/dns-domain.c
src/shared/find-esp.c
src/shared/hwdb-util.c
src/shared/hwdb-util.h
src/shared/install.c
src/shared/loop-util.c
src/shared/loop-util.h
src/shared/machine-credential.c
src/shared/machine-credential.h
src/shared/meson.build
src/shared/mount-util.c
src/shared/netif-naming-scheme.c
src/shared/netif-naming-scheme.h
src/shared/openssl-util.c
src/shared/openssl-util.h
src/shared/pkcs11-util.c
src/shared/pkcs11-util.h
src/shared/ptyfwd.c
src/shared/ptyfwd.h
src/shared/specifier.c
src/shared/tests.c
src/shared/varlink-io.systemd.Credentials.c [new file with mode: 0644]
src/shared/varlink-io.systemd.Credentials.h [new file with mode: 0644]
src/shared/varlink.c
src/shared/varlink.h
src/shutdown/shutdown.c
src/sysext/sysext.c
src/systemctl/systemctl-start-special.c
src/systemctl/systemctl-start-unit.c
src/test/meson.build
src/test/test-bootspec.c
src/test/test-cgroup-util.c
src/test/test-cgroup.c
src/test/test-color-util.c [new file with mode: 0644]
src/test/test-condition.c
src/test/test-env-util.c
src/test/test-mempress.c
src/test/test-mount-util.c
src/test/test-mountpoint-util.c
src/test/test-parse-util.c
src/test/test-rlimit-util.c
src/test/test-terminal-util.c
src/test/test-varlink-idl.c
src/test/test-watch-pid.c
src/timedate/timedated.c
src/timesync/timesyncd-bus.c
src/udev/test-udev-format.c
src/udev/test-udev-spawn.c
src/udev/udev-builtin-net_id.c
src/udev/udevadm-hwdb.c
src/ukify/ukify.py
src/userdb/userdbd-manager.c
src/userdb/userwork.c
src/vconsole/vconsole-setup.c
src/vmspawn/vmspawn.c
test/TEST-08-INITRD/deny-list-ubuntu-ci [deleted file]
test/TEST-08-INITRD/test.sh
test/TEST-24-CRYPTSETUP/template.cfg [new file with mode: 0644]
test/TEST-24-CRYPTSETUP/test.sh
test/TEST-64-UDEV-STORAGE/test.sh
test/TEST-74-AUX-UTILS/test.sh
test/hwdb-test.sh
test/test-functions
test/test-network-generator-conversion.sh
test/test-network/conf/11-test-unit-file.link [new file with mode: 0644]
test/test-network/conf/11-test-unit-file.link.d/dropin.conf [new file with mode: 0644]
test/test-network/conf/11-test-unit-file.netdev [new file with mode: 0644]
test/test-network/conf/11-test-unit-file.netdev.d/dropin.conf [new file with mode: 0644]
test/test-network/conf/11-test-unit-file.network [new file with mode: 0644]
test/test-network/conf/11-test-unit-file.network.d/dropin.conf [new file with mode: 0644]
test/test-network/conf/25-vcan.netdev
test/test-network/conf/25-vcan98.netdev [new file with mode: 0644]
test/test-network/conf/25-vcan98.network [new file with mode: 0644]
test/test-network/conf/networkd-manage-foreign-nexthops-no.conf [new file with mode: 0644]
test/test-network/systemd-networkd-tests.py
test/test-systemctl-enable.sh
test/units/testsuite-04.bsod.sh
test/units/testsuite-07.exec-context.sh
test/units/testsuite-07.issue-30412.sh
test/units/testsuite-08.sh
test/units/testsuite-15.sh
test/units/testsuite-19.delegate.sh
test/units/testsuite-24.sh
test/units/testsuite-26.sh
test/units/testsuite-31.sh
test/units/testsuite-54.sh
test/units/testsuite-60.sh
test/units/testsuite-62-6.service [new file with mode: 0644]
test/units/testsuite-62.sh
test/units/testsuite-65.sh
test/units/testsuite-73.sh
test/units/testsuite-74.bootctl.sh [new file with mode: 0755]
test/units/testsuite-74.networkctl.sh
test/units/testsuite-74.run.sh
test/units/testsuite-75.sh
test/units/util.sh
units/meson.build
units/systemd-creds.socket [new file with mode: 0644]
units/systemd-creds@.service [new file with mode: 0644]
units/systemd-homed-firstboot.service [new file with mode: 0644]
units/systemd-homed.service.in

diff --git a/NEWS b/NEWS
index a42c8f514b1056db56c4d634ce12f59b2955a1b9..b1e7779b6027c75a3fa53bacbcb52714dbceb1ce 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -1,9 +1,15 @@
 systemd System and Service Manager
 
-CHANGES WITH 255 in spe:
+CHANGES WITH 256 in spe:
 
         Announcements of Future Feature Removals and Incompatible Changes:
 
+        * Previously, systemd-networkd did not explicitly remove any bridge VLAN
+          IDs assigned on bridge master and ports. Since v256, if a .network
+          file for an interface has at least one valid settings in [BridgeVLAN]
+          section, then all assigned VLAN IDs on the interface that are not
+          configured in the .network file are removed.
+
 CHANGES WITH 255:
 
         Announcements of Future Feature Removals and Incompatible Changes:
diff --git a/TODO b/TODO
index c75f69e650eea7e5adeb936784e43463a6070ad6..4d2fd411dca34fd33b1515fa102b521ce01d039b 100644 (file)
--- a/TODO
+++ b/TODO
@@ -132,6 +132,21 @@ Deprecations and removals:
 
 Features:
 
+* add a new ExecStart= flag that inserts the configured user's shell as first
+  word in the comand line. (maybe use character '.'). Usecase: tool such as
+  uid0 can use that to spawn the target user's default shell.
+
+* varlink: figure out how to do docs for our varlink interfaces. Idea: install
+  interface files augmented with docs in /usr/share/ somewhere. And have
+  functionality in varlinkctl to merge interface info extracted from binaries
+  with interface info on disk. And store the doc strings only in the latter.
+
+* introduce mntid_t, and make it 64bit, as apparently the kernel switched to
+  64bit mount ids
+
+* use udev rule networkd ownership property to take ownership of network
+  interfaces nspawn creates
+
 * add a kernel cmdline switch (and cred?) for marking a system to be
   "headless", in which case we never open /dev/console for reading, only for
   writing. This would then mean: systemd-firstboot would process creds but not
@@ -920,10 +935,6 @@ Features:
     file system paths to enable on start.
   • make systemd-fstab-generator look for a system credential encoding root= or
     usr=
-  • systemd-homed: when initializing, look for a credential
-    systemd.homed.register or so with JSON user records to automatically
-    register if not registered yet.  Use case: deploy a system, and add an
-    account one can directly log into.
   • in gpt-auto-generator: check partition uuids against such uuids supplied via
     sd-stub credentials. That way, we can support parallel OS installations with
     pre-built kernels.
@@ -2248,11 +2259,6 @@ Features:
   - support new FS_IOC_ADD_ENCRYPTION_KEY ioctl for setting up fscrypt
   - maybe pre-create ~/.cache as subvol so that it can have separate quota
     easily?
-  - add a switch to homectl (maybe called --first-boot) where it will check if
-    any non-system users exist, and if not prompts interactively for basic user
-    info, mimicking systemd-firstboot. Then, place this in a service that runs
-    after systemd-homed, but before gdm and friends, as a simple, barebones
-    fallback logic to get a regular user created on uninitialized systems.
   - store PKCS#11 + FIDO2 token info in LUKS2 header, compatible with
     systemd-cryptsetup, so that it can unlock homed volumes
   - maybe make all *.home files owned by `systemd-home` user or so, so that we
index f508c84f4c6a41c597026bcf69f97ec457e2db17..153a42be5474df48b4dfe9b87c689456922ae963 100644 (file)
@@ -455,7 +455,7 @@ qemu-system-x86_64 \
         -device scsi-hd,drive=hd,bootindex=1 \
         -device vhost-vsock-pci,id=vhost-vsock-pci0,guest-cid=42 \
         -smbios type=11,value=io.systemd.credential:vmm.notify_socket=vsock:2:1234 \
-        -smbios type=11,value=io.systemd.credential.binary:tmpfiles.extra=$(echo "f~ /root/.ssh/authorized_keys 700 root root - $(ssh-add -L | base64 -w 0)" | base64 -w 0)
+        -smbios type=11,value=io.systemd.credential.binary:tmpfiles.extra=$(echo "f~ /root/.ssh/authorized_keys 600 root root - $(ssh-add -L | base64 -w 0)" | base64 -w 0)
 ```
 
 A process on the host can listen for the notification, for example:
index 5e15b2ba1d62e750433adac6af9ed1992bec209c..819d5367738bf974fe7d9aa944536bfdeebf5b44 100644 (file)
@@ -249,6 +249,14 @@ All tools:
   devices sysfs path are actually backed by sysfs. Relaxing this verification
   is useful for testing purposes.
 
+`udevadm` and `systemd-hwdb`:
+
+* `SYSTEMD_HWDB_UPDATE_BYPASS=` — If set to "1", execution of hwdb updates is skipped
+  when `udevadm hwdb --update` or `systemd-hwdb update` are invoked. This can
+  be useful if either of these tools are invoked unconditionally as a child
+  process by another tool, such as package managers running either of these
+  tools in a postinstall script.
+
 `nss-systemd`:
 
 * `$SYSTEMD_NSS_BYPASS_SYNTHETIC=1` — if set, `nss-systemd` won't synthesize
index a9ce4e2862d5032f9ad5c403cbcb7b7e18ccfbac..9a90420b5f1c093ba06aedf86769fb3300d9615a 100644 (file)
@@ -237,6 +237,15 @@ evdev:name:SYNA3602:00 0911:5288 Touchpad:dmi:*svnBANGHO:pnCLOUDPRO:*
  EVDEV_ABS_35=52:1747:17
  EVDEV_ABS_36=45:954:14
 
+#########################################
+# Bosto
+#########################################
+
+# Bosto BT-12HD series
+evdev:input:b0003v0ED1p7821*
+ EVDEV_ABS_00=::2271
+ EVDEV_ABS_01=::5080
+
 #########################################
 # Dell
 #########################################
index 5d09b88b13ea1c367e6ee9f789bdb7ca95fc2e18..1086940b78250225fc23a64adcc3a59942d7ddd6 100644 (file)
@@ -392,6 +392,9 @@ sensor:modalias:platform:HID-SENSOR-200073:dmi:*svnDell*:sku0CC4:*
 sensor:modalias:platform:HID-SENSOR-200073:dmi:*svnDell*:sku0CC5:*
 sensor:modalias:platform:HID-SENSOR-200073:dmi:*svnDell*:sku0CC7:*
 sensor:modalias:platform:HID-SENSOR-200073:dmi:*svnDell*:sku0CC8:*
+sensor:modalias:platform:HID-SENSOR-200073:dmi:*svnDell*:sku0CB2:*
+sensor:modalias:platform:HID-SENSOR-200073:dmi:*svnDell*:skuOCB4:*
+sensor:modalias:platform:HID-SENSOR-200073:dmi:*svnDell*:sku0CB3:*
  ACCEL_LOCATION=base
 
 # Dell Venue 8 Pro 3845
index ab56460d3e96dd55a416d7c73b2797fff0982d23..7420b5ee68a09d1b5f6ac66589ae346d089aacca 100644 (file)
   </refnamediv>
 
   <refsynopsisdiv>
-    <para><filename>/etc/binfmt.d/*.conf</filename></para>
-    <para><filename>/run/binfmt.d/*.conf</filename></para>
-    <para><filename>/usr/lib/binfmt.d/*.conf</filename></para>
+    <para><simplelist>
+      <member><filename>/etc/binfmt.d/*.conf</filename></member>
+      <member><filename>/run/binfmt.d/*.conf</filename></member>
+      <member><filename>/usr/lib/binfmt.d/*.conf</filename></member>
+    </simplelist></para>
   </refsynopsisdiv>
 
   <refsect1>
index 61014d3823517e6b30402119db6d49a843f6859c..aa73f62ea8f01133b697ec8f6b2f7e9aafbd34d4 100644 (file)
   </refnamediv>
 
   <refsynopsisdiv>
-    <para><filename>/etc/systemd/coredump.conf</filename></para>
-    <para><filename>/etc/systemd/coredump.conf.d/*.conf</filename></para>
-    <para><filename>/run/systemd/coredump.conf.d/*.conf</filename></para>
-    <para><filename>/usr/lib/systemd/coredump.conf.d/*.conf</filename></para>
+    <para><simplelist>
+      <member><filename>/etc/systemd/coredump.conf</filename></member>
+      <member><filename>/etc/systemd/coredump.conf.d/*.conf</filename></member>
+      <member><filename>/run/systemd/coredump.conf.d/*.conf</filename></member>
+      <member><filename>/usr/lib/systemd/coredump.conf.d/*.conf</filename></member>
+    </simplelist></para>
   </refsynopsisdiv>
 
   <refsect1>
index eb742be058221f5e438f047208ace545ea1d6904..c35d782c8dd4106ec196c164be9f6b450e0909b7 100644 (file)
       see above and below.</para></listitem>
 
       <listitem><para>The key may be acquired via a PKCS#11 compatible hardware security token or
-      smartcard. In this case an encrypted key is stored on disk/removable media, acquired via
-      <constant>AF_UNIX</constant>, or stored in the LUKS2 JSON token metadata header. The encrypted key is
-      then decrypted by the PKCS#11 token with an RSA key stored on it, and then used to unlock the encrypted
-      volume. Use the <option>pkcs11-uri=</option> option described below to use this mechanism.</para></listitem>
+      smartcard. In this case a saved key used in unlock process is stored on disk/removable media, acquired via
+      <constant>AF_UNIX</constant>, or stored in the LUKS2 JSON token metadata header. For RSA, the saved key
+      is an encrypted volume key. The encrypted volume key is then decrypted by the PKCS#11 token with an RSA
+      private key stored on it, and used to unlock the encrypted volume. For elliptic-curve (EC) cryptography,
+      the saved key is the public key generated in enrollment process. The public key is then used to derive
+      a shared secret with a private key stored in the PKCS#11 token. The derived shared secret is then used
+      to unlock the volume. Use the <option>pkcs11-uri=</option> option described below to use this mechanism.
+      </para></listitem>
 
       <listitem><para>Similarly, the key may be acquired via a FIDO2 compatible hardware security token
       (which must implement the "hmac-secret" extension). In this case a key generated randomly during
         <term><option>pkcs11-uri=</option></term>
 
         <listitem><para>Takes either the special value <literal>auto</literal> or an <ulink
-        url="https://tools.ietf.org/html/rfc7512">RFC7512 PKCS#11 URI</ulink> pointing to a private RSA key
+        url="https://tools.ietf.org/html/rfc7512">RFC7512 PKCS#11 URI</ulink> pointing to a private key
         which is used to decrypt the encrypted key specified in the third column of the line. This is useful
         for unlocking encrypted volumes through PKCS#11 compatible security tokens or smartcards. See below
         for an example how to set up this mechanism for unlocking a LUKS2 volume with a YubiKey security
         security token metadata in its LUKS2 JSON token section. In this mode the URI and the encrypted key
         are automatically read from the LUKS2 JSON token header. Use
         <citerefentry><refentrytitle>systemd-cryptenroll</refentrytitle><manvolnum>1</manvolnum></citerefentry>
-        as simple tool for enrolling PKCS#11 security tokens or smartcards in a way compatible with
+        as simple tool for enrolling PKCS#11 security tokens or smartcards in a way compatible with
         <literal>auto</literal>. In this mode the third column of the line should remain empty (that is,
         specified as <literal>-</literal>).</para>
 
-        <para>The specified URI can refer directly to a private RSA key stored on a token or alternatively
-        just to a slot or token, in which case a search for a suitable private RSA key will be performed.  In
-        this case if multiple suitable objects are found the token is refused. The encrypted key configured
-        in the third column of the line is passed as is (i.e. in binary form, unprocessed) to RSA
-        decryption. The resulting decrypted key is then Base64 encoded before it is used to unlock the LUKS
-        volume.</para>
+        <para>The specified URI can refer directly to a private key stored on a token or alternatively
+        just to a slot or token, in which case a search for a suitable private key will be performed.  In
+        this case if multiple suitable objects are found the token is refused. The keyfile configured
+        in the third column of the line is used as is (i.e. in binary form, unprocessed). The resulting
+        decrypted key (for RSA) or derived shared secret (for ECC) is then Base64 encoded before it is used
+        to unlock the LUKS volume.</para>
 
         <para>Use <command>systemd-cryptenroll --pkcs11-token-uri=list</command> to list all suitable PKCS#11
         security tokens currently plugged in, along with their URIs.</para>
@@ -969,8 +973,8 @@ external   /dev/sda3       keyfile:LABEL=keydev keyfile-timeout=10s,cipher=xchac
       <title>Yubikey-based PKCS#11 Volume Unlocking Example</title>
 
       <para>The PKCS#11 logic allows hooking up any compatible security token that is capable of storing RSA
-      decryption keys for unlocking an encrypted volume. Here's an example how to set up a Yubikey security
-      token for this purpose on a LUKS2 volume, using <citerefentry
+      or EC cryptographic keys for unlocking an encrypted volume. Here's an example how to set up a Yubikey
+      security token for this purpose on a LUKS2 volume, using <citerefentry
       project='debian'><refentrytitle>ykmap</refentrytitle><manvolnum>1</manvolnum></citerefentry> from the
       yubikey-manager project to initialize the token and
       <citerefentry><refentrytitle>systemd-cryptenroll</refentrytitle><manvolnum>1</manvolnum></citerefentry>
index 39b9515c45234aae202def276b03ce5ac8747f51..391f93b5b7799568865a45635af4aed8a8f45617 100644 (file)
   </refnamediv>
 
   <refsynopsisdiv>
-    <para><filename>/etc/dnssec-trust-anchors.d/*.positive</filename></para>
-    <para><filename>/run/dnssec-trust-anchors.d/*.positive</filename></para>
-    <para><filename>/usr/lib/dnssec-trust-anchors.d/*.positive</filename></para>
-    <para><filename>/etc/dnssec-trust-anchors.d/*.negative</filename></para>
-    <para><filename>/run/dnssec-trust-anchors.d/*.negative</filename></para>
-    <para><filename>/usr/lib/dnssec-trust-anchors.d/*.negative</filename></para>
+    <para><simplelist>
+      <member><filename>/etc/dnssec-trust-anchors.d/*.positive</filename></member>
+      <member><filename>/run/dnssec-trust-anchors.d/*.positive</filename></member>
+      <member><filename>/usr/lib/dnssec-trust-anchors.d/*.positive</filename></member>
+      <member><filename>/etc/dnssec-trust-anchors.d/*.negative</filename></member>
+      <member><filename>/run/dnssec-trust-anchors.d/*.negative</filename></member>
+      <member><filename>/usr/lib/dnssec-trust-anchors.d/*.negative</filename></member>
+    </simplelist></para>
   </refsynopsisdiv>
 
   <refsect1>
index fc03405a94098f2e9a20a28abd0599faa9794a68..f0c63da77deaf04924a398b855c775a368239292 100644 (file)
   </refnamediv>
 
   <refsynopsisdiv>
-    <para><filename>~/.config/environment.d/*.conf</filename></para>
-    <para><filename>/etc/environment.d/*.conf</filename></para>
-    <para><filename>/run/environment.d/*.conf</filename></para>
-    <para><filename>/usr/lib/environment.d/*.conf</filename></para>
-    <para><filename>/etc/environment</filename></para>
+    <para><simplelist>
+      <member><filename>~/.config/environment.d/*.conf</filename></member>
+      <member><filename>/etc/environment.d/*.conf</filename></member>
+      <member><filename>/run/environment.d/*.conf</filename></member>
+      <member><filename>/usr/lib/environment.d/*.conf</filename></member>
+      <member><filename>/etc/environment</filename></member>
+    </simplelist></para>
   </refsynopsisdiv>
 
   <refsect1>
index 7fc7d5f01223c0a9daec021c0d931ec3622ba44a..7e26c941799e8219b86b184f65adaae9ae304ca8 100644 (file)
@@ -18,6 +18,7 @@
 
   <refnamediv>
     <refname>homectl</refname>
+    <refname>systemd-homed-firstboot.service</refname>
     <refpurpose>Create, remove, change or inspect home directories</refpurpose>
   </refnamediv>
 
 
         <xi:include href="version-info.xml" xpointer="v250"/></listitem>
       </varlistentry>
+
+      <varlistentry>
+        <term><command>firstboot</command></term>
+
+        <listitem><para>This command is supposed to be invoked during the initial boot of the system. It
+        checks whether any regular home area exists so far, and if not queries the user interactively on the
+        console for user name and password and creates one. Alternatively, if one or more service credentials
+        whose name starts with <literal>home.create.</literal> are passed to the command (containing a user
+        record in JSON format) these users are automatically created at boot.</para>
+
+        <para>This command is invoked by the <filename>systemd-homed-firstboot.service</filename> service
+        unit.</para>
+
+        <xi:include href="version-info.xml" xpointer="v256"/></listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+
+  <refsect1>
+    <title>Credentials</title>
+
+    <para>When invoked with the <command>firstboot</command> command, <command>homectl</command> supports the
+    service credentials logic as implemented by
+    <varname>ImportCredential=</varname>/<varname>LoadCredential=</varname>/<varname>SetCredential=</varname>
+    (see <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>1</manvolnum></citerefentry> for
+    details). The following credentials are used when passed in:</para>
+
+    <variablelist class='system-credentials'>
+      <varlistentry>
+        <term><varname>home.create.*</varname></term>
+
+        <listitem><para>If one or more credentials whose names begin with <literal>home.create.</literal>,
+        followed by a valid UNIX username are passed, a new home area is created, one for each specified user
+        record.</para>
+
+        <xi:include href="version-info.xml" xpointer="v256"/></listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+
+  <refsect1>
+    <title>Kernel Command Line</title>
+
+    <variablelist class='kernel-commandline-options'>
+      <varlistentry>
+        <term><varname>systemd.firstboot=</varname></term>
+
+        <listitem><para>This boolean will disable the effect of <command>homectl firstboot</command>
+        command. It's primarily interpreted by
+        <citerefentry><refentrytitle>systemd-firstboot</refentrytitle><manvolnum>1</manvolnum></citerefentry>.</para>
+
+        <xi:include href="version-info.xml" xpointer="v256"/></listitem>
+      </varlistentry>
     </variablelist>
   </refsect1>
 
index acc5f5f17614f7726c6823edfc843e885740faa1..4a075511b7b72aff087bd0a96f1817de89c067b4 100644 (file)
   </refnamediv>
 
   <refsynopsisdiv>
-    <para><filename>/etc/systemd/homed.conf</filename></para>
-    <para><filename>/etc/systemd/homed.conf.d/*.conf</filename></para>
-    <para><filename>/run/systemd/homed.conf.d/*.conf</filename></para>
-    <para><filename>/usr/lib/systemd/homed.conf.d/*.conf</filename></para>
+    <para><simplelist>
+      <member><filename>/etc/systemd/homed.conf</filename></member>
+      <member><filename>/etc/systemd/homed.conf.d/*.conf</filename></member>
+      <member><filename>/run/systemd/homed.conf.d/*.conf</filename></member>
+      <member><filename>/usr/lib/systemd/homed.conf.d/*.conf</filename></member>
+    </simplelist></para>
   </refsynopsisdiv>
 
   <refsect1>
index a5a5b56ec34db8b648ec7d3d2034d48c377840e7..7d75f0e79f55d9c1c28f6dbf3705b6166a108e4e 100644 (file)
   </refnamediv>
 
   <refsynopsisdiv>
-    <para><filename>/etc/systemd/journal-remote.conf</filename></para>
-    <para><filename>/etc/systemd/journal-remote.conf.d/*.conf</filename></para>
-    <para><filename>/run/systemd/journal-remote.conf.d/*.conf</filename></para>
-    <para><filename>/usr/lib/systemd/journal-remote.conf.d/*.conf</filename></para>
+    <para><simplelist>
+      <member><filename>/etc/systemd/journal-remote.conf</filename></member>
+      <member><filename>/etc/systemd/journal-remote.conf.d/*.conf</filename></member>
+      <member><filename>/run/systemd/journal-remote.conf.d/*.conf</filename></member>
+      <member><filename>/usr/lib/systemd/journal-remote.conf.d/*.conf</filename></member>
+    </simplelist></para>
   </refsynopsisdiv>
 
   <refsect1>
index e150d04dcff24298d3e2c733675b2ce8f7a9c41c..634cec4b52db4efa2e23b9b1c6640873bd34ecb3 100644 (file)
   </refnamediv>
 
   <refsynopsisdiv>
-    <para><filename>/etc/systemd/journald.conf</filename></para>
-    <para><filename>/etc/systemd/journald.conf.d/*.conf</filename></para>
-    <para><filename>/run/systemd/journald.conf.d/*.conf</filename></para>
-    <para><filename>/usr/lib/systemd/journald.conf.d/*.conf</filename></para>
-    <para><filename>/etc/systemd/journald@<replaceable>NAMESPACE</replaceable>.conf</filename></para>
-    <para><filename>/etc/systemd/journald@<replaceable>NAMESPACE</replaceable>.conf.d/*.conf</filename></para>
-    <para><filename>/run/systemd/journald@<replaceable>NAMESPACE</replaceable>.conf.d/*.conf</filename></para>
-    <para><filename>/usr/lib/systemd/journald@<replaceable>NAMESPACE</replaceable>.conf.d/*.conf</filename></para>
+    <para><simplelist>
+      <member><filename>/etc/systemd/journald.conf</filename></member>
+      <member><filename>/etc/systemd/journald.conf.d/*.conf</filename></member>
+      <member><filename>/run/systemd/journald.conf.d/*.conf</filename></member>
+      <member><filename>/usr/lib/systemd/journald.conf.d/*.conf</filename></member>
+      <member><filename>/etc/systemd/journald@<replaceable>NAMESPACE</replaceable>.conf</filename></member>
+      <member><filename>/etc/systemd/journald@<replaceable>NAMESPACE</replaceable>.conf.d/*.conf</filename></member>
+      <member><filename>/run/systemd/journald@<replaceable>NAMESPACE</replaceable>.conf.d/*.conf</filename></member>
+      <member><filename>/usr/lib/systemd/journald@<replaceable>NAMESPACE</replaceable>.conf.d/*.conf</filename></member>
+    </simplelist></para>
   </refsynopsisdiv>
 
   <refsect1>
index 6ac20ad2f4f51596fe60ff41312afd24ca6ba3b7..7a7b2b7deb45f5af065e33de29eef4323a77ea69 100644 (file)
 
         <listitem><para>Takes a boolean argument, defaults to on. If off,
         <citerefentry><refentrytitle>systemd-firstboot.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+        and
+        <citerefentry><refentrytitle>systemd-homed-firstboot.service</refentrytitle><manvolnum>1</manvolnum></citerefentry>
         will not query the user for basic system settings, even if the system boots up for the first time and
         the relevant settings are not initialized yet. Not to be confused with
         <varname>systemd.condition-first-boot=</varname> (see below), which overrides the result of the
index 2b5c11b916658f333f2fa53af9b0bf1861b8931f..e1d65821bfab7a9de8887e42f5e3a0d2816d5fff 100644 (file)
   </refnamediv>
 
   <refsynopsisdiv>
-    <para><filename>/etc/systemd/logind.conf</filename></para>
-    <para><filename>/etc/systemd/logind.conf.d/*.conf</filename></para>
-    <para><filename>/run/systemd/logind.conf.d/*.conf</filename></para>
-    <para><filename>/usr/lib/systemd/logind.conf.d/*.conf</filename></para>
+    <para><simplelist>
+      <member><filename>/etc/systemd/logind.conf</filename></member>
+      <member><filename>/etc/systemd/logind.conf.d/*.conf</filename></member>
+      <member><filename>/run/systemd/logind.conf.d/*.conf</filename></member>
+      <member><filename>/usr/lib/systemd/logind.conf.d/*.conf</filename></member>
+    </simplelist></para>
   </refsynopsisdiv>
 
   <refsect1>
index cd0c00687a54f3b2662328fffc2dba5c4b6088ff..1293dd5249a2d956fa3d21bd2f30bbe37e39d373 100644 (file)
   </refnamediv>
 
   <refsynopsisdiv>
-    <para><filename>/etc/modules-load.d/*.conf</filename></para>
-    <para><filename>/run/modules-load.d/*.conf</filename></para>
-    <para><filename>/usr/lib/modules-load.d/*.conf</filename></para>
+    <para><simplelist>
+      <member><filename>/etc/modules-load.d/*.conf</filename></member>
+      <member><filename>/run/modules-load.d/*.conf</filename></member>
+      <member><filename>/usr/lib/modules-load.d/*.conf</filename></member>
+    </simplelist></para>
   </refsynopsisdiv>
 
   <refsect1>
index 3a2dc09eccbe6b28c8aa6a37b801feb35c419504..1a03e9e11d445da9d3440b04dda1f0557f220547 100644 (file)
@@ -461,6 +461,40 @@ s - Service VLAN, m - Two-port MAC Relay (TPMR)
 
         <xi:include href="version-info.xml" xpointer="v254"/></listitem>
       </varlistentry>
+
+      <varlistentry>
+        <term>
+          <command>mask</command>
+          <replaceable>FILE</replaceable>…
+        </term>
+        <listitem><para>Mask network configuration files, which include <filename>.network</filename>,
+        <filename>.netdev</filename>, and <filename>.link</filename> files. A symlink of the given name will
+        be created under <filename>/etc/</filename> or <filename>/run/</filename>, depending on
+        whether <option>--runtime</option> is specified, that points to <filename>/dev/null</filename>.
+        If a non-empty config file with the specified name exists under the target directory or a directory
+        with higher priority (e.g. <option>--runtime</option> is used while an existing config resides
+        in <filename>/etc/</filename>), the operation is aborted.</para>
+
+        <para>This command honors <option>--no-reload</option> in the same way as <command>edit</command>.
+        </para>
+
+        <xi:include href="version-info.xml" xpointer="v256"/></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>
+          <command>unmask</command>
+          <replaceable>FILE</replaceable>…
+        </term>
+        <listitem><para>Unmask network configuration files, i.e. reverting the effect of <command>mask</command>.
+        Note that this command operates regardless of the scope of the directory, i.e. <option>--runtime</option>
+        is of no effect.</para>
+
+        <para>This command honors <option>--no-reload</option> in the same way as <command>edit</command>
+        and <command>mask</command>.</para>
+
+        <xi:include href="version-info.xml" xpointer="v256"/></listitem>
+      </varlistentry>
     </variablelist>
   </refsect1>
 
@@ -534,11 +568,11 @@ s - Service VLAN, m - Two-port MAC Relay (TPMR)
         <term><option>--no-reload</option></term>
 
         <listitem>
-          <para>When used with <command>edit</command>,
+          <para>When used with <command>edit</command>, <command>mask</command>, or <command>unmask</command>,
           <citerefentry><refentrytitle>systemd-networkd.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
           or
           <citerefentry><refentrytitle>systemd-udevd.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
-          will not be reloaded after the editing finishes.</para>
+          will not be reloaded after the operation finishes.</para>
 
         <xi:include href="version-info.xml" xpointer="v254"/>
         </listitem>
@@ -547,8 +581,8 @@ s - Service VLAN, m - Two-port MAC Relay (TPMR)
         <term><option>--runtime</option></term>
 
         <listitem>
-          <para>When used with <command>edit</command>, edit the file under <filename>/run/</filename>
-          instead of <filename>/etc/</filename>.</para>
+          <para>When used with <command>edit</command> or <command>mask</command>,
+          operate on the file under <filename>/run/</filename> instead of <filename>/etc/</filename>.</para>
 
           <xi:include href="version-info.xml" xpointer="v256"/>
         </listitem>
index 018bde0fbfa445bd82607976865197d6208248e9..27cab25d88f19c3e08f52fda58e043abc11a829d 100644 (file)
   </refnamediv>
 
   <refsynopsisdiv>
-    <para><filename>/etc/systemd/networkd.conf</filename></para>
-    <para><filename>/etc/systemd/networkd.conf.d/*.conf</filename></para>
-    <para><filename>/usr/lib/systemd/networkd.conf.d/*.conf</filename></para>
+    <para><simplelist>
+      <member><filename>/etc/systemd/networkd.conf</filename></member>
+      <member><filename>/etc/systemd/networkd.conf.d/*.conf</filename></member>
+      <member><filename>/usr/lib/systemd/networkd.conf.d/*.conf</filename></member>
+    </simplelist></para>
   </refsynopsisdiv>
 
   <refsect1>
         <xi:include href="version-info.xml" xpointer="v246"/></listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><varname>ManageForeignNextHops=</varname></term>
+        <listitem><para>A boolean. When true, <command>systemd-networkd</command> will remove nexthops
+        that are not configured in .network files (except for routes with protocol
+        <literal>kernel</literal>). When false, it will
+        not remove any foreign nexthops, keeping them even if they are not configured in a .network file.
+        Defaults to yes.</para>
+
+        <xi:include href="version-info.xml" xpointer="v256"/></listitem>
+      </varlistentry>
+
       <varlistentry>
         <term><varname>RouteTable=</varname></term>
         <listitem><para>Defines the route table name. Takes a whitespace-separated list of the pairs of
index 4fc7abd89501f472b7ab22f44166f350a6930559..e8f9b58658e77eb95346fa70b825013849aca85f 100644 (file)
   </refnamediv>
 
   <refsynopsisdiv>
-    <para><filename>/etc/systemd/oomd.conf</filename></para>
-    <para><filename>/etc/systemd/oomd.conf.d/*.conf</filename></para>
-    <para><filename>/usr/lib/systemd/oomd.conf.d/*.conf</filename></para>
+    <para><simplelist>
+      <member><filename>/etc/systemd/oomd.conf</filename></member>
+      <member><filename>/etc/systemd/oomd.conf.d/*.conf</filename></member>
+      <member><filename>/usr/lib/systemd/oomd.conf.d/*.conf</filename></member>
+    </simplelist></para>
   </refsynopsisdiv>
 
   <refsect1>
index 014a5cc8833bba70da92104be6c4443bd12e1e5f..ced9bea7db0a236ed98238511a26c05085bbc6c0 100644 (file)
@@ -1356,7 +1356,7 @@ node /org/freedesktop/systemd1 {
       <para><function>ResetFailed()</function> resets the "failed" state of all units.</para>
 
       <para><function>ListUnits()</function> returns an array of all currently loaded units. Note that
-      units may be known by multiple names at the same name, and hence there might be more unit names loaded
+      units may be known by multiple names at the same time, and hence there might be more unit names loaded
       than actual units behind them. The array consists of structures with the following elements:
       <itemizedlist>
         <listitem><para>The primary unit name as string</para></listitem>
index f2e0f3ecb7b39dbff2a95f94922d6aad36d8f324..79de2451bf3774fb8e8b8d95540d073683c6c59b 100644 (file)
   </refnamediv>
 
   <refsynopsisdiv>
-    <para><filename>/etc/os-release</filename></para>
-    <para><filename>/usr/lib/os-release</filename></para>
-    <para><filename>/etc/initrd-release</filename></para>
-    <para><filename>/usr/lib/extension-release.d/extension-release.<replaceable>IMAGE</replaceable></filename></para>
+    <para><simplelist>
+      <member><filename>/etc/os-release</filename></member>
+      <member><filename>/usr/lib/os-release</filename></member>
+      <member><filename>/etc/initrd-release</filename></member>
+      <member><filename>/usr/lib/extension-release.d/extension-release.<replaceable>IMAGE</replaceable></filename></member>
+    </simplelist></para>
   </refsynopsisdiv>
 
   <refsect1>
index 79908a0805458350e75c2a54e440d7a9fcfae8ae..ea8cd9e4e496701f1e59826180b2b2918213c94b 100644 (file)
   </refnamediv>
 
   <refsynopsisdiv>
-    <para><literallayout><filename>/etc/repart.d/*.conf</filename>
-<filename>/run/repart.d/*.conf</filename>
-<filename>/usr/lib/repart.d/*.conf</filename>
-    </literallayout></para>
+    <para><simplelist>
+      <member><filename>/etc/repart.d/*.conf</filename></member>
+      <member><filename>/run/repart.d/*.conf</filename></member>
+      <member><filename>/usr/lib/repart.d/*.conf</filename></member>
+    </simplelist></para>
   </refsynopsisdiv>
 
   <refsect1>
index d153865c6e33a2adc45a22b7e49e9b8b5f97665a..194a614244ea2fb0f56b6a9d55949a410786e0a9 100644 (file)
   </refnamediv>
 
   <refsynopsisdiv>
-    <para><filename>/etc/systemd/resolved.conf</filename></para>
-    <para><filename>/etc/systemd/resolved.conf.d/*.conf</filename></para>
-    <para><filename>/run/systemd/resolved.conf.d/*.conf</filename></para>
-    <para><filename>/usr/lib/systemd/resolved.conf.d/*.conf</filename></para>
+    <para><simplelist>
+      <member><filename>/etc/systemd/resolved.conf</filename></member>
+      <member><filename>/etc/systemd/resolved.conf.d/*.conf</filename></member>
+      <member><filename>/run/systemd/resolved.conf.d/*.conf</filename></member>
+      <member><filename>/usr/lib/systemd/resolved.conf.d/*.conf</filename></member>
+    </simplelist></para>
   </refsynopsisdiv>
 
   <refsect1>
index 5dc3e08896dcb6dae239123ce3463cdfc42a8fb0..622921f8d63db890dbe71f48c03e7ea8aadfae68 100644 (file)
@@ -18,7 +18,7 @@ manpages = [
   'ENABLE_RESOLVE'],
  ['environment.d', '5', [], 'ENABLE_ENVIRONMENT_D'],
  ['file-hierarchy', '7', [], ''],
- ['homectl', '1', [], 'ENABLE_HOMED'],
+ ['homectl', '1', ['systemd-homed-firstboot.service'], 'ENABLE_HOMED'],
  ['homed.conf', '5', ['homed.conf.d'], 'ENABLE_HOMED'],
  ['hostname', '5', [], ''],
  ['hostnamectl', '1', [], 'ENABLE_HOSTNAMED'],
@@ -1257,6 +1257,7 @@ manpages = [
   ''],
  ['udev_new', '3', ['udev_ref', 'udev_unref'], ''],
  ['udevadm', '8', [], ''],
+ ['uid0', '1', [], ''],
  ['ukify', '1', [], 'ENABLE_UKIFY'],
  ['user@.service',
   '5',
index 4c9c00986a28b1861b4570460aac72a426767ecc..9e67b0b4672f4f028efe214892c00d61d0f3e408 100644 (file)
     for more information about D-Bus IPC.</para>
 
     <para>See
-    <literallayout><citerefentry><refentrytitle>sd_bus_add_match</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_add_object</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_add_object_manager</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_add_object_vtable</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_add_fallback</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_add_fallback_vtable</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_add_filter</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_add_node_enumerator</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_attach_event</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_call</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_call_async</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_call_method</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_call_method_async</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_can_send</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_close</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_creds_get_pid</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_creds_new_from_pid</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_default</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_emit_interfaces_added</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_emit_interfaces_added_strv</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_emit_interfaces_removed</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_emit_interfaces_removed_strv</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_emit_object_added</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_emit_object_removed</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_emit_properties_changed</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_emit_properties_changed_strv</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_emit_signal</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_emit_signalv</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_emit_signal_to</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_emit_signal_tov</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd-bus-errors</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_error</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_error_add_map</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_get_address</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_get_allow_interactive_authorization</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_get_bus_id</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_get_creds_mask</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_get_current_handler</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_get_current_message</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_get_current_slot</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_get_current_userdata</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_get_exit_on_disconnect</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_get_fd</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_get_method_call_timeout</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_get_n_queued_read</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_get_name_creds</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_get_name_machine_id</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_get_owner_creds</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_get_property</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_get_property_string</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_get_property_strv</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_get_property_trivial</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_get_scope</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_get_tid</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_get_unique_name</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_interface_name_is_valid</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_is_bus_client</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_is_monitor</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_is_server</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_list_names</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_message_append</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_message_append_array</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_message_append_basic</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_message_append_string_memfd</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_message_append_strv</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_message_at_end</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_message_close_container</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_message_copy</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_message_dump</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_message_enter_container</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_message_exit_container</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_message_get_allow_interactive_authorization</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_message_get_cookie</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_message_get_creds</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_message_get_errno</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_message_get_error</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_message_get_monotonic_usec</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_message_get_sender</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_message_get_signature</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_message_get_type</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_message_new</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_message_new_method_call</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_message_new_method_error</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_message_new_signal</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_message_new_signal_to</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_message_open_container</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_message_peek_type</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_message_read</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_message_read_array</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_message_read_basic</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_message_read_strv</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_message_rewind</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_message_seal</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_message_send</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_message_set_allow_interactive_authorization</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_message_set_destination</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_message_set_expect_reply</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_message_set_sender</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_message_skip</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_message_verify_type</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_negotiate_fds</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_new</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_path_encode</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_process</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_query_sender_creds</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_query_sender_privilege</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_reply_method_error</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_reply_method_return</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_request_name</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_send</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_send_to</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_set_address</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_set_allow_interactive_authorization</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_set_bus_client</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_set_close_on_exit</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_set_connected_signal</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_set_description</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_set_exit_on_disconnect</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_set_method_call_timeout</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_set_monitor</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_set_property</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_set_propertyv</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_set_sender</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_set_server</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_set_watch_bind</refentrytitle><manvolnum>3</manvolnum></citerefentry>
-<citerefentry><refentrytitle>sd_bus_slot_get_current_handler</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_slot_get_current_message</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_slot_get_current_userdata</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_slot_set_description</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_slot_set_destroy_callback</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_slot_set_floating</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_slot_set_userdata</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_start</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_track_add_name</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_track_new</refentrytitle><manvolnum>3</manvolnum></citerefentry>
-</literallayout>
+    <simplelist>
+      <member><citerefentry><refentrytitle>sd_bus_add_match</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+      <member><citerefentry><refentrytitle>sd_bus_add_object</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+      <member><citerefentry><refentrytitle>sd_bus_add_object_manager</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+      <member><citerefentry><refentrytitle>sd_bus_add_object_vtable</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+      <member><citerefentry><refentrytitle>sd_bus_add_fallback</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+      <member><citerefentry><refentrytitle>sd_bus_add_fallback_vtable</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+      <member><citerefentry><refentrytitle>sd_bus_add_filter</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+      <member><citerefentry><refentrytitle>sd_bus_add_node_enumerator</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+      <member><citerefentry><refentrytitle>sd_bus_attach_event</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+      <member><citerefentry><refentrytitle>sd_bus_call</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+      <member><citerefentry><refentrytitle>sd_bus_call_async</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+      <member><citerefentry><refentrytitle>sd_bus_call_method</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+      <member><citerefentry><refentrytitle>sd_bus_call_method_async</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+      <member><citerefentry><refentrytitle>sd_bus_can_send</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+      <member><citerefentry><refentrytitle>sd_bus_close</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+      <member><citerefentry><refentrytitle>sd_bus_creds_get_pid</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+      <member><citerefentry><refentrytitle>sd_bus_creds_new_from_pid</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+      <member><citerefentry><refentrytitle>sd_bus_default</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+      <member><citerefentry><refentrytitle>sd_bus_emit_interfaces_added</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+      <member><citerefentry><refentrytitle>sd_bus_emit_interfaces_added_strv</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+      <member><citerefentry><refentrytitle>sd_bus_emit_interfaces_removed</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+      <member><citerefentry><refentrytitle>sd_bus_emit_interfaces_removed_strv</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+      <member><citerefentry><refentrytitle>sd_bus_emit_object_added</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+      <member><citerefentry><refentrytitle>sd_bus_emit_object_removed</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+      <member><citerefentry><refentrytitle>sd_bus_emit_properties_changed</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+      <member><citerefentry><refentrytitle>sd_bus_emit_properties_changed_strv</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+      <member><citerefentry><refentrytitle>sd_bus_emit_signal</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+      <member><citerefentry><refentrytitle>sd_bus_emit_signalv</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+      <member><citerefentry><refentrytitle>sd_bus_emit_signal_to</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+      <member><citerefentry><refentrytitle>sd_bus_emit_signal_tov</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+      <member><citerefentry><refentrytitle>sd-bus-errors</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+      <member><citerefentry><refentrytitle>sd_bus_error</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+      <member><citerefentry><refentrytitle>sd_bus_error_add_map</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+      <member><citerefentry><refentrytitle>sd_bus_get_address</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+      <member><citerefentry><refentrytitle>sd_bus_get_allow_interactive_authorization</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+      <member><citerefentry><refentrytitle>sd_bus_get_bus_id</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+      <member><citerefentry><refentrytitle>sd_bus_get_creds_mask</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+      <member><citerefentry><refentrytitle>sd_bus_get_current_handler</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+      <member><citerefentry><refentrytitle>sd_bus_get_current_message</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+      <member><citerefentry><refentrytitle>sd_bus_get_current_slot</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+      <member><citerefentry><refentrytitle>sd_bus_get_current_userdata</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+      <member><citerefentry><refentrytitle>sd_bus_get_exit_on_disconnect</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+      <member><citerefentry><refentrytitle>sd_bus_get_fd</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+      <member><citerefentry><refentrytitle>sd_bus_get_method_call_timeout</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+      <member><citerefentry><refentrytitle>sd_bus_get_n_queued_read</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+      <member><citerefentry><refentrytitle>sd_bus_get_name_creds</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+      <member><citerefentry><refentrytitle>sd_bus_get_name_machine_id</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+      <member><citerefentry><refentrytitle>sd_bus_get_owner_creds</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+      <member><citerefentry><refentrytitle>sd_bus_get_property</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+      <member><citerefentry><refentrytitle>sd_bus_get_property_string</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+      <member><citerefentry><refentrytitle>sd_bus_get_property_strv</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+      <member><citerefentry><refentrytitle>sd_bus_get_property_trivial</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+      <member><citerefentry><refentrytitle>sd_bus_get_scope</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+      <member><citerefentry><refentrytitle>sd_bus_get_tid</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+      <member><citerefentry><refentrytitle>sd_bus_get_unique_name</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+      <member><citerefentry><refentrytitle>sd_bus_interface_name_is_valid</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+      <member><citerefentry><refentrytitle>sd_bus_is_bus_client</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+      <member><citerefentry><refentrytitle>sd_bus_is_monitor</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+      <member><citerefentry><refentrytitle>sd_bus_is_server</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+      <member><citerefentry><refentrytitle>sd_bus_list_names</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+      <member><citerefentry><refentrytitle>sd_bus_message_append</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+      <member><citerefentry><refentrytitle>sd_bus_message_append_array</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+      <member><citerefentry><refentrytitle>sd_bus_message_append_basic</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+      <member><citerefentry><refentrytitle>sd_bus_message_append_string_memfd</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+      <member><citerefentry><refentrytitle>sd_bus_message_append_strv</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+      <member><citerefentry><refentrytitle>sd_bus_message_at_end</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+      <member><citerefentry><refentrytitle>sd_bus_message_close_container</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+      <member><citerefentry><refentrytitle>sd_bus_message_copy</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+      <member><citerefentry><refentrytitle>sd_bus_message_dump</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+      <member><citerefentry><refentrytitle>sd_bus_message_enter_container</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+      <member><citerefentry><refentrytitle>sd_bus_message_exit_container</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+      <member><citerefentry><refentrytitle>sd_bus_message_get_allow_interactive_authorization</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+      <member><citerefentry><refentrytitle>sd_bus_message_get_cookie</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+      <member><citerefentry><refentrytitle>sd_bus_message_get_creds</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+      <member><citerefentry><refentrytitle>sd_bus_message_get_errno</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+      <member><citerefentry><refentrytitle>sd_bus_message_get_error</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+      <member><citerefentry><refentrytitle>sd_bus_message_get_monotonic_usec</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+      <member><citerefentry><refentrytitle>sd_bus_message_get_sender</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+      <member><citerefentry><refentrytitle>sd_bus_message_get_signature</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+      <member><citerefentry><refentrytitle>sd_bus_message_get_type</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+      <member><citerefentry><refentrytitle>sd_bus_message_new</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+      <member><citerefentry><refentrytitle>sd_bus_message_new_method_call</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+      <member><citerefentry><refentrytitle>sd_bus_message_new_method_error</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+      <member><citerefentry><refentrytitle>sd_bus_message_new_signal</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+      <member><citerefentry><refentrytitle>sd_bus_message_new_signal_to</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+      <member><citerefentry><refentrytitle>sd_bus_message_open_container</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+      <member><citerefentry><refentrytitle>sd_bus_message_peek_type</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+      <member><citerefentry><refentrytitle>sd_bus_message_read</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+      <member><citerefentry><refentrytitle>sd_bus_message_read_array</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+      <member><citerefentry><refentrytitle>sd_bus_message_read_basic</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+      <member><citerefentry><refentrytitle>sd_bus_message_read_strv</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+      <member><citerefentry><refentrytitle>sd_bus_message_rewind</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+      <member><citerefentry><refentrytitle>sd_bus_message_seal</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+      <member><citerefentry><refentrytitle>sd_bus_message_send</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+      <member><citerefentry><refentrytitle>sd_bus_message_set_allow_interactive_authorization</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+      <member><citerefentry><refentrytitle>sd_bus_message_set_destination</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+      <member><citerefentry><refentrytitle>sd_bus_message_set_expect_reply</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+      <member><citerefentry><refentrytitle>sd_bus_message_set_sender</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+      <member><citerefentry><refentrytitle>sd_bus_message_skip</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+      <member><citerefentry><refentrytitle>sd_bus_message_verify_type</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+      <member><citerefentry><refentrytitle>sd_bus_negotiate_fds</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+      <member><citerefentry><refentrytitle>sd_bus_new</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+      <member><citerefentry><refentrytitle>sd_bus_path_encode</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+      <member><citerefentry><refentrytitle>sd_bus_process</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+      <member><citerefentry><refentrytitle>sd_bus_query_sender_creds</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+      <member><citerefentry><refentrytitle>sd_bus_query_sender_privilege</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+      <member><citerefentry><refentrytitle>sd_bus_reply_method_error</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+      <member><citerefentry><refentrytitle>sd_bus_reply_method_return</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+      <member><citerefentry><refentrytitle>sd_bus_request_name</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+      <member><citerefentry><refentrytitle>sd_bus_send</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+      <member><citerefentry><refentrytitle>sd_bus_send_to</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+      <member><citerefentry><refentrytitle>sd_bus_set_address</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+      <member><citerefentry><refentrytitle>sd_bus_set_allow_interactive_authorization</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+      <member><citerefentry><refentrytitle>sd_bus_set_bus_client</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+      <member><citerefentry><refentrytitle>sd_bus_set_close_on_exit</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+      <member><citerefentry><refentrytitle>sd_bus_set_connected_signal</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+      <member><citerefentry><refentrytitle>sd_bus_set_description</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+      <member><citerefentry><refentrytitle>sd_bus_set_exit_on_disconnect</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+      <member><citerefentry><refentrytitle>sd_bus_set_method_call_timeout</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+      <member><citerefentry><refentrytitle>sd_bus_set_monitor</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+      <member><citerefentry><refentrytitle>sd_bus_set_property</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+      <member><citerefentry><refentrytitle>sd_bus_set_propertyv</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+      <member><citerefentry><refentrytitle>sd_bus_set_sender</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+      <member><citerefentry><refentrytitle>sd_bus_set_server</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+      <member><citerefentry><refentrytitle>sd_bus_set_watch_bind</refentrytitle><manvolnum>3</manvolnum></citerefentry></member>
+      <member><citerefentry><refentrytitle>sd_bus_slot_get_current_handler</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+      <member><citerefentry><refentrytitle>sd_bus_slot_get_current_message</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+      <member><citerefentry><refentrytitle>sd_bus_slot_get_current_userdata</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+      <member><citerefentry><refentrytitle>sd_bus_slot_set_description</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+      <member><citerefentry><refentrytitle>sd_bus_slot_set_destroy_callback</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+      <member><citerefentry><refentrytitle>sd_bus_slot_set_floating</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+      <member><citerefentry><refentrytitle>sd_bus_slot_set_userdata</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+      <member><citerefentry><refentrytitle>sd_bus_start</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+      <member><citerefentry><refentrytitle>sd_bus_track_add_name</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+      <member><citerefentry><refentrytitle>sd_bus_track_new</refentrytitle><manvolnum>3</manvolnum></citerefentry></member>
+    </simplelist>
     for more information about the functions available.</para>
   </refsect1>
 
index 4950781f84b6828c39c543ee6ca4fbdcaf69c23b..a22db76e04c1a52677015cc9adb020b38ca678b7 100644 (file)
     <filename>libudev.h</filename>.</para>
 
     <para>See
-    <literallayout><citerefentry><refentrytitle>sd_device_get_syspath</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_device_ref</refentrytitle><manvolnum>3</manvolnum></citerefentry>
-</literallayout>
+    <simplelist>
+      <member><citerefentry><refentrytitle>sd_device_get_syspath</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+      <member><citerefentry><refentrytitle>sd_device_ref</refentrytitle><manvolnum>3</manvolnum></citerefentry></member>
+    </simplelist>
     for more information about the functions available.</para>
   </refsect1>
 
index ea7cf4c085c6c11603822057e393162c081cb504..18d17bd9356961553bec1256c29bb35adff64d75 100644 (file)
@@ -37,7 +37,7 @@
   </refsect1>
 
   <refsect1>
-    <title>Core OS Command Line Arguments</title>
+    <title>Strings</title>
 
     <para>The following strings are supported:</para>
 
@@ -52,7 +52,7 @@
         <ulink url="https://systemd.io/CREDENTIALS">System and Service Credentials</ulink> for
         details.</para>
 
-        <xi:include href="version-info.xml" xpointer="v254"/></listitem>
+        <xi:include href="version-info.xml" xpointer="v252"/></listitem>
       </varlistentry>
 
       <varlistentry>
index 4d810e6c0c47f4d2655fe9777cc999da68a6859b..116ae9b3142a16beb9184dc31f819a02e4f3385f 100644 (file)
   </refnamediv>
 
   <refsynopsisdiv>
-    <para><filename>/etc/sysctl.d/*.conf</filename></para>
-    <para><filename>/run/sysctl.d/*.conf</filename></para>
-    <para><filename>/usr/lib/sysctl.d/*.conf</filename></para>
+    <para><simplelist>
+      <member><filename>/etc/sysctl.d/*.conf</filename></member>
+      <member><filename>/run/sysctl.d/*.conf</filename></member>
+      <member><filename>/usr/lib/sysctl.d/*.conf</filename></member>
+    </simplelist></para>
 
     <programlisting>key.name.under.proc.sys = some value
 key/name/under/proc/sys = some value
index 21447e8ed9afed8dde4fc3809c26ffcc220ef844..1882cd756cadcefc09a6c492792f3f5113096c56 100644 (file)
@@ -1855,7 +1855,7 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
       for more details. The patterns are matched against the primary names of
       units currently in memory, and patterns which do not match anything
       are silently skipped. For example:
-      <programlisting># systemctl stop sshd@*.service</programlisting>
+      <programlisting># systemctl stop "sshd@*.service"</programlisting>
       will stop all <filename>sshd@.service</filename> instances. Note that alias names of units, and units that aren't
       in memory are not considered for glob expansion.
       </para>
@@ -2607,9 +2607,10 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
         <term><option>--firmware-setup</option></term>
 
         <listitem>
-          <para>When used with the <command>reboot</command> command, indicate to the system's firmware to
-          reboot into the firmware setup interface. Note that this functionality is not available on all
-          systems.</para>
+          <para>When used with the <command>reboot</command>, <command>poweroff</command>, or
+          <command>halt</command> command, indicate to the system's firmware to reboot into the firmware
+          setup interface for the next boot. Note that this functionality is not available on all systems.
+          </para>
 
           <xi:include href="version-info.xml" xpointer="v220"/>
         </listitem>
@@ -2619,10 +2620,11 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
         <term><option>--boot-loader-menu=<replaceable>timeout</replaceable></option></term>
 
         <listitem>
-          <para>When used with the <command>reboot</command> command, indicate to the system's boot loader to
-          show the boot loader menu on the following boot. Takes a time value as parameter — indicating the
-          menu timeout. Pass zero in order to disable the menu timeout. Note that not all boot loaders
-          support this functionality.</para>
+          <para>When used with the <command>reboot</command>, <command>poweroff</command>, or
+          <command>halt</command> command, indicate to the system's boot loader to show the boot loader menu
+          on the following boot. Takes a time value as parameter — indicating the menu timeout. Pass zero
+          in order to disable the menu timeout. Note that not all boot loaders support this functionality.
+          </para>
 
           <xi:include href="version-info.xml" xpointer="v242"/>
         </listitem>
@@ -2632,10 +2634,11 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
         <term><option>--boot-loader-entry=<replaceable>ID</replaceable></option></term>
 
         <listitem>
-          <para>When used with the <command>reboot</command> command, indicate to the system's boot loader to
-          boot into a specific boot loader entry on the following boot. Takes a boot loader entry identifier
-          as argument, or <literal>help</literal> in order to list available entries. Note that not all boot
-          loaders support this functionality.</para>
+          <para>When used with the <command>reboot</command>, <command>poweroff</command>, or
+          <command>halt</command> command, indicate to the system's boot loader to boot into a specific
+          boot loader entry on the following boot. Takes a boot loader entry identifier as argument,
+          or <literal>help</literal> in order to list available entries. Note that not all boot loaders
+          support this functionality.</para>
 
           <xi:include href="version-info.xml" xpointer="v242"/>
         </listitem>
index 03b7317cd3506c187b3ff5b6cf4d89796def4ad8..90ce8cfbccd0e1cd45fed5ea341b8e66786cb83a 100644 (file)
   </refnamediv>
 
   <refsynopsisdiv>
-    <para><filename>systemd-ask-password-console.service</filename></para>
-    <para><filename>systemd-ask-password-console.path</filename></para>
-    <para><filename>systemd-ask-password-wall.service</filename></para>
-    <para><filename>systemd-ask-password-wall.path</filename></para>
+    <para><simplelist>
+      <member><filename>systemd-ask-password-console.service</filename></member>
+      <member><filename>systemd-ask-password-console.path</filename></member>
+      <member><filename>systemd-ask-password-wall.service</filename></member>
+      <member><filename>systemd-ask-password-wall.path</filename></member>
+    </simplelist></para>
   </refsynopsisdiv>
 
   <refsect1>
index 8fd885cb26427721ee8446d8e11b370e4d38f014..40e07ce24d9007db5bf82e59581cbc786023031a 100644 (file)
@@ -36,8 +36,8 @@
     supports tokens and credentials of the following kind to be enrolled:</para>
 
     <orderedlist>
-      <listitem><para>PKCS#11 security tokens and smartcards that may carry an RSA key pair (e.g. various
-      YubiKeys)</para></listitem>
+      <listitem><para>PKCS#11 security tokens and smartcards that may carry an RSA or EC key pair (e.g.
+      various YubiKeys)</para></listitem>
 
       <listitem><para>FIDO2 security tokens that implement the <literal>hmac-secret</literal> extension (most
       FIDO2 keys, including YubiKeys)</para></listitem>
         smartcard URI referring to the token. Alternatively the special value <literal>auto</literal> may
         be specified, in order to automatically determine the URI of a currently plugged in security token
         (of which there must be exactly one). The special value <literal>list</literal> may be used to
-        enumerate all suitable PKCS#11 tokens currently plugged in. The security token must contain an RSA
-        key pair which is used to encrypt the randomly generated key that is used to unlock the LUKS2
-        volume. The encrypted key is then stored in the LUKS2 JSON token header area.</para>
+        enumerate all suitable PKCS#11 tokens currently plugged in.</para>
+
+        <para>The PKCS#11 token must contain an RSA or EC key pair which will be used to unlock a LUKS2 volume.
+        For RSA, a randomly generated volume key is encrypted with a public key in the token, and stored in
+        the LUKS2 JSON token header area. To unlock a volume, the stored encrypted volume key will be decrypted
+        with a private key in the token. For ECC, ECDH algorithm is used: we generate a pair of EC keys in
+        the same EC group, then derive a shared secret using the generated private key and the public key
+        in the token. The derived shared secret is used as a volume key. The generated public key is
+        stored in the LUKS2 JSON token header area. The generated private key is erased. To unlock a volume,
+        we derive the shared secret with the stored public key and a private key in the token.</para>
 
         <para>In order to unlock a LUKS2 volume with an enrolled PKCS#11 security token, specify the
         <option>pkcs11-uri=</option> option in the respective <filename>/etc/crypttab</filename> line:</para>
index 78c661bfad87be36cec91bccadf4828d097f6c60..ae3574e228028d44a79c15598704b59e93593cd7 100644 (file)
   </refnamediv>
 
   <refsynopsisdiv>
-    <para><filename>systemd-fsck@.service</filename></para>
-    <para><filename>systemd-fsck-root.service</filename></para>
-    <para><filename>systemd-fsck-usr.service</filename></para>
-    <para><filename>/usr/lib/systemd/systemd-fsck</filename></para>
+    <para><simplelist>
+      <member><filename>systemd-fsck@.service</filename></member>
+      <member><filename>systemd-fsck-root.service</filename></member>
+      <member><filename>systemd-fsck-usr.service</filename></member>
+      <member><filename>/usr/lib/systemd/systemd-fsck</filename></member>
+    </simplelist></para>
   </refsynopsisdiv>
 
   <refsect1>
index 31435b28658fbc3c7772f5a8b2fb445a0fa4d0ef..48d1afcaf87fd2463e5c1e13ec0866110a2f3afc 100644 (file)
   </refnamediv>
 
   <refsynopsisdiv>
-    <para><filename>systemd-journald.service</filename></para>
-    <para><filename>systemd-journald.socket</filename></para>
-    <para><filename>systemd-journald-dev-log.socket</filename></para>
-    <para><filename>systemd-journald-audit.socket</filename></para>
-    <para><filename>systemd-journald@.service</filename></para>
-    <para><filename>systemd-journald@.socket</filename></para>
-    <para><filename>systemd-journald-varlink@.socket</filename></para>
-    <para><filename>/usr/lib/systemd/systemd-journald</filename></para>
+    <para><simplelist>
+      <member><filename>systemd-journald.service</filename></member>
+      <member><filename>systemd-journald.socket</filename></member>
+      <member><filename>systemd-journald-dev-log.socket</filename></member>
+      <member><filename>systemd-journald-audit.socket</filename></member>
+      <member><filename>systemd-journald@.service</filename></member>
+      <member><filename>systemd-journald@.socket</filename></member>
+      <member><filename>systemd-journald-varlink@.socket</filename></member>
+      <member><filename>/usr/lib/systemd/systemd-journald</filename></member>
+    </simplelist></para>
   </refsynopsisdiv>
 
   <refsect1>
index 5be9823c373de5143472db3bbd15ac5e9880f190..e6cf2a3bcb0bca82a1ae07b412f5c6a6093a8b77 100644 (file)
         <term><option>--slice-inherit</option></term>
 
         <listitem><para>Make the new <filename>.service</filename> or <filename>.scope</filename> unit part
-        of the inherited slice. This option can be combined with <option>--slice=</option>.</para>
-
-        <para>An inherited slice is located within <command>systemd-run</command> slice. Example: if
-        <command>systemd-run</command> slice is <filename>foo.slice</filename>, and the
-        <option>--slice=</option> argument is <filename>bar</filename>, the unit will be placed under the
+        of the slice the <command>systemd-run</command> itself has been invoked in. This option may be
+        combined with <option>--slice=</option>, in which case the slice specified via
+        <option>--slice=</option> is placed within the slice the <command>systemd-run</command> command is
+        invoked in.</para>
+
+        <para>Example: consider <command>systemd-run</command> being invoked in the slice
+        <filename>foo.slice</filename>, and the <option>--slice=</option> argument is
+        <filename>bar</filename>. The unit will then be placed under
         <filename>foo-bar.slice</filename>.</para>
 
         <xi:include href="version-info.xml" xpointer="v246"/>
         <xi:include href="version-info.xml" xpointer="v236"/></listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><option>--ignore-failure</option></term>
+
+        <listitem><para>By default, if the specified command fails the invoked unit will be marked failed
+        (though possibly still unloaded, see <option>--collect=</option>, above), and this is reported in the
+        logs. If this switch is specified this is suppressed and any non-success exit status/code of the
+        command is treated as success.</para>
+
+        <xi:include href="version-info.xml" xpointer="v256"/></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>--background=<replaceable>COLOR</replaceable></option></term>
+
+        <listitem><para>Change the terminal background color to the specified ANSI color as long as the
+        session lasts. The color specified should be an ANSI X3.64 SGR background color, i.e. strings such as
+        <literal>40</literal>, <literal>41</literal>, …, <literal>47</literal>, <literal>48;2;…</literal>,
+        <literal>48;5;…</literal>. See <ulink
+        url="https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_(Select_Graphic_Rendition)_parameters">ANSI
+        Escape Code (Wikipedia)</ulink> for details.</para>
+
+        <xi:include href="version-info.xml" xpointer="v256"/>
+        </listitem>
+      </varlistentry>
+
       <xi:include href="user-system-options.xml" xpointer="user" />
       <xi:include href="user-system-options.xml" xpointer="system" />
       <xi:include href="user-system-options.xml" xpointer="host" />
@@ -677,7 +705,8 @@ $ systemd-run --user --wait -p SuccessExitStatus=SIGUSR1 --expand-environment=no
       <citerefentry><refentrytitle>systemd.resource-control</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>systemd.timer</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>systemd-mount</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
-      <citerefentry><refentrytitle>machinectl</refentrytitle><manvolnum>1</manvolnum></citerefentry>
+      <citerefentry><refentrytitle>machinectl</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>uid0</refentrytitle><manvolnum>1</manvolnum></citerefentry>
     </para>
   </refsect1>
 
index ee13ce8703fdf7afdb414bdd5052d715352c1a12..9afa8b28bb3e24ea301097ef379f8e1db8c02093 100644 (file)
   </refnamediv>
 
   <refsynopsisdiv>
-    <para><filename>/etc/systemd/sleep.conf</filename></para>
-    <para><filename>/etc/systemd/sleep.conf.d/*.conf</filename></para>
-    <para><filename>/run/systemd/sleep.conf.d/*.conf</filename></para>
-    <para><filename>/usr/lib/systemd/sleep.conf.d/*.conf</filename></para>
+    <para><simplelist>
+      <member><filename>/etc/systemd/sleep.conf</filename></member>
+      <member><filename>/etc/systemd/sleep.conf.d/*.conf</filename></member>
+      <member><filename>/run/systemd/sleep.conf.d/*.conf</filename></member>
+      <member><filename>/usr/lib/systemd/sleep.conf.d/*.conf</filename></member>
+    </simplelist></para>
   </refsynopsisdiv>
 
   <refsect1>
index 6e853336c224fec713b5960b4a4d5f839ae6f342..385f1b74db12e8eee5512b242b026d14789dcfc0 100644 (file)
   </refnamediv>
 
   <refsynopsisdiv>
-    <para><filename>/usr/lib/systemd/boot/efi/linuxx64.efi.stub</filename></para>
-    <para><filename>/usr/lib/systemd/boot/efi/linuxia32.efi.stub</filename></para>
-    <para><filename>/usr/lib/systemd/boot/efi/linuxaa64.efi.stub</filename></para>
-    <para><filename><replaceable>ESP</replaceable>/.../<replaceable>foo</replaceable>.efi.extra.d/*.addon.efi</filename></para>
-    <para><filename><replaceable>ESP</replaceable>/.../<replaceable>foo</replaceable>.efi.extra.d/*.cred</filename></para>
-    <para><filename><replaceable>ESP</replaceable>/.../<replaceable>foo</replaceable>.efi.extra.d/*.raw</filename></para>
-    <para><filename><replaceable>ESP</replaceable>/loader/addons/*.addon.efi</filename></para>
-    <para><filename><replaceable>ESP</replaceable>/loader/credentials/*.cred</filename></para>
+    <para><simplelist>
+      <member><filename>/usr/lib/systemd/boot/efi/linuxx64.efi.stub</filename></member>
+      <member><filename>/usr/lib/systemd/boot/efi/linuxia32.efi.stub</filename></member>
+      <member><filename>/usr/lib/systemd/boot/efi/linuxaa64.efi.stub</filename></member>
+      <member><filename><replaceable>ESP</replaceable>/.../<replaceable>foo</replaceable>.efi.extra.d/*.addon.efi</filename></member>
+      <member><filename><replaceable>ESP</replaceable>/.../<replaceable>foo</replaceable>.efi.extra.d/*.cred</filename></member>
+      <member><filename><replaceable>ESP</replaceable>/.../<replaceable>foo</replaceable>.efi.extra.d/*.raw</filename></member>
+      <member><filename><replaceable>ESP</replaceable>/loader/addons/*.addon.efi</filename></member>
+      <member><filename><replaceable>ESP</replaceable>/loader/credentials/*.cred</filename></member>
+    </simplelist></para>
   </refsynopsisdiv>
 
   <refsect1>
index 02daecf33ba03df30840302f3a1b8d836fba4cb3..d15a3da21e2b02b33fa8ad6d4352e2856e658e01 100644 (file)
   </refnamediv>
 
   <refsynopsisdiv>
-    <para><filename>systemd-suspend.service</filename></para>
-    <para><filename>systemd-hibernate.service</filename></para>
-    <para><filename>systemd-hybrid-sleep.service</filename></para>
-    <para><filename>systemd-suspend-then-hibernate.service</filename></para>
-    <para><filename>/usr/lib/systemd/system-sleep</filename></para>
+    <para><simplelist>
+      <member><filename>systemd-suspend.service</filename></member>
+      <member><filename>systemd-hibernate.service</filename></member>
+      <member><filename>systemd-hybrid-sleep.service</filename></member>
+      <member><filename>systemd-suspend-then-hibernate.service</filename></member>
+      <member><filename>/usr/lib/systemd/system-sleep</filename></member>
+    </simplelist></para>
   </refsynopsisdiv>
 
   <refsect1>
index 76076931d5fd6a8452701178adb21a8b086dcd09..6180d1c4f57bc66cc6c708f2dbf3f1b67ec7d971 100644 (file)
@@ -31,7 +31,7 @@
       <arg choice="plain">COMMAND</arg>
     </cmdsynopsis>
 
-    <para><literallayout><filename>systemd-sysext.service</filename></literallayout></para>
+    <para><filename>systemd-sysext.service</filename></para>
 
     <cmdsynopsis>
       <command>systemd-confext</command>
@@ -39,8 +39,7 @@
       <arg choice="plain">COMMAND</arg>
     </cmdsynopsis>
 
-    <para><literallayout><filename>systemd-confext.service</filename></literallayout></para>
-
+    <para><filename>systemd-confext.service</filename></para>
   </refsynopsisdiv>
 
   <refsect1>
index 95e1e2951c8b1854e99c3c51d5d4670288ab01a9..28ac36398837f26b69eb2fea9e06621995a66bc9 100644 (file)
     </cmdsynopsis>
 
     <para>System units:
-<literallayout><filename>systemd-tmpfiles-setup.service</filename>
-<filename>systemd-tmpfiles-setup-dev-early.service</filename>
-<filename>systemd-tmpfiles-setup-dev.service</filename>
-<filename>systemd-tmpfiles-clean.service</filename>
-<filename>systemd-tmpfiles-clean.timer</filename></literallayout></para>
+    <simplelist>
+      <member><filename>systemd-tmpfiles-setup.service</filename></member>
+      <member><filename>systemd-tmpfiles-setup-dev-early.service</filename></member>
+      <member><filename>systemd-tmpfiles-setup-dev.service</filename></member>
+      <member><filename>systemd-tmpfiles-clean.service</filename></member>
+      <member><filename>systemd-tmpfiles-clean.timer</filename></member>
+    </simplelist>
+    </para>
 
     <para>User units:
-<literallayout><filename>systemd-tmpfiles-setup.service</filename>
-<filename>systemd-tmpfiles-clean.service</filename>
-<filename>systemd-tmpfiles-clean.timer</filename></literallayout></para>
+    <simplelist>
+      <member><filename>systemd-tmpfiles-setup.service</filename></member>
+      <member><filename>systemd-tmpfiles-clean.service</filename></member>
+      <member><filename>systemd-tmpfiles-clean.timer</filename></member>
+    </simplelist>
+    </para>
   </refsynopsisdiv>
 
   <refsect1>
index 856f6a641b1d4a9356be77c9adb3889d55d48db3..151c6ace43c2cbc90de435678216916d8b2b384a 100644 (file)
       <command>&USER_ENV_GENERATOR_DIR;/some-generator</command>
     </cmdsynopsis>
 
-    <para>
-      <literallayout><filename>/run/systemd/system-environment-generators/*</filename>
-<filename>/etc/systemd/system-environment-generators/*</filename>
-<filename>/usr/local/lib/systemd/system-environment-generators/*</filename>
-<filename>&SYSTEM_ENV_GENERATOR_DIR;/*</filename></literallayout>
-    </para>
-
-    <para>
-      <literallayout><filename>/run/systemd/user-environment-generators/*</filename>
-<filename>/etc/systemd/user-environment-generators/*</filename>
-<filename>/usr/local/lib/systemd/user-environment-generators/*</filename>
-<filename>&USER_ENV_GENERATOR_DIR;/*</filename></literallayout>
-    </para>
+    <para><simplelist>
+      <member><filename>/run/systemd/system-environment-generators/*</filename></member>
+      <member><filename>/etc/systemd/system-environment-generators/*</filename></member>
+      <member><filename>/usr/local/lib/systemd/system-environment-generators/*</filename></member>
+      <member><filename>&SYSTEM_ENV_GENERATOR_DIR;/*</filename></member>
+    </simplelist></para>
+
+    <para><simplelist>
+      <member><filename>/run/systemd/user-environment-generators/*</filename></member>
+      <member><filename>/etc/systemd/user-environment-generators/*</filename></member>
+      <member><filename>/usr/local/lib/systemd/user-environment-generators/*</filename></member>
+      <member><filename>&USER_ENV_GENERATOR_DIR;/*</filename></member>
+    </simplelist></para>
   </refsynopsisdiv>
 
   <refsect1>
index 525303c6ebfbde904332ea6009464c8c254f60a5..37fa6e4f3c10d262cb41777fd6fa05f347475454 100644 (file)
       <varlistentry>
         <term><varname>SetLoginEnvironment=</varname></term>
 
-        <listitem><para>Takes a boolean parameter that controls whether to set <varname>$HOME</varname>,
-        <varname>$LOGNAME</varname>, and <varname>$SHELL</varname> environment variables. If unset, this is
-        controlled by whether <varname>User=</varname> is set. If true, they will always be set for system services,
-        i.e. even when the default user <literal>root</literal> is used. If false, the mentioned variables are not set
-        by systemd, no matter whether <varname>User=</varname> is used or not. This option normally has no effect
-        on user services, since these variables are typically inherited from user manager's own environment anyway.</para>
+        <listitem><para>Takes a boolean parameter that controls whether to set the <varname>$HOME</varname>,
+        <varname>$LOGNAME</varname>, and <varname>$SHELL</varname> environment variables. If not set, this
+        defaults to true if <varname>User=</varname>, <varname>DynamicUser=</varname> or
+        <varname>PAMName=</varname> are set, false otherwise. If set to true, the variables will always be
+        set for system services, i.e. even when the default user <literal>root</literal> is used. If set to
+        false, the mentioned variables are not set by the service manager, no matter whether
+        <varname>User=</varname>, <varname>DynamicUser=</varname>, or <varname>PAMName=</varname> are used or
+        not. This option normally has no effect on services of the per-user service manager, since in that
+        case these variables are typically inherited from user manager's own environment anyway.</para>
 
         <xi:include href="version-info.xml" xpointer="v255"/></listitem>
       </varlistentry>
index b216ef96d0ee442e9eb3d0a743a5399fd0f4ac15..06fe7ec9d9e250f9c2f31fffc8355be6a11a886f 100644 (file)
       <arg choice="option"><replaceable>late-dir</replaceable></arg>
     </cmdsynopsis>
 
-    <para>
-      <literallayout><filename>/run/systemd/system-generators/*</filename>
-<filename>/etc/systemd/system-generators/*</filename>
-<filename>/usr/local/lib/systemd/system-generators/*</filename>
-<filename>&SYSTEM_GENERATOR_DIR;/*</filename></literallayout>
-    </para>
-
-    <para>
-      <literallayout><filename>/run/systemd/user-generators/*</filename>
-<filename>/etc/systemd/user-generators/*</filename>
-<filename>/usr/local/lib/systemd/user-generators/*</filename>
-<filename>&USER_GENERATOR_DIR;/*</filename></literallayout>
-    </para>
+    <para><simplelist>
+      <member><filename>/run/systemd/system-generators/*</filename></member>
+      <member><filename>/etc/systemd/system-generators/*</filename></member>
+      <member><filename>/usr/local/lib/systemd/system-generators/*</filename></member>
+      <member><filename>&SYSTEM_GENERATOR_DIR;/*</filename></member>
+    </simplelist></para>
+
+    <para><simplelist>
+      <member><filename>/run/systemd/user-generators/*</filename></member>
+      <member><filename>/etc/systemd/user-generators/*</filename></member>
+      <member><filename>/usr/local/lib/systemd/user-generators/*</filename></member>
+      <member><filename>&USER_GENERATOR_DIR;/*</filename></member>
+    </simplelist></para>
   </refsynopsisdiv>
 
   <refsect1>
index e9c00c935dc9030dc5652c654ce0327dea986d3f..e2e8a0919a38dda38232c3266956a73ebfa35ddf 100644 (file)
     particular version of systemd).</para>
   </refsect1>
 
+  <refsect1>
+    <title>Limiting the use of specific sysfs attributes</title>
+
+    <para>When creating names for network cards, some naming schemes use data from sysfs populated
+    by the kernel. This means that although a specific naming scheme in udev is picked,
+    the network card's name can still change when a new kernel version adds a new sysfs attribute.
+    For example if kernel starts setting the <constant>phys_port_name</constant>, udev will append the
+    "<constant>n</constant><replaceable>phys_port_name</replaceable>" suffix to the device name.</para>
+
+    <variablelist>
+      <varlistentry>
+        <term><varname>ID_NET_NAME_ALLOW=<replaceable>BOOL</replaceable></varname></term>
+
+        <listitem><para>This udev property sets a fallback policy for reading a sysfs attribute.
+        If set to <constant>0</constant> udev will not read any sysfs attribute by default, unless it is
+        explicitly allowlisted, see below. If set to <constant>1</constant> udev can use any sysfs attribute
+        unless it is explicitly forbidden. The default value is <constant>1</constant>.</para>
+
+        <xi:include href="version-info.xml" xpointer="v256"/>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>ID_NET_NAME_ALLOW_<replaceable>sysfsattr</replaceable>=<replaceable>BOOL</replaceable></varname></term>
+
+        <listitem><para>This udev property explicitly states if udev shall use the specified
+        <replaceable>sysfsattr</replaceable>, when composing the device name.</para>
+
+        <xi:include href="version-info.xml" xpointer="v256"/>
+        </listitem>
+      </varlistentry>
+    </variablelist>
+
+    <para>With these options, users can set an allowlist or denylist for sysfs attributes. To create
+    an allowlist, the user needs to set <varname>ID_NET_NAME_ALLOW=0</varname> for the device and then list
+    the allowed attributes with the
+    <varname>ID_NET_NAME_ALLOW_<replaceable>sysfsattr</replaceable>=1</varname>
+    options. In case of a denylist, the user needs to provide the list of denied attributes with
+    the <varname>ID_NET_NAME_ALLOW_<replaceable>sysfsattr</replaceable>=0</varname> options.</para>
+  </refsect1>
+
   <refsect1>
     <title>Examples</title>
 
@@ -617,6 +658,36 @@ ID_NET_NAME_PATH=enp0s29u1u2</programlisting>
 ID_NET_NAME_MAC=enx026d3c00000a
 ID_NET_NAME_PATH=encf5f0</programlisting>
     </example>
+
+    <example>
+      <title>Set an allowlist for reading sysfs attributes for network card naming</title>
+
+      <programlisting><filename>/etc/udev/hwdb.d/50-net-naming-allowlist.hwdb</filename>
+net:naming:drvirtio_net:*
+  ID_NET_NAME_ALLOW=0
+  ID_NET_NAME_ALLOW_ACPI_INDEX=1
+  ID_NET_NAME_ALLOW_ADDR_ASSIGN_TYPE=1
+  ID_NET_NAME_ALLOW_ADDRESS=1
+  ID_NET_NAME_ALLOW_ARI_ENABLED=1
+  ID_NET_NAME_ALLOW_DEV_PORT=1
+  ID_NET_NAME_ALLOW_FUNCTION_ID=1
+  ID_NET_NAME_ALLOW_IFLINK=1
+  ID_NET_NAME_ALLOW_INDEX=1
+  ID_NET_NAME_ALLOW_LABEL=1
+  ID_NET_NAME_ALLOW_PHYS_PORT_NAME=1
+  ID_NET_NAME_ALLOW_TYPE=1</programlisting>
+    </example>
+
+    <example>
+      <title>Set a denylist so that specified sysfs attribute are ignored</title>
+
+      <programlisting><filename>/etc/udev/hwdb.d/50-net-naming-denylist.hwdb</filename>
+net:naming:drvirtio_net:*
+  ID_NET_NAME_ALLOW=1
+  ID_NET_NAME_ALLOW_DEV_PORT=0
+  ID_NET_NAME_ALLOW_PHYS_PORT_NAME=0
+      </programlisting>
+    </example>
   </refsect1>
 
   <refsect1>
index 3436a32b115822fc1a9ba38da75dd9d9c166676b..7dc447d0a7f84b690bf4a3044edb7bac0679e7a7 100644 (file)
@@ -1715,8 +1715,10 @@ NFTSet=prefix:netdev:filter:eth_ipv4_prefix</programlisting>
       <varlistentry>
         <term><varname>Id=</varname></term>
         <listitem>
-          <para>The id of the next hop. Takes an integer in the range 1…4294967295. If unspecified,
-          then automatically chosen by kernel.</para>
+          <para>The id of the next hop. Takes an integer in the range 1…4294967295.
+          This is mandatory if <varname>ManageForeignNextHops=no</varname> is specified in
+          <citerefentry><refentrytitle>networkd.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
+          Otherwise, if unspecified, an unused ID will be automatically picked.</para>
 
           <xi:include href="version-info.xml" xpointer="v244"/>
         </listitem>
@@ -3976,9 +3978,9 @@ ServerAddress=192.168.0.1/24</programlisting>
       </varlistentry>
 
     </variablelist>
-    </refsect1>
+  </refsect1>
 
-    <refsect1>
+  <refsect1>
     <title>[IPv6Prefix] Section Options</title>
     <para>One or more [IPv6Prefix] sections contain the IPv6 prefixes that are announced via Router
     Advertisements. See <ulink url="https://tools.ietf.org/html/rfc4861">RFC 4861</ulink> for further
@@ -4053,9 +4055,9 @@ ServerAddress=192.168.0.1/24</programlisting>
         </listitem>
       </varlistentry>
     </variablelist>
-    </refsect1>
+  </refsect1>
 
-    <refsect1>
+  <refsect1>
     <title>[IPv6RoutePrefix] Section Options</title>
     <para>One or more [IPv6RoutePrefix] sections contain the IPv6
     prefix routes that are announced via Router Advertisements. See
@@ -4085,9 +4087,9 @@ ServerAddress=192.168.0.1/24</programlisting>
       </varlistentry>
 
     </variablelist>
-    </refsect1>
+  </refsect1>
 
-    <refsect1>
+  <refsect1>
     <title>[IPv6PREF64Prefix] Section Options</title>
     <para>One or more [IPv6PREF64Prefix] sections contain the IPv6 PREF64 (or NAT64) prefixes that are announced via Router
     Advertisements. See <ulink url="https://tools.ietf.org/html/rfc8781">RFC 8781</ulink> for further
@@ -4114,480 +4116,486 @@ ServerAddress=192.168.0.1/24</programlisting>
         <xi:include href="version-info.xml" xpointer="v255"/></listitem>
       </varlistentry>
     </variablelist>
-    </refsect1>
+  </refsect1>
 
-    <refsect1>
+  <refsect1>
     <title>[Bridge] Section Options</title>
-      <para>The [Bridge] section accepts the following keys:</para>
-      <variablelist class='network-directives'>
-        <varlistentry>
-          <term><varname>UnicastFlood=</varname></term>
-          <listitem>
-            <para>Takes a boolean. Controls whether the bridge should flood
-            traffic for which an FDB entry is missing and the destination
-            is unknown through this port. When unset, the kernel's default will be used.
-            </para>
-
-            <xi:include href="version-info.xml" xpointer="v223"/>
-          </listitem>
-        </varlistentry>
-        <varlistentry>
-          <term><varname>MulticastFlood=</varname></term>
-          <listitem>
-            <para>Takes a boolean. Controls whether the bridge should flood
-            traffic for which an MDB entry is missing and the destination
-            is unknown through this port. When unset, the kernel's default will be used.
-            </para>
-
-            <xi:include href="version-info.xml" xpointer="v242"/>
-          </listitem>
-        </varlistentry>
-        <varlistentry>
-          <term><varname>MulticastToUnicast=</varname></term>
-          <listitem>
-            <para>Takes a boolean. Multicast to unicast works on top of the multicast snooping feature of
-            the bridge. Which means unicast copies are only delivered to hosts which are interested in it.
-            When unset, the kernel's default will be used.
-            </para>
-
-            <xi:include href="version-info.xml" xpointer="v240"/>
-          </listitem>
-        </varlistentry>
-        <varlistentry>
-          <term><varname>NeighborSuppression=</varname></term>
-          <listitem>
-            <para>Takes a boolean. Configures whether ARP and ND neighbor suppression is enabled for
-            this port. When unset, the kernel's default will be used.
-            </para>
-
-            <xi:include href="version-info.xml" xpointer="v242"/>
-          </listitem>
-        </varlistentry>
-        <varlistentry>
-          <term><varname>Learning=</varname></term>
-          <listitem>
-            <para>Takes a boolean. Configures whether MAC address learning is enabled for
-            this port. When unset, the kernel's default will be used.
-            </para>
-
-            <xi:include href="version-info.xml" xpointer="v242"/>
-          </listitem>
-        </varlistentry>
-        <varlistentry>
-          <term><varname>HairPin=</varname></term>
-          <listitem>
-            <para>Takes a boolean. Configures whether traffic may be sent back out of the port on which it
-            was received. When this flag is false, then the bridge will not forward traffic back out of the
-            receiving port. When unset, the kernel's default will be used.</para>
-
-            <xi:include href="version-info.xml" xpointer="v223"/>
-          </listitem>
-        </varlistentry>
-        <varlistentry>
-          <term><varname>Isolated=</varname></term>
-          <listitem>
-            <para>Takes a boolean. Configures whether this port is isolated or not. Within a bridge,
-            isolated ports can only communicate with non-isolated ports. When set to true, this port can only
-            communicate with other ports whose Isolated setting is false.  When set to false, this port
-            can communicate with any other ports. When unset, the kernel's default will be used.</para>
-
-            <xi:include href="version-info.xml" xpointer="v251"/>
-          </listitem>
-        </varlistentry>
-        <varlistentry>
-          <term><varname>UseBPDU=</varname></term>
-          <listitem>
-            <para>Takes a boolean. Configures whether STP Bridge Protocol Data Units will be
-            processed by the bridge port. When unset, the kernel's default will be used.</para>
-
-            <xi:include href="version-info.xml" xpointer="v223"/>
-          </listitem>
-        </varlistentry>
-        <varlistentry>
-          <term><varname>FastLeave=</varname></term>
-          <listitem>
-            <para>Takes a boolean. This flag allows the bridge to immediately stop multicast
-            traffic on a port that receives an IGMP Leave message. It is only used with
-            IGMP snooping if enabled on the bridge. When unset, the kernel's default will be used.</para>
-
-            <xi:include href="version-info.xml" xpointer="v223"/>
-          </listitem>
-        </varlistentry>
-        <varlistentry>
-          <term><varname>AllowPortToBeRoot=</varname></term>
-          <listitem>
-            <para>Takes a boolean. Configures whether a given port is allowed to
-            become a root port. Only used when STP is enabled on the bridge.
-            When unset, the kernel's default will be used.</para>
-
-            <xi:include href="version-info.xml" xpointer="v223"/>
-          </listitem>
-        </varlistentry>
-        <varlistentry>
-          <term><varname>ProxyARP=</varname></term>
-          <listitem>
-            <para>Takes a boolean. Configures whether proxy ARP to be enabled on this port.
-            When unset, the kernel's default will be used.</para>
-
-            <xi:include href="version-info.xml" xpointer="v243"/>
-          </listitem>
-        </varlistentry>
-        <varlistentry>
-          <term><varname>ProxyARPWiFi=</varname></term>
-          <listitem>
-            <para>Takes a boolean. Configures whether proxy ARP to be enabled on this port
-            which meets extended requirements by IEEE 802.11 and Hotspot 2.0 specifications.
-            When unset, the kernel's default will be used.</para>
-
-            <xi:include href="version-info.xml" xpointer="v243"/>
-          </listitem>
-        </varlistentry>
-        <varlistentry>
-          <term><varname>MulticastRouter=</varname></term>
-          <listitem>
-            <para>Configures this port for having multicast routers attached. A port with a multicast
-            router will receive all multicast traffic. Takes one of <literal>no</literal>
-            to disable multicast routers on this port, <literal>query</literal> to let the system detect
-            the presence of routers, <literal>permanent</literal> to permanently enable multicast traffic
-            forwarding on this port, or <literal>temporary</literal> to enable multicast routers temporarily
-            on this port, not depending on incoming queries. When unset, the kernel's default will be used.</para>
-
-            <xi:include href="version-info.xml" xpointer="v243"/>
-          </listitem>
-        </varlistentry>
-        <varlistentry>
-          <term><varname>Cost=</varname></term>
-          <listitem>
-            <para>Sets the "cost" of sending packets of this interface.
-            Each port in a bridge may have a different speed and the cost
-            is used to decide which link to use. Faster interfaces
-            should have lower costs. It is an integer value between 1 and
-            65535.</para>
-
-            <xi:include href="version-info.xml" xpointer="v218"/>
-          </listitem>
-        </varlistentry>
-        <varlistentry>
-          <term><varname>Priority=</varname></term>
-          <listitem>
-            <para>Sets the "priority" of sending packets on this interface.
-            Each port in a bridge may have a different priority which is used
-            to decide which link to use. Lower value means higher priority.
-            It is an integer value between 0 to 63. Networkd does not set any
-            default, meaning the kernel default value of 32 is used.</para>
-
-            <xi:include href="version-info.xml" xpointer="v234"/>
-          </listitem>
-        </varlistentry>
-      </variablelist>
+    <para>The [Bridge] section accepts the following keys:</para>
+
+    <variablelist class='network-directives'>
+      <varlistentry>
+        <term><varname>UnicastFlood=</varname></term>
+        <listitem>
+          <para>Takes a boolean. Controls whether the bridge should flood
+          traffic for which an FDB entry is missing and the destination
+          is unknown through this port. When unset, the kernel's default will be used.
+          </para>
+
+          <xi:include href="version-info.xml" xpointer="v223"/>
+        </listitem>
+      </varlistentry>
+      <varlistentry>
+        <term><varname>MulticastFlood=</varname></term>
+        <listitem>
+          <para>Takes a boolean. Controls whether the bridge should flood
+          traffic for which an MDB entry is missing and the destination
+          is unknown through this port. When unset, the kernel's default will be used.
+          </para>
+
+          <xi:include href="version-info.xml" xpointer="v242"/>
+        </listitem>
+      </varlistentry>
+      <varlistentry>
+        <term><varname>MulticastToUnicast=</varname></term>
+        <listitem>
+          <para>Takes a boolean. Multicast to unicast works on top of the multicast snooping feature of
+          the bridge. Which means unicast copies are only delivered to hosts which are interested in it.
+          When unset, the kernel's default will be used.
+          </para>
+
+          <xi:include href="version-info.xml" xpointer="v240"/>
+        </listitem>
+      </varlistentry>
+      <varlistentry>
+        <term><varname>NeighborSuppression=</varname></term>
+        <listitem>
+          <para>Takes a boolean. Configures whether ARP and ND neighbor suppression is enabled for
+          this port. When unset, the kernel's default will be used.
+          </para>
+
+          <xi:include href="version-info.xml" xpointer="v242"/>
+        </listitem>
+      </varlistentry>
+      <varlistentry>
+        <term><varname>Learning=</varname></term>
+        <listitem>
+          <para>Takes a boolean. Configures whether MAC address learning is enabled for
+          this port. When unset, the kernel's default will be used.
+          </para>
+
+          <xi:include href="version-info.xml" xpointer="v242"/>
+        </listitem>
+      </varlistentry>
+      <varlistentry>
+        <term><varname>HairPin=</varname></term>
+        <listitem>
+          <para>Takes a boolean. Configures whether traffic may be sent back out of the port on which it
+          was received. When this flag is false, then the bridge will not forward traffic back out of the
+          receiving port. When unset, the kernel's default will be used.</para>
+
+          <xi:include href="version-info.xml" xpointer="v223"/>
+        </listitem>
+      </varlistentry>
+      <varlistentry>
+        <term><varname>Isolated=</varname></term>
+        <listitem>
+          <para>Takes a boolean. Configures whether this port is isolated or not. Within a bridge,
+          isolated ports can only communicate with non-isolated ports. When set to true, this port can only
+          communicate with other ports whose Isolated setting is false.  When set to false, this port
+          can communicate with any other ports. When unset, the kernel's default will be used.</para>
+
+          <xi:include href="version-info.xml" xpointer="v251"/>
+        </listitem>
+      </varlistentry>
+      <varlistentry>
+        <term><varname>UseBPDU=</varname></term>
+        <listitem>
+          <para>Takes a boolean. Configures whether STP Bridge Protocol Data Units will be
+          processed by the bridge port. When unset, the kernel's default will be used.</para>
+
+          <xi:include href="version-info.xml" xpointer="v223"/>
+        </listitem>
+      </varlistentry>
+      <varlistentry>
+        <term><varname>FastLeave=</varname></term>
+        <listitem>
+          <para>Takes a boolean. This flag allows the bridge to immediately stop multicast
+          traffic on a port that receives an IGMP Leave message. It is only used with
+          IGMP snooping if enabled on the bridge. When unset, the kernel's default will be used.</para>
+
+          <xi:include href="version-info.xml" xpointer="v223"/>
+        </listitem>
+      </varlistentry>
+      <varlistentry>
+        <term><varname>AllowPortToBeRoot=</varname></term>
+        <listitem>
+          <para>Takes a boolean. Configures whether a given port is allowed to
+          become a root port. Only used when STP is enabled on the bridge.
+          When unset, the kernel's default will be used.</para>
+
+          <xi:include href="version-info.xml" xpointer="v223"/>
+        </listitem>
+      </varlistentry>
+      <varlistentry>
+        <term><varname>ProxyARP=</varname></term>
+        <listitem>
+          <para>Takes a boolean. Configures whether proxy ARP to be enabled on this port.
+          When unset, the kernel's default will be used.</para>
+
+          <xi:include href="version-info.xml" xpointer="v243"/>
+        </listitem>
+      </varlistentry>
+      <varlistentry>
+        <term><varname>ProxyARPWiFi=</varname></term>
+        <listitem>
+          <para>Takes a boolean. Configures whether proxy ARP to be enabled on this port
+          which meets extended requirements by IEEE 802.11 and Hotspot 2.0 specifications.
+          When unset, the kernel's default will be used.</para>
+
+          <xi:include href="version-info.xml" xpointer="v243"/>
+        </listitem>
+      </varlistentry>
+      <varlistentry>
+        <term><varname>MulticastRouter=</varname></term>
+        <listitem>
+          <para>Configures this port for having multicast routers attached. A port with a multicast
+          router will receive all multicast traffic. Takes one of <literal>no</literal>
+          to disable multicast routers on this port, <literal>query</literal> to let the system detect
+          the presence of routers, <literal>permanent</literal> to permanently enable multicast traffic
+          forwarding on this port, or <literal>temporary</literal> to enable multicast routers temporarily
+          on this port, not depending on incoming queries. When unset, the kernel's default will be used.</para>
+
+          <xi:include href="version-info.xml" xpointer="v243"/>
+        </listitem>
+      </varlistentry>
+      <varlistentry>
+        <term><varname>Cost=</varname></term>
+        <listitem>
+          <para>Sets the "cost" of sending packets of this interface.
+          Each port in a bridge may have a different speed and the cost
+          is used to decide which link to use. Faster interfaces
+          should have lower costs. It is an integer value between 1 and
+          65535.</para>
+
+          <xi:include href="version-info.xml" xpointer="v218"/>
+        </listitem>
+      </varlistentry>
+      <varlistentry>
+        <term><varname>Priority=</varname></term>
+        <listitem>
+          <para>Sets the "priority" of sending packets on this interface.
+          Each port in a bridge may have a different priority which is used
+          to decide which link to use. Lower value means higher priority.
+          It is an integer value between 0 to 63. Networkd does not set any
+          default, meaning the kernel default value of 32 is used.</para>
+
+          <xi:include href="version-info.xml" xpointer="v234"/>
+        </listitem>
+      </varlistentry>
+    </variablelist>
   </refsect1>
+
   <refsect1>
     <title>[BridgeFDB] Section Options</title>
-      <para>The [BridgeFDB] section manages the forwarding database table of a port and accepts the following
-      keys. Specify several [BridgeFDB] sections to configure several static MAC table entries.</para>
 
-      <variablelist class='network-directives'>
-        <varlistentry>
-          <term><varname>MACAddress=</varname></term>
-          <listitem>
-            <para>As in the [Network] section. This key is mandatory.</para>
+    <para>The [BridgeFDB] section manages the forwarding database table of a port and accepts the following
+    keys. Specify several [BridgeFDB] sections to configure several static MAC table entries.</para>
+
+    <variablelist class='network-directives'>
+      <varlistentry>
+        <term><varname>MACAddress=</varname></term>
+        <listitem>
+          <para>As in the [Network] section. This key is mandatory.</para>
 
           <xi:include href="version-info.xml" xpointer="v219"/>
-          </listitem>
-        </varlistentry>
-        <varlistentry>
-          <term><varname>Destination=</varname></term>
-          <listitem>
-            <para>Takes an IP address of the destination VXLAN tunnel endpoint.</para>
+        </listitem>
+      </varlistentry>
+      <varlistentry>
+        <term><varname>Destination=</varname></term>
+        <listitem>
+          <para>Takes an IP address of the destination VXLAN tunnel endpoint.</para>
 
           <xi:include href="version-info.xml" xpointer="v243"/>
-          </listitem>
-        </varlistentry>
-        <varlistentry>
-          <term><varname>VLANId=</varname></term>
-          <listitem>
-            <para>The VLAN ID for the new static MAC table entry. If
-            omitted, no VLAN ID information is appended to the new static MAC
-            table entry.</para>
-
-            <xi:include href="version-info.xml" xpointer="v219"/>
-          </listitem>
-        </varlistentry>
-        <varlistentry>
-          <term><varname>VNI=</varname></term>
-          <listitem>
-            <para>The VXLAN Network Identifier (or VXLAN Segment ID) to use to connect to
-            the remote VXLAN tunnel endpoint. Takes a number in the range 1…16777215.
-            Defaults to unset.</para>
-
-            <xi:include href="version-info.xml" xpointer="v243"/>
-          </listitem>
-        </varlistentry>
-        <varlistentry>
-          <term><varname>AssociatedWith=</varname></term>
-          <listitem>
-            <para>Specifies where the address is associated with. Takes one of <literal>use</literal>,
-            <literal>self</literal>, <literal>master</literal> or <literal>router</literal>.
-            <literal>use</literal> means the address is in use. User space can use this option to
-            indicate to the kernel that the fdb entry is in use. <literal>self</literal> means
-            the address is associated with the port drivers fdb. Usually hardware. <literal>master</literal>
-            means the address is associated with master devices fdb. <literal>router</literal> means
-            the destination address is associated with a router. Note that it's valid if the referenced
-            device is a VXLAN type device and has route shortcircuit enabled. Defaults to <literal>self</literal>.</para>
+        </listitem>
+      </varlistentry>
+      <varlistentry>
+        <term><varname>VLANId=</varname></term>
+        <listitem>
+          <para>The VLAN ID for the new static MAC table entry. If
+          omitted, no VLAN ID information is appended to the new static MAC
+          table entry.</para>
+
+          <xi:include href="version-info.xml" xpointer="v219"/>
+        </listitem>
+      </varlistentry>
+      <varlistentry>
+        <term><varname>VNI=</varname></term>
+        <listitem>
+          <para>The VXLAN Network Identifier (or VXLAN Segment ID) to use to connect to
+          the remote VXLAN tunnel endpoint. Takes a number in the range 1…16777215.
+          Defaults to unset.</para>
 
           <xi:include href="version-info.xml" xpointer="v243"/>
-          </listitem>
-        </varlistentry>
-        <varlistentry>
-          <term><varname>OutgoingInterface=</varname></term>
-          <listitem>
-            <para>Specifies the name or index of the outgoing interface for the VXLAN device driver to
-            reach the remote VXLAN tunnel endpoint. Defaults to unset.</para>
-
-            <xi:include href="version-info.xml" xpointer="v249"/>
-          </listitem>
-        </varlistentry>
-      </variablelist>
+        </listitem>
+      </varlistentry>
+      <varlistentry>
+        <term><varname>AssociatedWith=</varname></term>
+        <listitem>
+          <para>Specifies where the address is associated with. Takes one of <literal>use</literal>,
+          <literal>self</literal>, <literal>master</literal> or <literal>router</literal>.
+          <literal>use</literal> means the address is in use. User space can use this option to
+          indicate to the kernel that the fdb entry is in use. <literal>self</literal> means
+          the address is associated with the port drivers fdb. Usually hardware. <literal>master</literal>
+          means the address is associated with master devices fdb. <literal>router</literal> means
+          the destination address is associated with a router. Note that it's valid if the referenced
+          device is a VXLAN type device and has route shortcircuit enabled. Defaults to <literal>self</literal>.</para>
+
+          <xi:include href="version-info.xml" xpointer="v243"/>
+        </listitem>
+      </varlistentry>
+      <varlistentry>
+        <term><varname>OutgoingInterface=</varname></term>
+        <listitem>
+          <para>Specifies the name or index of the outgoing interface for the VXLAN device driver to
+          reach the remote VXLAN tunnel endpoint. Defaults to unset.</para>
+
+          <xi:include href="version-info.xml" xpointer="v249"/>
+        </listitem>
+      </varlistentry>
+    </variablelist>
   </refsect1>
+
   <refsect1>
     <title>[BridgeMDB] Section Options</title>
-      <para>The [BridgeMDB] section manages the multicast membership entries forwarding database table of a port and accepts the following
-      keys. Specify several [BridgeMDB] sections to configure several permanent multicast membership entries.</para>
+    <para>The [BridgeMDB] section manages the multicast membership entries forwarding database table of a port and accepts the following
+    keys. Specify several [BridgeMDB] sections to configure several permanent multicast membership entries.</para>
 
-      <variablelist class='network-directives'>
-        <varlistentry>
-          <term><varname>MulticastGroupAddress=</varname></term>
-          <listitem>
-            <para>Specifies the IPv4 or IPv6 multicast group address to add. This setting is mandatory.</para>
+    <variablelist class='network-directives'>
+      <varlistentry>
+        <term><varname>MulticastGroupAddress=</varname></term>
+        <listitem>
+          <para>Specifies the IPv4 or IPv6 multicast group address to add. This setting is mandatory.</para>
 
           <xi:include href="version-info.xml" xpointer="v247"/>
-          </listitem>
-        </varlistentry>
-        <varlistentry>
-          <term><varname>VLANId=</varname></term>
-          <listitem>
-            <para>The VLAN ID for the new entry. Valid ranges are 0 (no VLAN) to 4094. Optional, defaults to 0.</para>
+        </listitem>
+      </varlistentry>
+      <varlistentry>
+        <term><varname>VLANId=</varname></term>
+        <listitem>
+          <para>The VLAN ID for the new entry. Valid ranges are 0 (no VLAN) to 4094. Optional, defaults to 0.</para>
 
           <xi:include href="version-info.xml" xpointer="v247"/>
-          </listitem>
-        </varlistentry>
-      </variablelist>
+        </listitem>
+      </varlistentry>
+    </variablelist>
   </refsect1>
 
   <refsect1>
     <title>[LLDP] Section Options</title>
-      <para>The [LLDP] section manages the Link Layer Discovery Protocol (LLDP) and accepts the following
-      keys:</para>
-      <variablelist class='network-directives'>
-        <varlistentry>
-          <term><varname>MUDURL=</varname></term>
-          <listitem>
-            <para>When configured, the specified Manufacturer Usage Descriptions (MUD) URL will be sent in
-            LLDP packets. The syntax and semantics are the same as for <varname>MUDURL=</varname> in the
-            [DHCPv4] section described above.</para>
-
-            <para>The MUD URLs received via LLDP packets are saved and can be read using the
-            <function>sd_lldp_neighbor_get_mud_url()</function> function.</para>
-
-            <xi:include href="version-info.xml" xpointer="v246"/>
-          </listitem>
-        </varlistentry>
-      </variablelist>
+    <para>The [LLDP] section manages the Link Layer Discovery Protocol (LLDP) and accepts the following
+    keys:</para>
+
+    <variablelist class='network-directives'>
+      <varlistentry>
+        <term><varname>MUDURL=</varname></term>
+        <listitem>
+          <para>When configured, the specified Manufacturer Usage Descriptions (MUD) URL will be sent in
+          LLDP packets. The syntax and semantics are the same as for <varname>MUDURL=</varname> in the
+          [DHCPv4] section described above.</para>
+
+          <para>The MUD URLs received via LLDP packets are saved and can be read using the
+          <function>sd_lldp_neighbor_get_mud_url()</function> function.</para>
+
+          <xi:include href="version-info.xml" xpointer="v246"/>
+        </listitem>
+      </varlistentry>
+    </variablelist>
   </refsect1>
 
   <refsect1>
     <title>[CAN] Section Options</title>
-      <para>The [CAN] section manages the Controller Area Network (CAN bus) and accepts the
-      following keys:</para>
-      <variablelist class='network-directives'>
-        <varlistentry>
-          <term><varname>BitRate=</varname></term>
-          <listitem>
-            <para>The bitrate of CAN device in bits per second. The usual SI prefixes (K, M) with the base of 1000 can
-            be used here. Takes a number in the range 1…4294967295.</para>
-
-            <xi:include href="version-info.xml" xpointer="v239"/>
-          </listitem>
-        </varlistentry>
-        <varlistentry>
-          <term><varname>SamplePoint=</varname></term>
-          <listitem>
-            <para>Optional sample point in percent with one decimal (e.g. <literal>75%</literal>,
-            <literal>87.5%</literal>) or permille (e.g. <literal>875‰</literal>). This will be ignored when
-            <varname>BitRate=</varname> is unspecified.</para>
+    <para>The [CAN] section manages the Controller Area Network (CAN bus) and accepts the
+    following keys:</para>
+    <variablelist class='network-directives'>
+      <varlistentry>
+        <term><varname>BitRate=</varname></term>
+        <listitem>
+          <para>The bitrate of CAN device in bits per second. The usual SI prefixes (K, M) with the base of 1000 can
+          be used here. Takes a number in the range 1…4294967295.</para>
+
+          <xi:include href="version-info.xml" xpointer="v239"/>
+        </listitem>
+      </varlistentry>
+      <varlistentry>
+        <term><varname>SamplePoint=</varname></term>
+        <listitem>
+          <para>Optional sample point in percent with one decimal (e.g. <literal>75%</literal>,
+          <literal>87.5%</literal>) or permille (e.g. <literal>875‰</literal>). This will be ignored when
+          <varname>BitRate=</varname> is unspecified.</para>
 
           <xi:include href="version-info.xml" xpointer="v239"/>
-          </listitem>
-        </varlistentry>
-        <varlistentry>
-          <term><varname>TimeQuantaNSec=</varname></term>
-          <term><varname>PropagationSegment=</varname></term>
-          <term><varname>PhaseBufferSegment1=</varname></term>
-          <term><varname>PhaseBufferSegment2=</varname></term>
-          <term><varname>SyncJumpWidth=</varname></term>
-          <listitem>
-            <para>Specifies the time quanta, propagation segment, phase buffer segment 1 and 2, and the
-            synchronization jump width, which allow one to define the CAN bit-timing in a hardware
-            independent format as proposed by the Bosch CAN 2.0 Specification.
-            <varname>TimeQuantaNSec=</varname> takes a timespan in nanoseconds.
-            <varname>PropagationSegment=</varname>, <varname>PhaseBufferSegment1=</varname>,
-            <varname>PhaseBufferSegment2=</varname>, and <varname>SyncJumpWidth=</varname> take number
-            of time quantum specified in <varname>TimeQuantaNSec=</varname> and must be an unsigned
-            integer in the range 0…4294967295. These settings except for
-            <varname>SyncJumpWidth=</varname> will be ignored when <varname>BitRate=</varname> is
-            specified.</para>
-
-            <xi:include href="version-info.xml" xpointer="v250"/>
-          </listitem>
-        </varlistentry>
-        <varlistentry>
-          <term><varname>DataBitRate=</varname></term>
-          <term><varname>DataSamplePoint=</varname></term>
-          <listitem>
-            <para>The bitrate and sample point for the data phase, if CAN-FD is used. These settings are
-            analogous to the <varname>BitRate=</varname> and <varname>SamplePoint=</varname> keys.</para>
-
-            <xi:include href="version-info.xml" xpointer="v246"/>
-          </listitem>
-        </varlistentry>
-        <varlistentry>
-          <term><varname>DataTimeQuantaNSec=</varname></term>
-          <term><varname>DataPropagationSegment=</varname></term>
-          <term><varname>DataPhaseBufferSegment1=</varname></term>
-          <term><varname>DataPhaseBufferSegment2=</varname></term>
-          <term><varname>DataSyncJumpWidth=</varname></term>
-          <listitem>
-            <para>Specifies the time quanta, propagation segment, phase buffer segment 1 and 2, and the
-            synchronization jump width for the data phase, if CAN-FD is used. These settings are
-            analogous to the <varname>TimeQuantaNSec=</varname> or related settings.</para>
-
-            <xi:include href="version-info.xml" xpointer="v250"/>
-          </listitem>
-        </varlistentry>
-        <varlistentry>
-          <term><varname>FDMode=</varname></term>
-          <listitem>
-            <para>Takes a boolean. When <literal>yes</literal>, CAN-FD mode is enabled for the interface.
-            Note, that a bitrate and optional sample point should also be set for the CAN-FD data phase using
-            the <varname>DataBitRate=</varname> and <varname>DataSamplePoint=</varname> keys, or
-            <varname>DataTimeQuanta=</varname> and related settings.</para>
+        </listitem>
+      </varlistentry>
+      <varlistentry>
+        <term><varname>TimeQuantaNSec=</varname></term>
+        <term><varname>PropagationSegment=</varname></term>
+        <term><varname>PhaseBufferSegment1=</varname></term>
+        <term><varname>PhaseBufferSegment2=</varname></term>
+        <term><varname>SyncJumpWidth=</varname></term>
+        <listitem>
+          <para>Specifies the time quanta, propagation segment, phase buffer segment 1 and 2, and the
+          synchronization jump width, which allow one to define the CAN bit-timing in a hardware
+          independent format as proposed by the Bosch CAN 2.0 Specification.
+          <varname>TimeQuantaNSec=</varname> takes a timespan in nanoseconds.
+          <varname>PropagationSegment=</varname>, <varname>PhaseBufferSegment1=</varname>,
+          <varname>PhaseBufferSegment2=</varname>, and <varname>SyncJumpWidth=</varname> take number
+          of time quantum specified in <varname>TimeQuantaNSec=</varname> and must be an unsigned
+          integer in the range 0…4294967295. These settings except for
+          <varname>SyncJumpWidth=</varname> will be ignored when <varname>BitRate=</varname> is
+          specified.</para>
+
+          <xi:include href="version-info.xml" xpointer="v250"/>
+        </listitem>
+      </varlistentry>
+      <varlistentry>
+        <term><varname>DataBitRate=</varname></term>
+        <term><varname>DataSamplePoint=</varname></term>
+        <listitem>
+          <para>The bitrate and sample point for the data phase, if CAN-FD is used. These settings are
+          analogous to the <varname>BitRate=</varname> and <varname>SamplePoint=</varname> keys.</para>
 
           <xi:include href="version-info.xml" xpointer="v246"/>
-          </listitem>
-        </varlistentry>
-        <varlistentry>
-          <term><varname>FDNonISO=</varname></term>
-          <listitem>
-            <para>Takes a boolean. When <literal>yes</literal>, non-ISO CAN-FD mode is enabled for the
-            interface. When unset, the kernel's default will be used.</para>
+        </listitem>
+      </varlistentry>
+      <varlistentry>
+        <term><varname>DataTimeQuantaNSec=</varname></term>
+        <term><varname>DataPropagationSegment=</varname></term>
+        <term><varname>DataPhaseBufferSegment1=</varname></term>
+        <term><varname>DataPhaseBufferSegment2=</varname></term>
+        <term><varname>DataSyncJumpWidth=</varname></term>
+        <listitem>
+          <para>Specifies the time quanta, propagation segment, phase buffer segment 1 and 2, and the
+          synchronization jump width for the data phase, if CAN-FD is used. These settings are
+          analogous to the <varname>TimeQuantaNSec=</varname> or related settings.</para>
+
+          <xi:include href="version-info.xml" xpointer="v250"/>
+        </listitem>
+      </varlistentry>
+      <varlistentry>
+        <term><varname>FDMode=</varname></term>
+        <listitem>
+          <para>Takes a boolean. When <literal>yes</literal>, CAN-FD mode is enabled for the interface.
+          Note, that a bitrate and optional sample point should also be set for the CAN-FD data phase using
+          the <varname>DataBitRate=</varname> and <varname>DataSamplePoint=</varname> keys, or
+          <varname>DataTimeQuanta=</varname> and related settings.</para>
+
+          <xi:include href="version-info.xml" xpointer="v246"/>
+        </listitem>
+      </varlistentry>
+      <varlistentry>
+        <term><varname>FDNonISO=</varname></term>
+        <listitem>
+          <para>Takes a boolean. When <literal>yes</literal>, non-ISO CAN-FD mode is enabled for the
+          interface. When unset, the kernel's default will be used.</para>
+
+          <xi:include href="version-info.xml" xpointer="v246"/>
+        </listitem>
+      </varlistentry>
+      <varlistentry>
+        <term><varname>RestartSec=</varname></term>
+        <listitem>
+          <para>Automatic restart delay time. If set to a non-zero value, a restart of the CAN controller will be
+          triggered automatically in case of a bus-off condition after the specified delay time. Subsecond delays can
+          be specified using decimals (e.g. <literal>0.1s</literal>) or a <literal>ms</literal> or
+          <literal>us</literal> postfix. Using <literal>infinity</literal> or <literal>0</literal> will turn the
+          automatic restart off. By default automatic restart is disabled.</para>
+
+          <xi:include href="version-info.xml" xpointer="v239"/>
+        </listitem>
+      </varlistentry>
+      <varlistentry>
+        <term><varname>Termination=</varname></term>
+        <listitem>
+          <para>Takes a boolean or a termination resistor value in ohm in the range 0…65535. When
+          <literal>yes</literal>, the termination resistor is set to 120 ohm. When
+          <literal>no</literal> or <literal>0</literal> is set, the termination resistor is disabled.
+          When unset, the kernel's default will be used.</para>
 
           <xi:include href="version-info.xml" xpointer="v246"/>
-          </listitem>
-        </varlistentry>
-        <varlistentry>
-          <term><varname>RestartSec=</varname></term>
-          <listitem>
-            <para>Automatic restart delay time. If set to a non-zero value, a restart of the CAN controller will be
-            triggered automatically in case of a bus-off condition after the specified delay time. Subsecond delays can
-            be specified using decimals (e.g. <literal>0.1s</literal>) or a <literal>ms</literal> or
-            <literal>us</literal> postfix. Using <literal>infinity</literal> or <literal>0</literal> will turn the
-            automatic restart off. By default automatic restart is disabled.</para>
-
-            <xi:include href="version-info.xml" xpointer="v239"/>
-          </listitem>
-        </varlistentry>
-        <varlistentry>
-          <term><varname>Termination=</varname></term>
-          <listitem>
-            <para>Takes a boolean or a termination resistor value in ohm in the range 0…65535. When
-            <literal>yes</literal>, the termination resistor is set to 120 ohm. When
-            <literal>no</literal> or <literal>0</literal> is set, the termination resistor is disabled.
-            When unset, the kernel's default will be used.</para>
-
-            <xi:include href="version-info.xml" xpointer="v246"/>
-          </listitem>
-        </varlistentry>
-        <varlistentry>
-          <term><varname>TripleSampling=</varname></term>
-          <listitem>
-            <para>Takes a boolean. When <literal>yes</literal>, three samples (instead of one) are used to determine
-            the value of a received bit by majority rule. When unset, the kernel's default will be used.</para>
+        </listitem>
+      </varlistentry>
+      <varlistentry>
+        <term><varname>TripleSampling=</varname></term>
+        <listitem>
+          <para>Takes a boolean. When <literal>yes</literal>, three samples (instead of one) are used to determine
+          the value of a received bit by majority rule. When unset, the kernel's default will be used.</para>
 
           <xi:include href="version-info.xml" xpointer="v242"/>
-          </listitem>
-        </varlistentry>
-        <varlistentry>
-          <term><varname>BusErrorReporting=</varname></term>
-          <listitem>
-            <para>Takes a boolean. When <literal>yes</literal>, reporting of CAN bus errors is activated
-            (those include single bit, frame format, and bit stuffing errors, unable to send dominant bit,
-            unable to send recessive bit, bus overload, active error announcement, error occurred on
-            transmission). When unset, the kernel's default will be used. Note: in case of a CAN bus with a
-            single CAN device, sending a CAN frame may result in a huge number of CAN bus errors.</para>
+        </listitem>
+      </varlistentry>
+      <varlistentry>
+        <term><varname>BusErrorReporting=</varname></term>
+        <listitem>
+          <para>Takes a boolean. When <literal>yes</literal>, reporting of CAN bus errors is activated
+          (those include single bit, frame format, and bit stuffing errors, unable to send dominant bit,
+          unable to send recessive bit, bus overload, active error announcement, error occurred on
+          transmission). When unset, the kernel's default will be used. Note: in case of a CAN bus with a
+          single CAN device, sending a CAN frame may result in a huge number of CAN bus errors.</para>
 
           <xi:include href="version-info.xml" xpointer="v248"/>
-          </listitem>
-        </varlistentry>
-        <varlistentry>
-          <term><varname>ListenOnly=</varname></term>
-          <listitem>
-            <para>Takes a boolean. When <literal>yes</literal>, listen-only mode is enabled. When the
-            interface is in listen-only mode, the interface neither transmit CAN frames nor send ACK
-            bit. Listen-only mode is important to debug CAN networks without interfering with the
-            communication or acknowledge the CAN frame. When unset, the kernel's default will be used.
-            </para>
+        </listitem>
+      </varlistentry>
+      <varlistentry>
+        <term><varname>ListenOnly=</varname></term>
+        <listitem>
+          <para>Takes a boolean. When <literal>yes</literal>, listen-only mode is enabled. When the
+          interface is in listen-only mode, the interface neither transmit CAN frames nor send ACK
+          bit. Listen-only mode is important to debug CAN networks without interfering with the
+          communication or acknowledge the CAN frame. When unset, the kernel's default will be used.
+          </para>
 
           <xi:include href="version-info.xml" xpointer="v246"/>
-          </listitem>
-        </varlistentry>
-        <varlistentry>
-          <term><varname>Loopback=</varname></term>
-          <listitem>
-            <para>Takes a boolean. When <literal>yes</literal>, loopback mode is enabled. When the
-            loopback mode is enabled, the interface treats messages transmitted by itself as received
-            messages. The loopback mode is important to debug CAN networks. When unset, the kernel's
-            default will be used.</para>
+        </listitem>
+      </varlistentry>
+      <varlistentry>
+        <term><varname>Loopback=</varname></term>
+        <listitem>
+          <para>Takes a boolean. When <literal>yes</literal>, loopback mode is enabled. When the
+          loopback mode is enabled, the interface treats messages transmitted by itself as received
+          messages. The loopback mode is important to debug CAN networks. When unset, the kernel's
+          default will be used.</para>
 
           <xi:include href="version-info.xml" xpointer="v250"/>
-          </listitem>
-        </varlistentry>
-        <varlistentry>
-          <term><varname>OneShot=</varname></term>
-          <listitem>
-            <para>Takes a boolean. When <literal>yes</literal>, one-shot mode is enabled. When unset,
-            the kernel's default will be used.</para>
+        </listitem>
+      </varlistentry>
+      <varlistentry>
+        <term><varname>OneShot=</varname></term>
+        <listitem>
+          <para>Takes a boolean. When <literal>yes</literal>, one-shot mode is enabled. When unset,
+          the kernel's default will be used.</para>
 
           <xi:include href="version-info.xml" xpointer="v250"/>
-          </listitem>
-        </varlistentry>
-        <varlistentry>
-          <term><varname>PresumeAck=</varname></term>
-          <listitem>
-            <para>Takes a boolean. When <literal>yes</literal>, the interface will ignore missing CAN
-            ACKs. When unset, the kernel's default will be used.</para>
+        </listitem>
+      </varlistentry>
+      <varlistentry>
+        <term><varname>PresumeAck=</varname></term>
+        <listitem>
+          <para>Takes a boolean. When <literal>yes</literal>, the interface will ignore missing CAN
+          ACKs. When unset, the kernel's default will be used.</para>
 
           <xi:include href="version-info.xml" xpointer="v250"/>
-          </listitem>
-        </varlistentry>
-        <varlistentry>
-          <term><varname>ClassicDataLengthCode=</varname></term>
-          <listitem>
-            <para>Takes a boolean. When <literal>yes</literal>, the interface will handle the 4bit data
-            length code (DLC). When unset, the kernel's default will be used.</para>
+        </listitem>
+      </varlistentry>
+      <varlistentry>
+        <term><varname>ClassicDataLengthCode=</varname></term>
+        <listitem>
+          <para>Takes a boolean. When <literal>yes</literal>, the interface will handle the 4bit data
+          length code (DLC). When unset, the kernel's default will be used.</para>
 
           <xi:include href="version-info.xml" xpointer="v250"/>
-          </listitem>
-        </varlistentry>
-      </variablelist>
+        </listitem>
+      </varlistentry>
+    </variablelist>
   </refsect1>
 
   <refsect1>
     <title>[IPoIB] Section Options</title>
-      <para>The [IPoIB] section manages the IP over Infiniband and accepts the following keys:</para>
-      <variablelist class='network-directives'>
-        <xi:include href="systemd.netdev.xml" xpointer="ipoib_mode" />
-        <xi:include href="systemd.netdev.xml" xpointer="ipoib_umcast" />
-      </variablelist>
+    <para>The [IPoIB] section manages the IP over Infiniband and accepts the following keys:</para>
+
+    <variablelist class='network-directives'>
+      <xi:include href="systemd.netdev.xml" xpointer="ipoib_mode" />
+      <xi:include href="systemd.netdev.xml" xpointer="ipoib_umcast" />
+    </variablelist>
   </refsect1>
 
   <refsect1>
@@ -4601,7 +4609,7 @@ ServerAddress=192.168.0.1/24</programlisting>
           <para>Specifies the parent Queueing Discipline (qdisc). Takes one of <literal>clsact</literal>
           or <literal>ingress</literal>. This is mandatory.</para>
 
-        <xi:include href="version-info.xml" xpointer="v244"/>
+          <xi:include href="version-info.xml" xpointer="v244"/>
         </listitem>
       </varlistentry>
 
@@ -5857,47 +5865,54 @@ ServerAddress=192.168.0.1/24</programlisting>
 
   <refsect1>
     <title>[BridgeVLAN] Section Options</title>
-      <para>The [BridgeVLAN] section manages the VLAN ID configuration of a bridge port and accepts the
-      following keys. Specify several [BridgeVLAN] sections to configure several VLAN entries. The
-      <varname>VLANFiltering=</varname> option has to be enabled, see the [Bridge] section in
-      <citerefentry><refentrytitle>systemd.netdev</refentrytitle><manvolnum>5</manvolnum></citerefentry>.</para>
-
-      <variablelist class='network-directives'>
-        <varlistentry>
-          <term><varname>VLAN=</varname></term>
-          <listitem>
-            <para>The VLAN ID allowed on the port. This can be either a single ID or a range M-N. Takes an
-            integer in the range 1…4094. This setting can be specified multiple times. If an empty string is
-            assigned, then the all previous assignments are cleared.</para>
-
-            <xi:include href="version-info.xml" xpointer="v231"/>
-          </listitem>
-        </varlistentry>
-        <varlistentry>
-          <term><varname>EgressUntagged=</varname></term>
-          <listitem>
-            <para>The VLAN ID specified here will be used to untag frames on egress. Configuring
-            <varname>EgressUntagged=</varname> implicates the use of <varname>VLAN=</varname> above and will
-            enable the VLAN ID for ingress as well. This can be either a single ID or a range M-N. This
-            setting can be specified multiple times. If an empty string is assigned, then the all previous
-            assignments are cleared.</para>
-
-            <xi:include href="version-info.xml" xpointer="v231"/>
-          </listitem>
-        </varlistentry>
-        <varlistentry>
-          <term><varname>PVID=</varname></term>
-          <listitem>
-            <para>The port VLAN ID specified here is assigned to all untagged frames at ingress. Takes an
-            VLAN ID or negative boolean value (e.g. <literal>no</literal>). When false, the currently
-            assigned port VLAN ID will be dropped. Configuring <varname>PVID=</varname> implicates the use of
-            <varname>VLAN=</varname> setting in the above and will enable the VLAN ID for ingress as well.
-            Defaults to unset, and will keep the assigned port VLAN ID if exists.</para>
-
-            <xi:include href="version-info.xml" xpointer="v231"/>
-          </listitem>
-        </varlistentry>
-      </variablelist>
+    <para>
+      The [BridgeVLAN] section manages the VLAN ID configurations of a bridge master or port, and accepts the
+      following keys. To make the settings in this section take an effect,
+      <varname>VLANFiltering=</varname> option has to be enabled on the bridge master, see the [Bridge]
+      section in
+      <citerefentry><refentrytitle>systemd.netdev</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
+      If at least one valid settings specified in this section in a .network file for an interface, all
+      assigned VLAN IDs on the interface that are not configured in the .network file will be removed. If
+      VLAN IDs on an interface need to be managed by other tools, then the settings in this section cannot
+      be used in the matching .network file.
+    </para>
+
+    <variablelist class='network-directives'>
+      <varlistentry>
+        <term><varname>VLAN=</varname></term>
+        <listitem>
+          <para>The VLAN ID allowed on the port. This can be either a single ID or a range M-N. Takes an
+          integer in the range 1…4094. This setting can be specified multiple times. If an empty string is
+          assigned, then the all previous assignments are cleared.</para>
+
+          <xi:include href="version-info.xml" xpointer="v231"/>
+        </listitem>
+      </varlistentry>
+      <varlistentry>
+        <term><varname>EgressUntagged=</varname></term>
+        <listitem>
+          <para>The VLAN ID specified here will be used to untag frames on egress. Configuring
+          <varname>EgressUntagged=</varname> implicates the use of <varname>VLAN=</varname> above and will
+          enable the VLAN ID for ingress as well. This can be either a single ID or a range M-N. This
+          setting can be specified multiple times. If an empty string is assigned, then the all previous
+          assignments are cleared.</para>
+
+          <xi:include href="version-info.xml" xpointer="v231"/>
+        </listitem>
+      </varlistentry>
+      <varlistentry>
+        <term><varname>PVID=</varname></term>
+        <listitem>
+          <para>The port VLAN ID specified here is assigned to all untagged frames at ingress. Takes an
+          VLAN ID or negative boolean value (e.g. <literal>no</literal>). When false, the currently
+          assigned port VLAN ID will be dropped. Configuring <varname>PVID=</varname> implicates the use of
+          <varname>VLAN=</varname> setting in the above and will enable the VLAN ID for ingress as well.
+          Defaults to unset, and will keep the assigned port VLAN ID if exists.</para>
+
+          <xi:include href="version-info.xml" xpointer="v231"/>
+        </listitem>
+      </varlistentry>
+    </variablelist>
   </refsect1>
 
   <refsect1>
index 79806199e571f54b063cb37bb019492d5515ee2a..786031c7557471196afaead9680562580d206df0 100644 (file)
   </refnamediv>
 
   <refsynopsisdiv>
-    <para><filename>/etc/systemd/nspawn/<replaceable>machine</replaceable>.nspawn</filename></para>
-    <para><filename>/run/systemd/nspawn/<replaceable>machine</replaceable>.nspawn</filename></para>
-    <para><filename>/var/lib/machines/<replaceable>machine</replaceable>.nspawn</filename></para>
+    <para><simplelist>
+      <member><filename>/etc/systemd/nspawn/<replaceable>machine</replaceable>.nspawn</filename></member>
+      <member><filename>/run/systemd/nspawn/<replaceable>machine</replaceable>.nspawn</filename></member>
+      <member><filename>/var/lib/machines/<replaceable>machine</replaceable>.nspawn</filename></member>
+    </simplelist></para>
   </refsynopsisdiv>
 
   <refsect1>
index 5687db502187c6266acd2b150f36c379d7496ac0..1dcba3fe532e839881768365e1ec6bb15f133757 100644 (file)
   </refnamediv>
 
   <refsynopsisdiv>
-    <para><literallayout>
-<filename>/etc/pcrlock.d/*.pcrlock</filename>
-<filename>/etc/pcrlock.d/*.pcrlock.d/*.pcrlock</filename>
-<filename>/run/pcrlock.d/*.pcrlock</filename>
-<filename>/run/pcrlock.d/*.pcrlock.d/*.pcrlock</filename>
-<filename>/var/lib/pcrlock.d/*.pcrlock</filename>
-<filename>/var/lib/pcrlock.d/*.pcrlock.d/*.pcrlock</filename>
-<filename>/usr/local/pcrlock.d/*.pcrlock</filename>
-<filename>/usr/local/pcrlock.d/*.pcrlock.d/*.pcrlock</filename>
-<filename>/usr/lib/pcrlock.d/*.pcrlock</filename>
-<filename>/usr/lib/pcrlock.d/*.pcrlock.d/*.pcrlock</filename></literallayout></para>
+    <para><simplelist>
+      <member><filename>/etc/pcrlock.d/*.pcrlock</filename></member>
+      <member><filename>/etc/pcrlock.d/*.pcrlock.d/*.pcrlock</filename></member>
+      <member><filename>/run/pcrlock.d/*.pcrlock</filename></member>
+      <member><filename>/run/pcrlock.d/*.pcrlock.d/*.pcrlock</filename></member>
+      <member><filename>/var/lib/pcrlock.d/*.pcrlock</filename></member>
+      <member><filename>/var/lib/pcrlock.d/*.pcrlock.d/*.pcrlock</filename></member>
+      <member><filename>/usr/local/pcrlock.d/*.pcrlock</filename></member>
+      <member><filename>/usr/local/pcrlock.d/*.pcrlock.d/*.pcrlock</filename></member>
+      <member><filename>/usr/lib/pcrlock.d/*.pcrlock</filename></member>
+      <member><filename>/usr/lib/pcrlock.d/*.pcrlock.d/*.pcrlock</filename></member>
+    </simplelist></para>
   </refsynopsisdiv>
 
   <refsect1>
index 5d46a88c3e94f41c158727d3f2e6c8260ea01c64..26e090ea2ccf475b0c71549067089d54b6c088e4 100644 (file)
   </refnamediv>
 
   <refsynopsisdiv>
-    <para><filename>/etc/systemd/system-preset/*.preset</filename></para>
-    <para><filename>/run/systemd/system-preset/*.preset</filename></para>
-    <para><filename>/usr/lib/systemd/system-preset/*.preset</filename></para>
-    <para><filename>/etc/systemd/user-preset/*.preset</filename></para>
-    <para><filename>/run/systemd/user-preset/*.preset</filename></para>
-    <para><filename>/usr/lib/systemd/user-preset/*.preset</filename></para>
+    <para><simplelist>
+      <member><filename>/etc/systemd/system-preset/*.preset</filename></member>
+      <member><filename>/run/systemd/system-preset/*.preset</filename></member>
+      <member><filename>/usr/lib/systemd/system-preset/*.preset</filename></member>
+      <member><filename>/etc/systemd/user-preset/*.preset</filename></member>
+      <member><filename>/run/systemd/user-preset/*.preset</filename></member>
+      <member><filename>/usr/lib/systemd/user-preset/*.preset</filename></member>
+    </simplelist></para>
   </refsynopsisdiv>
 
   <refsect1>
index f7f0df18aab3f60ce6b8da263bed7004cba435fa..2a2d03b29fd1daf364b6a699b78ec1eb54749c3a 100644 (file)
           <xi:include href="version-info.xml" xpointer="v254"/>
         </listitem>
       </varlistentry>
+
+      <varlistentry>
+        <term><varname>home.create.*</varname></term>
+        <listitem>
+          <para>Creates a home area for the specified user with the user record data passed in. For details see
+          <citerefentry><refentrytitle>homectl</refentrytitle><manvolnum>1</manvolnum></citerefentry>.</para>
+
+          <xi:include href="version-info.xml" xpointer="v256"/>
+        </listitem>
+      </varlistentry>
     </variablelist>
   </refsect1>
 
index 7fed74a22730d11babf0a8d541e1981e844518bb..c21c8b8221978f5d44d1592da21e1fc9699f00f0 100644 (file)
     <refsect2>
       <title>System Unit Search Path</title>
 
-      <para><literallayout><filename>/etc/systemd/system.control/*</filename>
-<filename>/run/systemd/system.control/*</filename>
-<filename>/run/systemd/transient/*</filename>
-<filename>/run/systemd/generator.early/*</filename>
-<filename>/etc/systemd/system/*</filename>
-<filename>/etc/systemd/system.attached/*</filename>
-<filename>/run/systemd/system/*</filename>
-<filename>/run/systemd/system.attached/*</filename>
-<filename>/run/systemd/generator/*</filename>
-<filename index='false'>…</filename>
-<filename>/usr/lib/systemd/system/*</filename>
-<filename>/run/systemd/generator.late/*</filename></literallayout></para>
+      <para><simplelist>
+        <member><filename>/etc/systemd/system.control/*</filename></member>
+        <member><filename>/run/systemd/system.control/*</filename></member>
+        <member><filename>/run/systemd/transient/*</filename></member>
+        <member><filename>/run/systemd/generator.early/*</filename></member>
+        <member><filename>/etc/systemd/system/*</filename></member>
+        <member><filename>/etc/systemd/system.attached/*</filename></member>
+        <member><filename>/run/systemd/system/*</filename></member>
+        <member><filename>/run/systemd/system.attached/*</filename></member>
+        <member><filename>/run/systemd/generator/*</filename></member>
+        <member><filename index='false'>…</filename></member>
+        <member><filename>/usr/lib/systemd/system/*</filename></member>
+        <member><filename>/run/systemd/generator.late/*</filename></member>
+      </simplelist></para>
     </refsect2>
 
     <refsect2>
       <title>User Unit Search Path</title>
-      <para><literallayout><filename>~/.config/systemd/user.control/*</filename>
-<filename>$XDG_RUNTIME_DIR/systemd/user.control/*</filename>
-<filename>$XDG_RUNTIME_DIR/systemd/transient/*</filename>
-<filename>$XDG_RUNTIME_DIR/systemd/generator.early/*</filename>
-<filename>~/.config/systemd/user/*</filename>
-<filename>$XDG_CONFIG_DIRS/systemd/user/*</filename>
-<filename>/etc/systemd/user/*</filename>
-<filename>$XDG_RUNTIME_DIR/systemd/user/*</filename>
-<filename>/run/systemd/user/*</filename>
-<filename>$XDG_RUNTIME_DIR/systemd/generator/*</filename>
-<filename>$XDG_DATA_HOME/systemd/user/*</filename>
-<filename>$XDG_DATA_DIRS/systemd/user/*</filename>
-<filename index='false'>…</filename>
-<filename>/usr/lib/systemd/user/*</filename>
-<filename>$XDG_RUNTIME_DIR/systemd/generator.late/*</filename></literallayout></para>
+      <para><simplelist>
+        <member><filename>~/.config/systemd/user.control/*</filename></member>
+        <member><filename>$XDG_RUNTIME_DIR/systemd/user.control/*</filename></member>
+        <member><filename>$XDG_RUNTIME_DIR/systemd/transient/*</filename></member>
+        <member><filename>$XDG_RUNTIME_DIR/systemd/generator.early/*</filename></member>
+        <member><filename>~/.config/systemd/user/*</filename></member>
+        <member><filename>$XDG_CONFIG_DIRS/systemd/user/*</filename></member>
+        <member><filename>/etc/systemd/user/*</filename></member>
+        <member><filename>$XDG_RUNTIME_DIR/systemd/user/*</filename></member>
+        <member><filename>/run/systemd/user/*</filename></member>
+        <member><filename>$XDG_RUNTIME_DIR/systemd/generator/*</filename></member>
+        <member><filename>$XDG_DATA_HOME/systemd/user/*</filename></member>
+        <member><filename>$XDG_DATA_DIRS/systemd/user/*</filename></member>
+        <member><filename index='false'>…</filename></member>
+        <member><filename>/usr/lib/systemd/user/*</filename></member>
+        <member><filename>$XDG_RUNTIME_DIR/systemd/generator.late/*</filename></member>
+      </simplelist></para>
     </refsect2>
 
   </refsynopsisdiv>
index 00b4ac887d91568a86dfac43cf2e2c1b89dd3257..b5c89811f5c0fe275fde20589d6a5a0f27b50eab 100644 (file)
   </refnamediv>
 
   <refsynopsisdiv>
-    <para><literallayout><filename>/etc/sysupdate.d/*.conf</filename>
-<filename>/run/sysupdate.d/*.conf</filename>
-<filename>/usr/lib/sysupdate.d/*.conf</filename>
-    </literallayout></para>
+    <para><simplelist>
+      <member><filename>/etc/sysupdate.d/*.conf</filename></member>
+      <member><filename>/run/sysupdate.d/*.conf</filename></member>
+      <member><filename>/usr/lib/sysupdate.d/*.conf</filename></member>
+    </simplelist></para>
   </refsynopsisdiv>
 
   <refsect1>
index e7cd2855d93664812d359926cec47a633892f44c..5c126741a0a661dd8bd915a90d787e9f8006952b 100644 (file)
   </refnamediv>
 
   <refsynopsisdiv>
-    <para><filename>/etc/sysusers.d/*.conf</filename></para>
-    <para><filename>/run/sysusers.d/*.conf</filename></para>
-    <para><filename>/usr/lib/sysusers.d/*.conf</filename></para>
+    <para><simplelist>
+      <member><filename>/etc/sysusers.d/*.conf</filename></member>
+      <member><filename>/run/sysusers.d/*.conf</filename></member>
+      <member><filename>/usr/lib/sysusers.d/*.conf</filename></member>
+    </simplelist></para>
 
     <programlisting>
 #Type Name       ID                  GECOS              Home directory Shell
index e804f5fb8410f437e4beb920ba9ad0fc694001e2..b2000383760c1ef00e6db74fd245d29882c9c837 100644 (file)
   </refnamediv>
 
   <refsynopsisdiv>
-    <para><filename>/etc/systemd/timesyncd.conf</filename></para>
-    <para><filename>/etc/systemd/timesyncd.conf.d/*.conf</filename></para>
-    <para><filename>/run/systemd/timesyncd.conf.d/*.conf</filename></para>
-    <para><filename>/usr/lib/systemd/timesyncd.conf.d/*.conf</filename></para>
+    <para><simplelist>
+      <member><filename>/etc/systemd/timesyncd.conf</filename></member>
+      <member><filename>/etc/systemd/timesyncd.conf.d/*.conf</filename></member>
+      <member><filename>/run/systemd/timesyncd.conf.d/*.conf</filename></member>
+      <member><filename>/usr/lib/systemd/timesyncd.conf.d/*.conf</filename></member>
+    </simplelist></para>
   </refsynopsisdiv>
 
   <refsect1>
index 971b7e6a9e92948c8a9b752b1d3f765520d01238..1800de903aa49cfd306fb203547d0690f92f6fd7 100644 (file)
 
   <refnamediv>
     <refname>tmpfiles.d</refname>
-    <refpurpose>Configuration for creation, deletion and cleaning of
-    volatile and temporary files</refpurpose>
+    <refpurpose>Configuration for creation, deletion, and cleaning of files and directories</refpurpose>
   </refnamediv>
 
   <refsynopsisdiv>
-    <para><literallayout><filename>/etc/tmpfiles.d/*.conf</filename>
-<filename>/run/tmpfiles.d/*.conf</filename>
-<filename>/usr/lib/tmpfiles.d/*.conf</filename>
-    </literallayout></para>
-
-    <para><literallayout><filename>~/.config/user-tmpfiles.d/*.conf</filename>
-<filename>$XDG_RUNTIME_DIR/user-tmpfiles.d/*.conf</filename>
-<filename>~/.local/share/user-tmpfiles.d/*.conf</filename>
-<filename index='false'>…</filename>
-<filename>/usr/share/user-tmpfiles.d/*.conf</filename>
-    </literallayout></para>
+    <para><simplelist>
+      <member><filename>/etc/tmpfiles.d/*.conf</filename></member>
+      <member><filename>/run/tmpfiles.d/*.conf</filename></member>
+      <member><filename>/usr/lib/tmpfiles.d/*.conf</filename></member>
+    </simplelist></para>
+
+    <para><simplelist>
+      <member><filename>~/.config/user-tmpfiles.d/*.conf</filename></member>
+      <member><filename>$XDG_RUNTIME_DIR/user-tmpfiles.d/*.conf</filename></member>
+      <member><filename>~/.local/share/user-tmpfiles.d/*.conf</filename></member>
+      <member><filename index='false'>…</filename></member>
+      <member><filename>/usr/share/user-tmpfiles.d/*.conf</filename></member>
+    </simplelist></para>
 
     <programlisting>#Type Path                                     Mode User Group Age         Argument
 f     /file/to/create                          mode user group -           content
diff --git a/man/uid0.xml b/man/uid0.xml
new file mode 100644 (file)
index 0000000..0d36ca7
--- /dev/null
@@ -0,0 +1,232 @@
+<?xml version='1.0'?>
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
+  "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+<!-- SPDX-License-Identifier: LGPL-2.1-or-later -->
+
+<refentry id="uid0"
+          xmlns:xi="http://www.w3.org/2001/XInclude">
+
+  <refentryinfo>
+    <title>uid0</title>
+    <productname>systemd</productname>
+  </refentryinfo>
+
+  <refmeta>
+    <refentrytitle>uid0</refentrytitle>
+    <manvolnum>1</manvolnum>
+  </refmeta>
+
+  <refnamediv>
+    <refname>uid0</refname>
+    <refpurpose>Elevate privileges</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv>
+    <cmdsynopsis>
+      <command>uid0</command>
+      <arg choice="opt" rep="repeat">OPTIONS</arg>
+      <arg choice="opt" rep="repeat">COMMAND</arg>
+    </cmdsynopsis>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>Description</title>
+
+    <para><command>uid0</command> may be used to temporarily and interactively acquire elavated or different
+    privileges. It serves a similar purpose as <citerefentry
+    project='man-pages'><refentrytitle>sudo</refentrytitle><manvolnum>8</manvolnum></citerefentry>, but
+    operates differently in a couple of key areas:</para>
+
+    <itemizedlist>
+      <listitem><para>No execution or security context credentials are inherited from the caller into the
+      invoked commands, as they are invoked from a fresh, isolated service forked off the service
+      manager.</para></listitem>
+
+      <listitem><para>Authentication takes place via <ulink
+      url="https://www.freedesktop.org/wiki/Software/polkit">polkit</ulink>, thus isolating the
+      authentication prompt from the terminal (if possible).</para></listitem>
+
+      <listitem><para>An independent pseudo-tty is allocated for the invoked command, detaching its lifecycle and
+      isolating it for security.</para></listitem>
+
+      <listitem><para>No SetUID/SetGID file access bit functionality is used for the implementation.</para></listitem>
+    </itemizedlist>
+
+    <para>Altogether this should provide a safer and more robust alternative to the <command>sudo</command>
+    mechanism, in particular in OS environments where SetUID/SetGID support is not available (for example by
+    setting the <varname>NoNewPrivileges=</varname> variable in
+    <citerefentry><refentrytitle>systemd-system.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>).</para>
+
+    <para>Any session invoked via <command>uid0</command> will run through the
+    <literal>systemd-uid0</literal> PAM stack.</para>
+
+    <para>Note that <command>uid0</command> is implemented as an alternative multi-call invocation of
+    <citerefentry><refentrytitle>systemd-run</refentrytitle><manvolnum>1</manvolnum></citerefentry>.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>Options</title>
+
+    <para>The following options are understood:</para>
+
+    <variablelist>
+      <varlistentry>
+        <term><option>--no-ask-password</option></term>
+
+        <listitem><para>Do not query the user for authentication for privileged operations.</para>
+
+        <xi:include href="version-info.xml" xpointer="v256"/></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>--unit=</option></term>
+
+        <listitem><para>Use this unit name instead of an automatically generated one.</para>
+
+        <xi:include href="version-info.xml" xpointer="v256"/></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>--property=</option></term>
+
+        <listitem><para>Sets a property on the service unit that is created. This option takes an assignment
+        in the same format as
+        <citerefentry><refentrytitle>systemctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>'s
+        <command>set-property</command> command.</para>
+
+        <xi:include href="version-info.xml" xpointer="v256"/>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>--description=</option></term>
+
+        <listitem><para>Provide a description for the service unit that is invoked. If not specified,
+        the command itself will be used as a description. See <varname>Description=</varname> in
+        <citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
+        </para>
+
+        <xi:include href="version-info.xml" xpointer="v256"/></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>--slice=</option></term>
+
+        <listitem><para>Make the new <filename>.service</filename> unit part of the specified slice, instead
+        of <filename>user.slice</filename>.</para>
+
+        <xi:include href="version-info.xml" xpointer="v256"/>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>--slice-inherit</option></term>
+
+        <listitem><para>Make the new <filename>.service</filename> unit part of the slice the
+        <command>uid0</command> itself has been invoked in. This option may be combined with
+        <option>--slice=</option>, in which case the slice specified via <option>--slice=</option> is placed
+        within the slice the <command>uid0</command> command is invoked in.</para>
+
+        <para>Example: consider <command>uid0</command> being invoked in the slice
+        <filename>foo.slice</filename>, and the <option>--slice=</option> argument is
+        <filename>bar</filename>. The unit will then be placed under
+        <filename>foo-bar.slice</filename>.</para>
+
+        <xi:include href="version-info.xml" xpointer="v256"/>
+
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>--user=</option></term>
+        <term><option>-u</option></term>
+        <term><option>--group=</option></term>
+        <term><option>-g</option></term>
+
+        <listitem><para>Switches to the specified user/group instead of root.</para>
+
+        <xi:include href="version-info.xml" xpointer="v256"/>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>--nice=</option></term>
+
+        <listitem><para>Runs the invoked session with the specified nice level.</para>
+
+        <xi:include href="version-info.xml" xpointer="v256"/>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>--chdir=</option></term>
+        <term><option>-D</option></term>
+
+        <listitem><para>Runs the invoked session with the specified working directory. If not specified
+        defaults to the client's current working directory if switching to the root user, or the target
+        user's home directory otherwise.</para>
+
+        <xi:include href="version-info.xml" xpointer="v256"/>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>--setenv=<replaceable>NAME</replaceable>[=<replaceable>VALUE</replaceable>]</option></term>
+
+        <listitem><para>Runs the invoked session with the specified environment variable set. This parameter
+        may be used more than once to set multiple variables. When <literal>=</literal> and
+        <replaceable>VALUE</replaceable> are omitted, the value of the variable with the same name in the
+        invoking environment will be used.</para>
+
+        <xi:include href="version-info.xml" xpointer="v256"/>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>--background=<replaceable>COLOR</replaceable></option></term>
+
+        <listitem><para>Change the terminal background color to the specified ANSI color as long as the
+        session lasts. If not specified, the background will be tinted in a reddish tone when operating as
+        root, and in a yellowish tone when operating under another UID, as reminder of the changed
+        privileges. The color specified should be an ANSI X3.64 SGR background color, i.e. strings such as
+        <literal>40</literal>, <literal>41</literal>, …, <literal>47</literal>, <literal>48;2;…</literal>,
+        <literal>48;5;…</literal>. See <ulink
+        url="https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_(Select_Graphic_Rendition)_parameters">ANSI
+        Escape Code (Wikipedia)</ulink> for details. Set to an empty string to disable.</para>
+
+        <para>Example: <literal>--background=44</literal> for a blue background.</para>
+
+        <xi:include href="version-info.xml" xpointer="v256"/>
+        </listitem>
+      </varlistentry>
+
+      <xi:include href="user-system-options.xml" xpointer="machine" />
+      <xi:include href="standard-options.xml" xpointer="help" />
+      <xi:include href="standard-options.xml" xpointer="version" />
+    </variablelist>
+
+    <para>All command line arguments after the first non-option argument become part of the command line of
+    the launched process. If no command line is specified an interactive shell is invoked. The shell to
+    invoke may be controlled via <option>--setenv=SHELL=…</option> and currently defaults to the
+    <emphasis>originating user's</emphasis> shell (i.e. not the target user's!) if operating locally, or
+    <filename>/bin/sh</filename> when operating with <option>--machine=</option>.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>Exit status</title>
+
+    <para>On success, 0 is returned. If <command>uid0</command> failed to start the session or the specified command fails, a
+    non-zero return value will be returned.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>See Also</title>
+    <para>
+      <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>systemd-run</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+      <citerefentry project='man-pages'><refentrytitle>sudo</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>machinectl</refentrytitle><manvolnum>1</manvolnum></citerefentry>
+    </para>
+  </refsect1>
+
+</refentry>
index 9b7e20997ac4acf92f79a35d7373e2f7e6da5557..1865c1bfed7f375a075c699becd75a0b50a70e0a 100644 (file)
 
       <para>Also see the description of <option>-j</option>/<option>--json=</option> and
       <option>--section=</option>.</para>
+
+      <para>Other tools that may be useful for inspect UKIs:
+      <citerefentry project='man-pages'><refentrytitle>llvm-objdump</refentrytitle><manvolnum>1</manvolnum></citerefentry>
+      <option>-p</option> and <command>pe-inspect</command>.
+      <!-- TODO: add link to pe-inspect man page when it gets one -->
+      </para>
     </refsect2>
   </refsect1>
 
index 06033b8b627dde326ddd28fc2cc1c7c6192fb21a..5ebc0a51e9101c75d72fefa10f23f4da36a4aef0 100644 (file)
@@ -698,7 +698,7 @@ if run_command(ln, '--relative', '--help', check : false).returncode() != 0
         error('ln does not support --relative (added in coreutils 8.16)')
 endif
 
-############################################################
+#####################################################################
 
 gperf = find_program('gperf')
 
@@ -724,7 +724,7 @@ message('gperf len type is @0@'.format(gperf_len_type))
 conf.set('GPERF_LEN_TYPE', gperf_len_type,
          description : 'The type of gperf "len" parameter')
 
-############################################################
+#####################################################################
 
 if not cc.has_header('sys/capability.h')
         error('POSIX caps headers not found')
@@ -744,7 +744,7 @@ foreach header : ['crypt.h',
                    cc.has_header(header))
 endforeach
 
-############################################################
+#####################################################################
 
 fallback_hostname = get_option('fallback-hostname')
 if fallback_hostname == '' or fallback_hostname[0] == '.' or fallback_hostname[0] == '-'
@@ -1659,13 +1659,13 @@ conf.set10('ENABLE_TIMEDATECTL', get_option('timedated') or get_option('timesync
 
 conf.set10('SYSTEMD_SLOW_TESTS_DEFAULT', slow_tests)
 
-############################################################
+#####################################################################
 
 pymod = import('python')
 python = pymod.find_installation('python3', required : true, modules : ['jinja2'])
 python_39 = python.language_version().version_compare('>=3.9')
 
-############################################################
+#####################################################################
 
 if conf.get('BPF_FRAMEWORK') == 1
         bpf_clang_flags = [
@@ -1814,7 +1814,7 @@ endif
 want_ukify = get_option('ukify').require(python_39, error_message : 'Python >= 3.9 required').allowed()
 conf.set10('ENABLE_UKIFY', want_ukify)
 
-############################################################
+#####################################################################
 
 check_version_history_py = find_program('tools/check-version-history.py')
 elf2efi_py = find_program('tools/elf2efi.py')
@@ -1832,7 +1832,7 @@ update_man_rules_py = find_program('tools/update-man-rules.py')
 update_syscall_tables_sh = find_program('tools/update-syscall-tables.sh')
 xml_helper_py = find_program('tools/xml_helper.py')
 
-############################################################
+#####################################################################
 
 version_tag = get_option('version-tag')
 version_h = vcs_tag(
@@ -1849,13 +1849,13 @@ if shared_lib_tag == ''
         shared_lib_tag = meson.project_version()
 endif
 
-############################################################
+#####################################################################
 
 if get_option('b_coverage')
         userspace_c_args += ['-include', 'src/basic/coverage.h']
 endif
 
-############################################################
+#####################################################################
 
 config_h = configure_file(
         output : 'config.h',
@@ -1873,7 +1873,7 @@ userspace = declare_dependency(
 
 man_page_depends = []
 
-############################################################
+#####################################################################
 
 simple_tests = []
 libsystemd_tests = []
@@ -2006,7 +2006,7 @@ install_libudev_static = static_library(
         c_args : static_libudev_pic ? [] : ['-fno-PIC'],
         pic : static_libudev_pic)
 
-############################################################
+#####################################################################
 
 runtest_env = custom_target(
         'systemd-runtest.env',
@@ -2027,7 +2027,7 @@ if '-O2' in c_args and '-flto=auto' in c_args
         test_cflags += cc.first_supported_argument('-Wno-maybe-uninitialized')
 endif
 
-############################################################
+#####################################################################
 
 executable_template = {
         'include_directories' : includes,
@@ -2123,7 +2123,7 @@ module_additional_kwargs = {
         'dependencies' : userspace,
 }
 
-############################################################
+#####################################################################
 
 # systemd-analyze requires 'libcore'
 subdir('src/core')
@@ -2241,7 +2241,7 @@ subdir('mime')
 
 alias_target('devel', libsystemd_pc, libudev_pc, systemd_pc, udev_pc)
 
-############################################################
+#####################################################################
 
 foreach test : simple_tests
         executables += test_template + { 'sources' : [test] }
@@ -2378,7 +2378,7 @@ endforeach
 
 alias_target('fuzzers', fuzzer_exes)
 
-############################################################
+#####################################################################
 
 test_dlopen = executables_by_name.get('test-dlopen')
 
@@ -2442,7 +2442,7 @@ foreach dict : modules
         endif
 endforeach
 
-############################################################
+#####################################################################
 
 ukify = custom_target(
         'ukify',
@@ -2461,12 +2461,12 @@ if want_ukify
                                              libexecdir / 'ukify'))
 endif
 
-############################################################
+#####################################################################
 
 subdir('rules.d')
 subdir('test')
 
-############################################################
+#####################################################################
 
 subdir('docs/sysvinit')
 subdir('docs/var-log')
@@ -2508,7 +2508,7 @@ install_subdir('LICENSES',
 
 install_emptydir(systemdstatedir)
 
-############################################################
+#####################################################################
 
 # Ensure that changes to the docs/ directory do not break the
 # basic Github pages build. But only run it in developer mode,
@@ -2524,7 +2524,7 @@ if get_option('mode') == 'developer' and want_tests != 'false' and jekyll.found(
                      '--destination', project_build_root / '_site'])
 endif
 
-############################################################
+#####################################################################
 
 check_help = find_program('tools/check-help.sh')
 check_version = find_program('tools/check-version.sh')
@@ -2593,7 +2593,7 @@ foreach tuple : fuzz_sanitizers
         endif
 endforeach
 
-############################################################
+#####################################################################
 
 if git.found()
         all_files = run_command(
@@ -2650,7 +2650,7 @@ if git.found()
                            'HEAD'])
 endif
 
-############################################################
+#####################################################################
 
 check_api_docs_sh = find_program('tools/check-api-docs.sh')
 run_target(
@@ -2670,7 +2670,7 @@ if not meson.is_cross_build()
                 command : [export_dbus_interfaces_py, '@OUTPUT@', dbus_programs])
 endif
 
-############################################################
+#####################################################################
 
 alt_time_epoch = run_command('date', '-Is', '-u', '-d', '@@0@'.format(time_epoch),
                              check : true).stdout().strip()
index 9961407a04319770c82ab0a7a8ba20025fcfccf6..b02c24cdd90af5bd01b538925af4bc6f34a5717b 100644 (file)
@@ -4,7 +4,7 @@
 Images=system
 
 [Output]
-OutputDirectory=mkosi.output
+@OutputDirectory=mkosi.output
 BuildDirectory=mkosi.builddir
 CacheDirectory=mkosi.cache
 
@@ -18,8 +18,6 @@ Environment=ASAN_OPTIONS=verify_asan_link_order=false
 @Incremental=yes
 @QemuMem=2G
 @RuntimeSize=8G
-# Make sure we don't trigger systemd-firstboot prompting for the root password.
-Credentials=passwd.plaintext-password.root=
 KernelCommandLineExtra=systemd.crash_shell
                        systemd.log_level=debug
                        systemd.log_ratelimit_kmsg=0
@@ -37,3 +35,4 @@ KernelCommandLineExtra=systemd.crash_shell
                        selinux=0
                        enforcing=0
                        systemd.early_core_pattern=/core
+                       systemd.firstboot=no
index f26098cedf5c7835c1329f842b9fbd7374235392..9524fdb103a6bc4f940e1671990e5b3fadcb4d96 100755 (executable)
@@ -44,7 +44,7 @@ EOF
 fi
 
 if [ ! -f "$BUILDDIR"/build.ninja ]; then
-    sysvinit_path=$(realpath /etc/init.d)
+    [[ -d /etc/rc.d/init.d ]] && sysvinit_path="/etc/rc.d/init.d" || sysvinit_path="/etc/init.d"
 
     if [ "$ID" = "centos" ] && [ "$VERSION" = "8" ]; then
         UKIFY="disabled"
@@ -70,7 +70,7 @@ if [ ! -f "$BUILDDIR"/build.ninja ]; then
         -D tests=unsafe
         -D slow-tests="${SLOW_TESTS:-false}"
         -D create-log-dirs=false
-        -D pamconfdir=no
+        -D pamconfdir=/usr/lib/pam.d/
         -D utmp=true
         -D hibernate=true
         -D ldconfig=true
index 6c6d0457752b1a846782232a3a7a71e00323f414..be8586ce3b9e8c1e7ab45f67cd920408ff8fceef 100644 (file)
@@ -15,10 +15,11 @@ Packages=
 
 BuildPackages=
         acl
-        diffutils
-        gawk
         binutils
         clang
+        diffutils
+        gawk
+        gdb
         gettext
         git
         gperf
@@ -30,5 +31,6 @@ BuildPackages=
         pkgconf
         rsync
         sed
+        strace
         tar
         zstd
index 7612f221cc50783ed205721905fc45565fd458c7..6948f8eb7ce8eb65b6a1f4e2220b0997b26ae4db 100644 (file)
@@ -33,6 +33,7 @@ Packages=
         strace
         systemd
         tmux
+        tar
         tree
         udev
         util-linux
index 145c79bf63d5f718a90bf4a2219d72a3bbae6cf8..67d46432d40f81dd20c675b161f67bc72fc30280 100644 (file)
@@ -14,6 +14,7 @@ Packages=
         integritysetup
         iproute
         iproute-tc
+        kernel-core
         libcap-ng-utils
         netcat
         openssh-server
index af4862d4b147031ac251a0d823833b2afab1557e..146e03a89551fe02a256c4a662504700f9370c37 100644 (file)
@@ -2,3 +2,7 @@
 
 [Match]
 Distribution=centos
+
+[Content]
+Packages=
+        kernel-modules # For squashfs support
diff --git a/mkosi.images/system/mkosi.conf.d/10-centos/mkosi.conf.d/10-centos8.conf b/mkosi.images/system/mkosi.conf.d/10-centos/mkosi.conf.d/10-centos8.conf
deleted file mode 100644 (file)
index 30643e7..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-
-[Match]
-Release=8
-
-[Content]
-Packages=
-        kernel-core-4.18.0-521.el8
-        kernel-modules-4.18.0-521.el8 # For squashfs support
diff --git a/mkosi.images/system/mkosi.conf.d/10-centos/mkosi.conf.d/10-centos9.conf b/mkosi.images/system/mkosi.conf.d/10-centos/mkosi.conf.d/10-centos9.conf
deleted file mode 100644 (file)
index a21739f..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-
-[Match]
-Release=9
-
-[Content]
-Packages=
-        kernel-core
-        kernel-modules
index 281e9464f621af0cb958e115d5149d8af1f8f6ab..5863f03b19c0cc3740871758f8ef695714dc9099 100644 (file)
@@ -8,4 +8,4 @@ Packages=
         btrfs-progs
         compsize
         f2fs-tools
-        kernel-core
+        glibc-langpack-en
index e0728de27985e231b7983e0fe3b53cbc810ddd78..0fec067ebb2a6c8d80c8f7d01d95b7c0c236e63a 100755 (executable)
@@ -83,3 +83,12 @@ if [ "$ID" = "centos" ] && [ "$VERSION" = "8" ]; then
     alternatives --install /usr/bin/python3 python3 /usr/bin/python3.9 1
     alternatives --set python3 /usr/bin/python3.9
 fi
+
+mkdir -p /usr/lib/sysusers.d
+cat >/usr/lib/sysusers.d/testuser.conf <<EOF
+u      testuser        4711    "Test User"     /home/testuser
+EOF
+mkdir -p /usr/lib/tmpfiles.d
+cat >/usr/lib/tmpfiles.d/testuser.conf <<EOF
+q      /home/testuser  0700    4711    4711
+EOF
index bd6f3c23c60b58ea7ee2fc299561fe24668af338..966e449f5f233eab469fc9aae9d3f5f7dfb4ed93 100644 (file)
--- a/po/tr.po
+++ b/po/tr.po
@@ -10,15 +10,16 @@ msgid ""
 msgstr ""
 "Report-Msgid-Bugs-To: \n"
 "POT-Creation-Date: 2023-11-14 21:25+0000\n"
-"PO-Revision-Date: 2023-08-03 17:21+0000\n"
+"PO-Revision-Date: 2023-12-11 13:43+0000\n"
 "Last-Translator: Oğuz Ersen <oguz@ersen.moe>\n"
-"Language-Team: Turkish <https://translate.fedoraproject.org/projects/systemd/master/tr/>\n"
+"Language-Team: Turkish <https://translate.fedoraproject.org/projects/systemd/"
+"master/tr/>\n"
 "Language: tr\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 4.18.2\n"
+"X-Generator: Weblate 5.2.1\n"
 
 #: src/core/org.freedesktop.systemd1.policy.in:22
 msgid "Send passphrase back to system"
@@ -120,11 +121,14 @@ msgstr "Bir kullanıcının ev alanının parolasını değiştirmek kimlik doğ
 #, c-format
 msgid "Home of user %s is currently absent, please plug in the necessary storage device or backing file system."
 msgstr ""
+"%s kullanıcısının ev dizini şu anda mevcut değil, lütfen gerekli depolama "
+"aygıtını veya içeren dosya sistemini bağlayın."
 
 #: src/home/pam_systemd_home.c:292
 #, c-format
 msgid "Too frequent login attempts for user %s, try again later."
 msgstr ""
+"%s kullanıcısı için çok sık oturum açma denemesi, daha sonra tekrar deneyin."
 
 #: src/home/pam_systemd_home.c:304
 msgid "Password: "
@@ -134,116 +138,135 @@ msgstr "Parola: "
 #, c-format
 msgid "Password incorrect or not sufficient for authentication of user %s."
 msgstr ""
+"Parola yanlış veya %s kullanıcısının kimlik doğrulaması için yeterli değil."
 
 #: src/home/pam_systemd_home.c:307
 msgid "Sorry, try again: "
-msgstr ""
+msgstr "Üzgünüm, tekrar deneyin: "
 
 #: src/home/pam_systemd_home.c:329
 msgid "Recovery key: "
-msgstr ""
+msgstr "Kurtarma anahtarı: "
 
 #: src/home/pam_systemd_home.c:331
 #, c-format
 msgid "Password/recovery key incorrect or not sufficient for authentication of user %s."
 msgstr ""
+"Parola/kurtarma anahtarı yanlış veya %s kullanıcısının kimlik doğrulaması "
+"için yeterli değil."
 
 #: src/home/pam_systemd_home.c:332
 msgid "Sorry, reenter recovery key: "
-msgstr ""
+msgstr "Üzgünüm, kurtarma anahtarını yeniden girin: "
 
 #: src/home/pam_systemd_home.c:352
 #, c-format
 msgid "Security token of user %s not inserted."
-msgstr ""
+msgstr "%s kullanıcısının güvenlik belirteci girilmedi."
 
 #: src/home/pam_systemd_home.c:353 src/home/pam_systemd_home.c:356
 msgid "Try again with password: "
-msgstr ""
+msgstr "Parola ile tekrar deneyin: "
 
 #: src/home/pam_systemd_home.c:355
 #, c-format
 msgid "Password incorrect or not sufficient, and configured security token of user %s not inserted."
 msgstr ""
+"Parola yanlış veya yeterli değil ve %s kullanıcısının yapılandırılan "
+"güvenlik belirteci girilmedi."
 
 #: src/home/pam_systemd_home.c:376
 msgid "Security token PIN: "
-msgstr ""
+msgstr "Güvenlik belirteci PIN kodu: "
 
 #: src/home/pam_systemd_home.c:393
 #, c-format
 msgid "Please authenticate physically on security token of user %s."
 msgstr ""
+"Lütfen %s kullanıcısının güvenlik belirteci ile fiziksel olarak kimlik "
+"doğrulaması yapın."
 
 #: src/home/pam_systemd_home.c:404
 #, c-format
 msgid "Please confirm presence on security token of user %s."
-msgstr ""
+msgstr "Lütfen %s kullanıcısının güvenlik belirtecinin varlığını doğrulayın."
 
 #: src/home/pam_systemd_home.c:415
 #, c-format
 msgid "Please verify user on security token of user %s."
 msgstr ""
+"Lütfen %s kullanıcısının güvenlik belirtecindeki kullanıcıyı doğrulayın."
 
 #: src/home/pam_systemd_home.c:424
 msgid "Security token PIN is locked, please unlock it first. (Hint: Removal and re-insertion might suffice.)"
 msgstr ""
+"Güvenlik belirteci PIN kodu kilitli, lütfen önce kilidini açın. (İpucu: "
+"Çıkarma ve yeniden takma yeterli olabilir.)"
 
 #: src/home/pam_systemd_home.c:432
 #, c-format
 msgid "Security token PIN incorrect for user %s."
-msgstr ""
+msgstr "Güvenlik belirteci PIN kodu %s kullanıcısı için yanlış."
 
 #: src/home/pam_systemd_home.c:433 src/home/pam_systemd_home.c:452
 #: src/home/pam_systemd_home.c:471
 msgid "Sorry, retry security token PIN: "
-msgstr ""
+msgstr "Üzgünüm, güvenlik belirteci PIN kodunu yeniden deneyin: "
 
 #: src/home/pam_systemd_home.c:451
 #, c-format
 msgid "Security token PIN of user %s incorrect (only a few tries left!)"
 msgstr ""
+"%s kullanıcısının güvenlik belirteci PIN kodu yanlış (sadece birkaç deneme "
+"kaldı!)"
 
 #: src/home/pam_systemd_home.c:470
 #, c-format
 msgid "Security token PIN of user %s incorrect (only one try left!)"
 msgstr ""
+"%s kullanıcısının güvenlik belirteci PIN kodu yanlış (sadece bir deneme "
+"kaldı!)"
 
 #: src/home/pam_systemd_home.c:616
 #, c-format
 msgid "Home of user %s is currently not active, please log in locally first."
 msgstr ""
+"%s kullanıcısının ev dizini şu anda etkin değil, lütfen önce yerel olarak "
+"oturum açın."
 
 #: src/home/pam_systemd_home.c:618
 #, c-format
 msgid "Home of user %s is currently locked, please unlock locally first."
 msgstr ""
+"%s kullanıcısının ev dizini şu anda kilitli, lütfen önce yerel olarak "
+"kilidini açın."
 
 #: src/home/pam_systemd_home.c:645
 #, c-format
 msgid "Too many unsuccessful login attempts for user %s, refusing."
 msgstr ""
+"%s kullanıcısı için çok fazla başarısız oturum açma denemesi, reddediliyor."
 
 #: src/home/pam_systemd_home.c:868
 msgid "User record is blocked, prohibiting access."
-msgstr ""
+msgstr "Kullanıcı kaydı engellendi, erişim engelleniyor."
 
 #: src/home/pam_systemd_home.c:872
 msgid "User record is not valid yet, prohibiting access."
-msgstr ""
+msgstr "Kullanıcı kaydı henüz geçerli değil, erişim engelleniyor."
 
 #: src/home/pam_systemd_home.c:876
 msgid "User record is not valid anymore, prohibiting access."
-msgstr ""
+msgstr "Kullanıcı kaydı artık geçerli değil, erişim engelleniyor."
 
 #: src/home/pam_systemd_home.c:881 src/home/pam_systemd_home.c:932
 msgid "User record not valid, prohibiting access."
-msgstr ""
+msgstr "Kullanıcı kaydı geçerli değil, erişim engelleniyor."
 
 #: src/home/pam_systemd_home.c:893
 #, c-format
 msgid "Too many logins, try again in %s."
-msgstr ""
+msgstr "Çok fazla oturum açıldı, %s içinde tekrar deneyin."
 
 #: src/home/pam_systemd_home.c:904
 msgid "Password change required."
index 10234fd9e0bad107561a0025b5718a2083efb570..bb241534e430b8fac0f2f9b91285ab65b6128a73 100644 (file)
@@ -32,10 +32,7 @@ ACTION!="add", GOTO="default_end"
 
 SUBSYSTEM=="tty", KERNEL=="ptmx", GROUP="tty", MODE="0666"
 SUBSYSTEM=="tty", KERNEL=="tty", GROUP="tty", MODE="0666"
-SUBSYSTEM=="tty", KERNEL=="tty[0-9]*", GROUP="tty", MODE="0620"
-SUBSYSTEM=="tty", KERNEL=="sclp_line[0-9]*", GROUP="tty", MODE="0620"
-SUBSYSTEM=="tty", KERNEL=="ttysclp[0-9]*", GROUP="tty", MODE="0620"
-SUBSYSTEM=="tty", KERNEL=="3270/tty[0-9]*", GROUP="tty", MODE="0620"
+SUBSYSTEM=="tty", KERNEL=="tty[0-9]*|hvc[0-9]*|sclp_line[0-9]*|ttysclp[0-9]*|3270/tty[0-9]*", GROUP="tty", MODE="0620"
 SUBSYSTEM=="vc", KERNEL=="vcs*|vcsa*", GROUP="tty"
 KERNEL=="tty[A-Z]*[0-9]|ttymxc[0-9]*|pppox[0-9]*|ircomm[0-9]*|noz[0-9]*|rfcomm[0-9]*", GROUP="dialout"
 
index 7e62f8b26b79c16335fa582274be50fb87defe24..5ba70a654500acc408f54e7f0fb9712cf54a537c 100644 (file)
@@ -3,6 +3,8 @@
 ACTION=="remove", GOTO="net_end"
 SUBSYSTEM!="net", GOTO="net_end"
 
+IMPORT{builtin}="hwdb 'net:naming:dr$env{ID_NET_DRIVER}:'"
+
 IMPORT{builtin}="net_id"
 
 SUBSYSTEMS=="usb", IMPORT{builtin}="usb_id", IMPORT{builtin}="hwdb --subsystem=usb"
index 6fbd6fa54c37d9b09feceb8efc6fc614fdec882d..bc61ebe2b17c6f266bc57f54c24652851ace430c 100644 (file)
@@ -152,7 +152,7 @@ int verify_set_unit_path(char **filenames) {
          * Treat explicit empty path to mean that nothing should be appended. */
         old = getenv("SYSTEMD_UNIT_PATH");
         if (!streq_ptr(old, "") &&
-            !strextend_with_separator(&joined, ":", old ?: ""))
+            !strextend_with_separator(&joined, ":", strempty(old)))
                 return -ENOMEM;
 
         assert_se(set_unit_path(joined) >= 0);
@@ -201,19 +201,23 @@ static int verify_executables(Unit *u, const char *root) {
 
         assert(u);
 
-        ExecCommand *exec =
-                u->type == UNIT_SOCKET ? SOCKET(u)->control_command :
-                u->type == UNIT_MOUNT ? MOUNT(u)->control_command :
-                u->type == UNIT_SWAP ? SWAP(u)->control_command : NULL;
-        RET_GATHER(r, verify_executable(u, exec, root));
+        if (u->type == UNIT_MOUNT)
+                FOREACH_ARRAY(i, MOUNT(u)->exec_command, ELEMENTSOF(MOUNT(u)->exec_command))
+                        RET_GATHER(r, verify_executable(u, i, root));
 
         if (u->type == UNIT_SERVICE)
                 FOREACH_ARRAY(i, SERVICE(u)->exec_command, ELEMENTSOF(SERVICE(u)->exec_command))
-                        RET_GATHER(r, verify_executable(u, *i, root));
+                        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))
-                        RET_GATHER(r, verify_executable(u, *i, root));
+                        LIST_FOREACH(command, j, *i)
+                                RET_GATHER(r, verify_executable(u, j, root));
+
+        if (u->type == UNIT_SWAP)
+                FOREACH_ARRAY(i, SWAP(u)->exec_command, ELEMENTSOF(SWAP(u)->exec_command))
+                        RET_GATHER(r, verify_executable(u, i, root));
 
         return r;
 }
index c587adad7b063d824c543fe78f17ca44456867e4..8fb32ab9b6eed7842e5d9e3d4d852729228e233e 100644 (file)
@@ -138,6 +138,12 @@ const char* const systemd_features =
         " -LIBCRYPTSETUP"
 #endif
 
+#if HAVE_LIBCRYPTSETUP_PLUGINS
+        " +LIBCRYPTSETUP_PLUGINS"
+#else
+        " -LIBCRYPTSETUP_PLUGINS"
+#endif
+
 #if HAVE_LIBFDISK
         " +LIBFDISK"
 #else
index 18b16ecc0e5c5786024568b9c11a2859f9aee547..68b1584629219f274894c61d031a57246e19a417 100644 (file)
@@ -2141,15 +2141,14 @@ int cg_kernel_controllers(Set **ret) {
                 _cleanup_free_ char *controller = NULL;
                 int enabled = 0;
 
-                errno = 0;
                 if (fscanf(f, "%ms %*i %*i %i", &controller, &enabled) != 2) {
 
+                        if (ferror(f))
+                                return -errno;
+
                         if (feof(f))
                                 break;
 
-                        if (ferror(f))
-                                return errno_or_else(EIO);
-
                         return -EBADMSG;
                 }
 
index d3bf73385fbcf64ccc35263a105c43b0d2ddc476..7ac47732ba823e825493df44f58edf93fc051fcc 100644 (file)
@@ -458,6 +458,35 @@ int strv_env_assign(char ***l, const char *key, const char *value) {
         return strv_env_replace_consume(l, p);
 }
 
+int strv_env_assignf(char ***l, const char *key, const char *valuef, ...) {
+        int r;
+
+        assert(l);
+        assert(key);
+
+        if (!env_name_is_valid(key))
+                return -EINVAL;
+
+        if (!valuef) {
+                strv_env_unset(*l, key);
+                return 0;
+        }
+
+        _cleanup_free_ char *value = NULL;
+        va_list ap;
+        va_start(ap, valuef);
+        r = vasprintf(&value, valuef, ap);
+        va_end(ap);
+        if (r < 0)
+                return -ENOMEM;
+
+        char *p = strjoin(key, "=", value);
+        if (!p)
+                return -ENOMEM;
+
+        return strv_env_replace_consume(l, p);
+}
+
 int _strv_env_assign_many(char ***l, ...) {
         va_list ap;
         int r;
index f7fb1e90823d9735c581325e30166274d95ce583..8e77cc71d6b8aac27ac256babc69977524d4ec92 100644 (file)
@@ -49,6 +49,7 @@ int strv_env_replace_consume(char ***l, char *p); /* In place ... */
 int strv_env_replace_strdup(char ***l, const char *assignment);
 int strv_env_replace_strdup_passthrough(char ***l, const char *assignment);
 int strv_env_assign(char ***l, const char *key, const char *value);
+int strv_env_assignf(char ***l, const char *key, const char *valuef, ...) _printf_(3, 4);
 int _strv_env_assign_many(char ***l, ...) _sentinel_;
 #define strv_env_assign_many(l, ...) _strv_env_assign_many(l, __VA_ARGS__, NULL)
 
index 0a6a54f8f139dded22de9ac299e9d81320a24e3d..12c256a474e9bb177525d0a3b7f40c2df7099738 100644 (file)
@@ -270,3 +270,11 @@ int parse_ether_addr(const char *s, struct ether_addr *ret) {
         *ret = a.ether;
         return 0;
 }
+
+void ether_addr_mark_random(struct ether_addr *addr) {
+        assert(addr);
+
+        /* see eth_random_addr in the kernel */
+        addr->ether_addr_octet[0] &= 0xfe;        /* clear multicast bit */
+        addr->ether_addr_octet[0] |= 0x02;        /* set local assignment bit (IEEE802) */
+}
index 83ed77d634513d189c30ac84542013833298fd6d..187e4ef5830a47a01acfd715d6013b596dbbdf31 100644 (file)
@@ -113,3 +113,5 @@ static inline bool ether_addr_is_global(const struct ether_addr *addr) {
 
 extern const struct hash_ops ether_addr_hash_ops;
 extern const struct hash_ops ether_addr_hash_ops_free;
+
+void ether_addr_mark_random(struct ether_addr *addr);
index 991889a14eba64d3d07cd2c36296d4efb6b38e8a..64569452832cc46541e7d8558dbf7916d9820d78 100644 (file)
@@ -62,8 +62,10 @@ char* set_iovec_string_field_free(struct iovec *iovec, size_t *n_iovec, const ch
         return x;
 }
 
-void iovec_array_free(struct iovec *iovec, size_t n) {
-        FOREACH_ARRAY(i, iovec, n)
+void iovec_array_free(struct iovec *iovec, size_t n_iovec) {
+        assert(iovec || n_iovec == 0);
+
+        FOREACH_ARRAY(i, iovec, n_iovec)
                 free(i->iov_base);
 
         free(iovec);
index 39feabd42601214956d90d6657bf7e6142aa3cfe..3f5cdd02e103b4620881a4a93d60bcaf2ecd301b 100644 (file)
@@ -41,4 +41,4 @@ static inline bool iovec_is_set(const struct iovec *iovec) {
 char* set_iovec_string_field(struct iovec *iovec, size_t *n_iovec, const char *field, const char *value);
 char* set_iovec_string_field_free(struct iovec *iovec, size_t *n_iovec, const char *field, char *value);
 
-void iovec_array_free(struct iovec *iovec, size_t n);
+void iovec_array_free(struct iovec *iovec, size_t n_iovec);
index dc868c9b8e9acfff9f7f7c0d94deb3ea584e6dd5..0430e33e40df55e22c3773dda826252657430530 100644 (file)
@@ -123,8 +123,7 @@ int parse_ifindex(const char *s) {
 }
 
 int parse_mtu(int family, const char *s, uint32_t *ret) {
-        uint64_t u;
-        size_t m;
+        uint64_t u, m;
         int r;
 
         r = parse_size(s, 1024, &u);
@@ -134,10 +133,16 @@ int parse_mtu(int family, const char *s, uint32_t *ret) {
         if (u > UINT32_MAX)
                 return -ERANGE;
 
-        if (family == AF_INET6)
+        switch (family) {
+        case AF_INET:
+                m = IPV4_MIN_MTU; /* This is 68 */
+                break;
+        case AF_INET6:
                 m = IPV6_MIN_MTU; /* This is 1280 */
-        else
-                m = IPV4_MIN_MTU; /* For all other protocols, including 'unspecified' we assume the IPv4 minimal MTU */
+                break;
+        default:
+                m = 0;
+        }
 
         if (u < m)
                 return -ERANGE;
index 69b5cad575973887a21f07b1b9b16e0200a0bb31..d3821c1f544204f192b4ca9a2ece35c4feedbef6 100644 (file)
@@ -106,6 +106,38 @@ int pidref_set_pidfd_consume(PidRef *pidref, int fd) {
         return r;
 }
 
+int pidref_set_parent(PidRef *ret) {
+        _cleanup_(pidref_done) PidRef parent = PIDREF_NULL;
+        pid_t ppid;
+        int r;
+
+        assert(ret);
+
+        /* Acquires a pidref to our parent process. Deals with the fact that parent processes might exit, and
+         * we get reparented to other processes, with our old parent's PID already being recycled. */
+
+        ppid = getppid();
+        for (;;) {
+                r = pidref_set_pid(&parent, ppid);
+                if (r < 0)
+                        return r;
+
+                if (parent.fd < 0) /* If pidfds are not available, then we are done */
+                        break;
+
+                pid_t now_ppid = getppid();
+                if (now_ppid == ppid) /* If our ppid is still the same, then we are done */
+                        break;
+
+                /* Otherwise let's try again with the new ppid */
+                ppid = now_ppid;
+                pidref_done(&parent);
+        }
+
+        *ret = TAKE_PIDREF(parent);
+        return 0;
+}
+
 void pidref_done(PidRef *pidref) {
         assert(pidref);
 
index dada069357def5f23ecd97b793010a9750cc6be6..a01d4cc85ba078b8f1200895051d9fbd1bccb4fc 100644 (file)
@@ -38,7 +38,7 @@ int pidref_set_pidstr(PidRef *pidref, const char *pid);
 int pidref_set_pidfd(PidRef *pidref, int fd);
 int pidref_set_pidfd_take(PidRef *pidref, int fd); /* takes ownership of the passed pidfd on success*/
 int pidref_set_pidfd_consume(PidRef *pidref, int fd); /* takes ownership of the passed pidfd in both success and failure */
-
+int pidref_set_parent(PidRef *ret);
 static inline int pidref_set_self(PidRef *pidref) {
         return pidref_set_pid(pidref, 0);
 }
index c1f0b2b974c7f0d9f4069c7d6682a5d5cf6bab38..a9f7b87f2899617318d816d95a1c920e45b0efac 100644 (file)
@@ -6,11 +6,14 @@
 #include "errno-util.h"
 #include "extract-word.h"
 #include "fd-util.h"
+#include "fileio.h"
 #include "format-util.h"
 #include "macro.h"
 #include "missing_resource.h"
+#include "process-util.h"
 #include "rlimit-util.h"
 #include "string-table.h"
+#include "strv.h"
 #include "time-util.h"
 
 int setrlimit_closest(int resource, const struct rlimit *rlim) {
@@ -426,3 +429,116 @@ int rlimit_nofile_safe(void) {
 
         return 1;
 }
+
+int pid_getrlimit(pid_t pid, int resource, struct rlimit *ret) {
+
+        static const char * const prefix_table[_RLIMIT_MAX] = {
+                [RLIMIT_CPU]        = "Max cpu time",
+                [RLIMIT_FSIZE]      = "Max file size",
+                [RLIMIT_DATA]       = "Max data size",
+                [RLIMIT_STACK]      = "Max stack size",
+                [RLIMIT_CORE]       = "Max core file size",
+                [RLIMIT_RSS]        = "Max resident set",
+                [RLIMIT_NPROC]      = "Max processes",
+                [RLIMIT_NOFILE]     = "Max open files",
+                [RLIMIT_MEMLOCK]    = "Max locked memory",
+                [RLIMIT_AS]         = "Max address space",
+                [RLIMIT_LOCKS]      = "Max file locks",
+                [RLIMIT_SIGPENDING] = "Max pending signals",
+                [RLIMIT_MSGQUEUE]   = "Max msgqueue size",
+                [RLIMIT_NICE]       = "Max nice priority",
+                [RLIMIT_RTPRIO]     = "Max realtime priority",
+                [RLIMIT_RTTIME]     = "Max realtime timeout",
+        };
+
+        int r;
+
+        assert(resource >= 0);
+        assert(resource < _RLIMIT_MAX);
+        assert(pid >= 0);
+        assert(ret);
+
+        if (pid == 0 || pid == getpid_cached())
+                return RET_NERRNO(getrlimit(resource, ret));
+
+        r = RET_NERRNO(prlimit(pid, resource, /* new_limit= */ NULL, ret));
+        if (!ERRNO_IS_NEG_PRIVILEGE(r))
+                return r;
+
+        /* We don't have access? Then try to go via /proc/$PID/limits. Weirdly that's world readable in
+         * contrast to querying the data via prlimit() */
+
+        const char *p = procfs_file_alloca(pid, "limits");
+        _cleanup_free_ char *limits = NULL;
+
+        r = read_full_virtual_file(p, &limits, NULL);
+        if (r < 0)
+                return -EPERM; /* propagate original permission error if we can't access the limits file */
+
+        _cleanup_strv_free_ char **l = NULL;
+        l = strv_split(limits, "\n");
+        if (!l)
+                return -ENOMEM;
+
+        STRV_FOREACH(i, strv_skip(l, 1)) {
+                _cleanup_free_ char *soft = NULL, *hard = NULL;
+                uint64_t sv, hv;
+                const char *e;
+
+                e = startswith(*i, prefix_table[resource]);
+                if (!e)
+                        continue;
+
+                if (*e != ' ')
+                        continue;
+
+                e += strspn(e, WHITESPACE);
+
+                size_t n;
+                n = strcspn(e, WHITESPACE);
+                if (n == 0)
+                        continue;
+
+                soft = strndup(e, n);
+                if (!soft)
+                        return -ENOMEM;
+
+                e += n;
+                if (*e != ' ')
+                        continue;
+
+                e += strspn(e, WHITESPACE);
+                n = strcspn(e, WHITESPACE);
+                if (n == 0)
+                        continue;
+
+                hard = strndup(e, n);
+                if (!hard)
+                        return -ENOMEM;
+
+                if (streq(soft, "unlimited"))
+                        sv = RLIM_INFINITY;
+                else {
+                        r = safe_atou64(soft, &sv);
+                        if (r < 0)
+                                return r;
+                }
+
+                if (streq(hard, "unlimited"))
+                        hv = RLIM_INFINITY;
+                else {
+                        r = safe_atou64(hard, &hv);
+                        if (r < 0)
+                                return r;
+                }
+
+                *ret = (struct rlimit) {
+                        .rlim_cur = sv,
+                        .rlim_max = hv,
+                };
+
+                return 0;
+        }
+
+        return -ENOTRECOVERABLE;
+}
index 202c3fdc449b82da150b7357f02e4523e9bad370..afc1a1f26223988d1f108d389bb99806b035f148 100644 (file)
@@ -25,3 +25,5 @@ void rlimit_free_all(struct rlimit **rl);
 
 int rlimit_nofile_bump(int limit);
 int rlimit_nofile_safe(void);
+
+int pid_getrlimit(pid_t pid, int resource, struct rlimit *ret);
index 5d9484626d86444c5253396fb6abce34f45be7c8..f8f4e509ad2855da83aa0565c0181963a5da44b5 100644 (file)
@@ -120,39 +120,39 @@ int sigprocmask_many(int how, sigset_t *old, ...) {
 }
 
 static const char *const static_signal_table[] = {
-        [SIGHUP] = "HUP",
-        [SIGINT] = "INT",
-        [SIGQUIT] = "QUIT",
-        [SIGILL] = "ILL",
-        [SIGTRAP] = "TRAP",
-        [SIGABRT] = "ABRT",
-        [SIGBUS] = "BUS",
-        [SIGFPE] = "FPE",
-        [SIGKILL] = "KILL",
-        [SIGUSR1] = "USR1",
-        [SIGSEGV] = "SEGV",
-        [SIGUSR2] = "USR2",
-        [SIGPIPE] = "PIPE",
-        [SIGALRM] = "ALRM",
-        [SIGTERM] = "TERM",
+        [SIGHUP]    = "HUP",
+        [SIGINT]    = "INT",
+        [SIGQUIT]   = "QUIT",
+        [SIGILL]    = "ILL",
+        [SIGTRAP]   = "TRAP",
+        [SIGABRT]   = "ABRT",
+        [SIGBUS]    = "BUS",
+        [SIGFPE]    = "FPE",
+        [SIGKILL]   = "KILL",
+        [SIGUSR1]   = "USR1",
+        [SIGSEGV]   = "SEGV",
+        [SIGUSR2]   = "USR2",
+        [SIGPIPE]   = "PIPE",
+        [SIGALRM]   = "ALRM",
+        [SIGTERM]   = "TERM",
 #ifdef SIGSTKFLT
         [SIGSTKFLT] = "STKFLT",  /* Linux on SPARC doesn't know SIGSTKFLT */
 #endif
-        [SIGCHLD] = "CHLD",
-        [SIGCONT] = "CONT",
-        [SIGSTOP] = "STOP",
-        [SIGTSTP] = "TSTP",
-        [SIGTTIN] = "TTIN",
-        [SIGTTOU] = "TTOU",
-        [SIGURG] = "URG",
-        [SIGXCPU] = "XCPU",
-        [SIGXFSZ] = "XFSZ",
+        [SIGCHLD]   = "CHLD",
+        [SIGCONT]   = "CONT",
+        [SIGSTOP]   = "STOP",
+        [SIGTSTP]   = "TSTP",
+        [SIGTTIN]   = "TTIN",
+        [SIGTTOU]   = "TTOU",
+        [SIGURG]    = "URG",
+        [SIGXCPU]   = "XCPU",
+        [SIGXFSZ]   = "XFSZ",
         [SIGVTALRM] = "VTALRM",
-        [SIGPROF] = "PROF",
-        [SIGWINCH] = "WINCH",
-        [SIGIO] = "IO",
-        [SIGPWR] = "PWR",
-        [SIGSYS] = "SYS"
+        [SIGPROF]   = "PROF",
+        [SIGWINCH]  = "WINCH",
+        [SIGIO]     = "IO",
+        [SIGPWR]    = "PWR",
+        [SIGSYS]    = "SYS"
 };
 
 DEFINE_PRIVATE_STRING_TABLE_LOOKUP(static_signal, int);
index 0b3e845bf4684c023d15a64705540e08a27e2779..72147a99040a17bc1788b8c8b611fc3ed66702ab 100644 (file)
@@ -25,12 +25,12 @@ void siphash24_compress(const void *in, size_t inlen, struct siphash *state);
 
 static inline void siphash24_compress_boolean(bool in, struct siphash *state) {
         uint8_t i = in;
-
         siphash24_compress(&i, sizeof i, state);
 }
 
 static inline void siphash24_compress_usec_t(usec_t in, struct siphash *state) {
-        siphash24_compress(&in, sizeof in, state);
+        uint64_t u = htole64(in);
+        siphash24_compress(&u, sizeof u, state);
 }
 
 static inline void siphash24_compress_safe(const void *in, size_t inlen, struct siphash *state) {
index beb64d8e6c7bf2edce4e30e3d21e0277a1a5126a..4f28d16b5e4b275ae2e66775195e7cbe1f2d1d37 100644 (file)
@@ -872,13 +872,11 @@ bool address_label_valid(const char *p) {
 int getpeercred(int fd, struct ucred *ucred) {
         socklen_t n = sizeof(struct ucred);
         struct ucred u;
-        int r;
 
         assert(fd >= 0);
         assert(ucred);
 
-        r = getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &u, &n);
-        if (r < 0)
+        if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &u, &n) < 0)
                 return -errno;
 
         if (n != sizeof(struct ucred))
@@ -907,8 +905,10 @@ int getpeersec(int fd, char **ret) {
                 if (!s)
                         return -ENOMEM;
 
-                if (getsockopt(fd, SOL_SOCKET, SO_PEERSEC, s, &n) >= 0)
+                if (getsockopt(fd, SOL_SOCKET, SO_PEERSEC, s, &n) >= 0) {
+                        s[n] = 0;
                         break;
+                }
 
                 if (errno != ERANGE)
                         return -errno;
index 3355b749cc01c8af2b2bf3f557140570dc778a25..b740b49775f8789311ba72f3c0d81fd62246e4d1 100644 (file)
@@ -27,6 +27,7 @@
 #include "fd-util.h"
 #include "fileio.h"
 #include "fs-util.h"
+#include "hexdecoct.h"
 #include "inotify-util.h"
 #include "io-util.h"
 #include "log.h"
@@ -1551,3 +1552,218 @@ int set_terminal_cursor_position(int fd, unsigned int row, unsigned int column)
 
         return 0;
 }
+
+void termios_disable_echo(struct termios *termios) {
+        assert(termios);
+
+        termios->c_lflag &= ~(ICANON|ECHO);
+        termios->c_cc[VMIN] = 1;
+        termios->c_cc[VTIME] = 0;
+}
+
+typedef enum BackgroundColorState {
+        BACKGROUND_TEXT,
+        BACKGROUND_ESCAPE,
+        BACKGROUND_BRACKET,
+        BACKGROUND_FIRST_ONE,
+        BACKGROUND_SECOND_ONE,
+        BACKGROUND_SEMICOLON,
+        BACKGROUND_R,
+        BACKGROUND_G,
+        BACKGROUND_B,
+        BACKGROUND_RED,
+        BACKGROUND_GREEN,
+        BACKGROUND_BLUE,
+} BackgroundColorState;
+
+typedef struct BackgroundColorContext {
+        BackgroundColorState state;
+        uint32_t red, green, blue;
+        unsigned red_bits, green_bits, blue_bits;
+} BackgroundColorContext;
+
+static int scan_background_color_response(
+                BackgroundColorContext *context,
+                const char *buf,
+                size_t size) {
+
+        assert(context);
+        assert(buf || size == 0);
+
+        for (size_t i = 0; i < size; i++) {
+                char c = buf[i];
+
+                switch (context->state) {
+
+                case BACKGROUND_TEXT:
+                        context->state = c == '\x1B' ? BACKGROUND_ESCAPE : BACKGROUND_TEXT;
+                        break;
+
+                case BACKGROUND_ESCAPE:
+                        context->state = c == ']' ? BACKGROUND_BRACKET : BACKGROUND_TEXT;
+                        break;
+
+                case BACKGROUND_BRACKET:
+                        context->state = c == '1' ? BACKGROUND_FIRST_ONE : BACKGROUND_TEXT;
+                        break;
+
+                case BACKGROUND_FIRST_ONE:
+                        context->state = c == '1' ? BACKGROUND_SECOND_ONE : BACKGROUND_TEXT;
+                        break;
+
+                case BACKGROUND_SECOND_ONE:
+                        context->state = c == ';' ? BACKGROUND_SEMICOLON : BACKGROUND_TEXT;
+                        break;
+
+                case BACKGROUND_SEMICOLON:
+                        context->state = c == 'r' ? BACKGROUND_R : BACKGROUND_TEXT;
+                        break;
+
+                case BACKGROUND_R:
+                        context->state = c == 'g' ? BACKGROUND_G : BACKGROUND_TEXT;
+                        break;
+
+                case BACKGROUND_G:
+                        context->state = c == 'b' ? BACKGROUND_B : BACKGROUND_TEXT;
+                        break;
+
+                case BACKGROUND_B:
+                        context->state = c == ':' ? BACKGROUND_RED : BACKGROUND_TEXT;
+                        break;
+
+                case BACKGROUND_RED:
+                        if (c == '/')
+                                context->state = context->red_bits > 0 ? BACKGROUND_GREEN : BACKGROUND_TEXT;
+                        else {
+                                int d = unhexchar(c);
+                                if (d < 0 || context->red_bits >= sizeof(context->red)*8)
+                                        context->state = BACKGROUND_TEXT;
+                                else {
+                                        context->red = (context->red << 4) | d;
+                                        context->red_bits += 4;
+                                }
+                        }
+                        break;
+
+                case BACKGROUND_GREEN:
+                        if (c == '/')
+                                context->state = context->green_bits > 0 ? BACKGROUND_BLUE : BACKGROUND_TEXT;
+                        else {
+                                int d = unhexchar(c);
+                                if (d < 0 || context->green_bits >= sizeof(context->green)*8)
+                                        context->state = BACKGROUND_TEXT;
+                                else {
+                                        context->green = (context->green << 4) | d;
+                                        context->green_bits += 4;
+                                }
+                        }
+                        break;
+
+                case BACKGROUND_BLUE:
+                        if (c == '\x07') {
+                                if (context->blue_bits > 0)
+                                        return 1; /* success! */
+
+                                context->state = BACKGROUND_TEXT;
+                        } else {
+                                int d = unhexchar(c);
+                                if (d < 0 || context->blue_bits >= sizeof(context->blue)*8)
+                                        context->state = BACKGROUND_TEXT;
+                                else {
+                                        context->blue = (context->blue << 4) | d;
+                                        context->blue_bits += 4;
+                                }
+                        }
+                        break;
+                }
+
+                /* Reset any colors we might have picked up */
+                if (context->state == BACKGROUND_TEXT) {
+                        /* reset color */
+                        context->red = context->green = context->blue = 0;
+                        context->red_bits = context->green_bits = context->blue_bits = 0;
+                }
+        }
+
+        return 0; /* all good, but not enough data yet */
+}
+
+int get_default_background_color(double *ret_red, double *ret_green, double *ret_blue) {
+        int r;
+
+        assert(ret_red);
+        assert(ret_green);
+        assert(ret_blue);
+
+        if (!colors_enabled())
+                return -EOPNOTSUPP;
+
+        if (isatty(STDOUT_FILENO) < 1 || isatty(STDIN_FILENO) < 1)
+                return -EOPNOTSUPP;
+
+        if (streq_ptr(getenv("TERM"), "linux")) {
+                /* Linux console is black */
+                *ret_red = *ret_green = *ret_blue = 0.0;
+                return 0;
+        }
+
+        struct termios old_termios;
+        if (tcgetattr(STDIN_FILENO, &old_termios) < 0)
+                return -errno;
+
+        struct termios new_termios = old_termios;
+        termios_disable_echo(&new_termios);
+
+        if (tcsetattr(STDOUT_FILENO, TCSADRAIN, &new_termios) < 0)
+                return -errno;
+
+        r = loop_write(STDOUT_FILENO, "\x1B]11;?\x07", SIZE_MAX);
+        if (r < 0)
+                goto finish;
+
+        usec_t end = usec_add(now(CLOCK_MONOTONIC), 100 * USEC_PER_MSEC);
+        char buf[256];
+        size_t buf_full = 0;
+        BackgroundColorContext context = {};
+
+        for (;;) {
+                usec_t n = now(CLOCK_MONOTONIC);
+
+                if (n >= end) {
+                        r = -EOPNOTSUPP;
+                        goto finish;
+                }
+
+                r = fd_wait_for_event(STDIN_FILENO, POLLIN, usec_sub_unsigned(end, n));
+                if (r < 0)
+                        goto finish;
+
+                ssize_t l;
+                l = read(STDIN_FILENO, buf, sizeof(buf) - buf_full);
+                if (l < 0) {
+                        r = -errno;
+                        goto finish;
+                }
+
+                buf_full += l;
+                assert(buf_full <= sizeof(buf));
+
+                r = scan_background_color_response(&context, buf, buf_full);
+                if (r < 0)
+                        goto finish;
+                if (r > 0) {
+                        assert(context.red_bits > 0);
+                        *ret_red = (double) context.red / ((UINT64_C(1) << context.red_bits) - 1);
+                        assert(context.green_bits > 0);
+                        *ret_green = (double) context.green / ((UINT64_C(1) << context.green_bits) - 1);
+                        assert(context.blue_bits > 0);
+                        *ret_blue = (double) context.blue / ((UINT64_C(1) << context.blue_bits) - 1);
+                        r = 0;
+                        goto finish;
+                }
+        }
+
+finish:
+        (void) tcsetattr(STDOUT_FILENO, TCSADRAIN, &old_termios);
+        return r;
+}
index cae42887c44a89659bde1d92c435f7cbe08813b0..1ec057c2dd5be75efb345df23968b04ca22a10f7 100644 (file)
@@ -6,6 +6,7 @@
 #include <stdio.h>
 #include <syslog.h>
 #include <sys/types.h>
+#include <termios.h>
 
 #include "macro.h"
 #include "time-util.h"
@@ -168,7 +169,6 @@ bool underline_enabled(void);
 bool dev_console_colors_enabled(void);
 
 static inline bool colors_enabled(void) {
-
         /* Returns true if colors are considered supported on our stdout. */
         return get_color_mode() != COLOR_OFF;
 }
@@ -286,3 +286,7 @@ static inline const char* ansi_highlight_green_red(bool b) {
 
 /* This assumes there is a 'tty' group */
 #define TTY_MODE 0620
+
+void termios_disable_echo(struct termios *termios);
+
+int get_default_background_color(double *ret_red, double *ret_green, double *ret_blue);
index ed4c1aabd489f1674cc1ac99d9cd6cf2fe645ef8..29373477f486deda53d5975a55ca3e24430e409f 100644 (file)
@@ -219,6 +219,9 @@ static inline int usleep_safe(usec_t usec) {
          * ⚠️ Note we are not using plain nanosleep() here, since that operates on CLOCK_REALTIME, not
          *    CLOCK_MONOTONIC! */
 
+        if (usec == 0)
+                return 0;
+
         // FIXME: use RET_NERRNO() macro here. Currently, this header cannot include errno-util.h.
         return clock_nanosleep(CLOCK_MONOTONIC, 0, TIMESPEC_STORE(usec), NULL) < 0 ? -errno : 0;
 }
index 846359927612a22532f9435dedc902b4b2ddada0..d933d9fa5c1faf0c3e377423f1e6eb3c37c4edea 100644 (file)
@@ -180,6 +180,30 @@ bool uid_range_covers(const UidRange *range, uid_t start, uid_t nr) {
         return false;
 }
 
+int uid_map_read_one(FILE *f, uid_t *ret_base, uid_t *ret_shift, uid_t *ret_range) {
+        uid_t uid_base, uid_shift, uid_range;
+        int r;
+
+        assert(f);
+        assert(ret_base);
+        assert(ret_shift);
+        assert(ret_range);
+
+        errno = 0;
+        r = fscanf(f, UID_FMT " " UID_FMT " " UID_FMT "\n", &uid_base, &uid_shift, &uid_range);
+        if (r == EOF)
+                return errno_or_else(ENOMSG);
+        assert(r >= 0);
+        if (r != 3)
+                return -EBADMSG;
+
+        *ret_base = uid_base;
+        *ret_shift = uid_shift;
+        *ret_range = uid_range;
+
+        return 0;
+}
+
 int uid_range_load_userns(UidRange **ret, const char *path) {
         _cleanup_(uid_range_freep) UidRange *range = NULL;
         _cleanup_fclose_ FILE *f = NULL;
@@ -212,18 +236,12 @@ int uid_range_load_userns(UidRange **ret, const char *path) {
 
         for (;;) {
                 uid_t uid_base, uid_shift, uid_range;
-                int k;
-
-                errno = 0;
-                k = fscanf(f, UID_FMT " " UID_FMT " " UID_FMT "\n", &uid_base, &uid_shift, &uid_range);
-                if (k == EOF) {
-                        if (ferror(f))
-                                return errno_or_else(EIO);
 
+                r = uid_map_read_one(f, &uid_base, &uid_shift, &uid_range);
+                if (r == -ENOMSG)
                         break;
-                }
-                if (k != 3)
-                        return -EBADMSG;
+                if (r < 0)
+                        return r;
 
                 r = uid_range_add_internal(&range, uid_base, uid_range, /* coalesce = */ false);
                 if (r < 0)
index 461a5117373bbe2c12c4d2c78122af7d9b81a8a9..bfe78926698c150cb8b4b0d554c956783c4e51d5 100644 (file)
@@ -31,4 +31,6 @@ static inline bool uid_range_contains(const UidRange *range, uid_t uid) {
         return uid_range_covers(range, uid, 1);
 }
 
+int uid_map_read_one(FILE *f, uid_t *ret_base, uid_t *ret_shift, uid_t *ret_range);
+
 int uid_range_load_userns(UidRange **ret, const char *path);
index a0b6fbcd658c230c49930186a7c6683c70d34f42..09aebabcd5e35a4d3fc503eb202a531dd86d60d9 100644 (file)
@@ -21,6 +21,7 @@
 #include "stat-util.h"
 #include "string-table.h"
 #include "string-util.h"
+#include "uid-range.h"
 #include "virt.h"
 
 enum {
@@ -814,7 +815,7 @@ Virtualization detect_virtualization(void) {
 
 static int userns_has_mapping(const char *name) {
         _cleanup_fclose_ FILE *f = NULL;
-        uid_t a, b, c;
+        uid_t base, shift, range;
         int r;
 
         f = fopen(name, "re");
@@ -823,26 +824,22 @@ static int userns_has_mapping(const char *name) {
                 return errno == ENOENT ? false : -errno;
         }
 
-        errno = 0;
-        r = fscanf(f, UID_FMT " " UID_FMT " " UID_FMT "\n", &a, &b, &c);
-        if (r == EOF) {
-                if (ferror(f))
-                        return log_debug_errno(errno_or_else(EIO), "Failed to read %s: %m", name);
-
-                log_debug("%s is empty, we're in an uninitialized user namespace", name);
+        r = uid_map_read_one(f, &base, &shift, &range);
+        if (r == -ENOMSG) {
+                log_debug("%s is empty, we're in an uninitialized user namespace.", name);
                 return true;
         }
-        if (r != 3)
-                return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "Failed to parse %s: %m", name);
+        if (r < 0)
+                return log_debug_errno(r, "Failed to read %s: %m", name);
 
-        if (a == 0 && b == 0 && c == UINT32_MAX) {
+        if (base == 0 && shift == 0 && range == UINT32_MAX) {
                 /* The kernel calls mappings_overlap() and does not allow overlaps */
                 log_debug("%s has a full 1:1 mapping", name);
                 return false;
         }
 
         /* Anything else implies that we are in a user namespace */
-        log_debug("Mapping found in %s, we're in a user namespace", name);
+        log_debug("Mapping found in %s, we're in a user namespace.", name);
         return true;
 }
 
index bacbbb293903b4c9886b2ca10c25caef75320157..d1bd3c681c7c1ec5db5ed10281ecb38388f55c5a 100644 (file)
@@ -318,6 +318,46 @@ static int create_subdirs(const char *root, const char * const *subdirs) {
         return 0;
 }
 
+static int update_efi_boot_binaries(const char *esp_path, const char *source_path) {
+        _cleanup_closedir_ DIR *d = NULL;
+        _cleanup_free_ char *p = NULL;
+        int r, ret = 0;
+
+        r = chase_and_opendir("/EFI/BOOT", esp_path, CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS, &p, &d);
+        if (r == -ENOENT)
+                return 0;
+        if (r < 0)
+                return log_error_errno(r, "Failed to open directory \"%s/EFI/BOOT\": %m", esp_path);
+
+        FOREACH_DIRENT(de, d, break) {
+                _cleanup_close_ int fd = -EBADF;
+                _cleanup_free_ char *v = NULL;
+
+                if (!endswith_no_case(de->d_name, ".efi"))
+                        continue;
+
+                fd = openat(dirfd(d), de->d_name, O_RDONLY|O_CLOEXEC);
+                if (fd < 0)
+                        return log_error_errno(errno, "Failed to open \"%s/%s\" for reading: %m", p, de->d_name);
+
+                r = get_file_version(fd, &v);
+                if (r == -ESRCH)
+                        continue;  /* No version information */
+                if (r < 0)
+                        return r;
+                if (startswith(v, "systemd-boot ")) {
+                        _cleanup_free_ char *dest_path = NULL;
+
+                        dest_path = path_join(p, de->d_name);
+                        if (!dest_path)
+                                return log_oom();
+
+                        RET_GATHER(ret, copy_file_with_version_check(source_path, dest_path, /* force = */ false));
+                }
+        }
+
+        return ret;
+}
 
 static int copy_one_file(const char *esp_path, const char *name, bool force) {
         char *root = IN_SET(arg_install_source, ARG_INSTALL_SOURCE_AUTO, ARG_INSTALL_SOURCE_IMAGE) ? arg_root : NULL;
@@ -371,9 +411,12 @@ static int copy_one_file(const char *esp_path, const char *name, bool force) {
                 if (r < 0)
                         return log_error_errno(r, "Failed to resolve path %s under directory %s: %m", v, esp_path);
 
-                r = copy_file_with_version_check(source_path, default_dest_path, force);
-                if (r < 0 && ret == 0)
-                        ret = r;
+                RET_GATHER(ret, copy_file_with_version_check(source_path, default_dest_path, force));
+
+                /* If we were installed under any other name in /EFI/BOOT, make sure we update those binaries
+                 * as well. */
+                if (!force)
+                        RET_GATHER(ret, update_efi_boot_binaries(esp_path, source_path));
         }
 
         return ret;
@@ -845,9 +888,6 @@ static int remove_boot_efi(const char *esp_path) {
                 if (!endswith_no_case(de->d_name, ".efi"))
                         continue;
 
-                if (!startswith_no_case(de->d_name, "boot"))
-                        continue;
-
                 fd = openat(dirfd(d), de->d_name, O_RDONLY|O_CLOEXEC);
                 if (fd < 0)
                         return log_error_errno(errno, "Failed to open \"%s/%s\" for reading: %m", p, de->d_name);
index 16b2eaed072bf852f69f5fdba52b495a3b56c9a6..c8041c4bd1f4c67e3ad791d97769de8094232940 100644 (file)
@@ -187,7 +187,6 @@ static int status_variables(void) {
 static int enumerate_binaries(
                 const char *esp_path,
                 const char *path,
-                const char *prefix,
                 char **previous,
                 bool *is_first) {
 
@@ -213,9 +212,6 @@ static int enumerate_binaries(
                 if (!endswith_no_case(de->d_name, ".efi"))
                         continue;
 
-                if (prefix && !startswith_no_case(de->d_name, prefix))
-                        continue;
-
                 filename = path_join(p, de->d_name);
                 if (!filename)
                         return log_oom();
@@ -272,11 +268,11 @@ static int status_binaries(const char *esp_path, sd_id128_t partition) {
                 printf(" (/dev/disk/by-partuuid/" SD_ID128_UUID_FORMAT_STR ")", SD_ID128_FORMAT_VAL(partition));
         printf("\n");
 
-        r = enumerate_binaries(esp_path, "EFI/systemd", NULL, &last, &is_first);
+        r = enumerate_binaries(esp_path, "EFI/systemd", &last, &is_first);
         if (r < 0)
                 goto fail;
 
-        k = enumerate_binaries(esp_path, "EFI/BOOT", "boot", &last, &is_first);
+        k = enumerate_binaries(esp_path, "EFI/BOOT", &last, &is_first);
         if (k < 0) {
                 r = k;
                 goto fail;
index 4237e694c01223bba2dffbe7c50218bf51746647..4c0b3ddf9c27972efcc9430652891053fc901a17 100644 (file)
@@ -2188,7 +2188,7 @@ int bus_cgroup_set_property(
                                 c->restrict_network_interfaces_is_allow_list = is_allow_list;
 
                         STRV_FOREACH(s, l) {
-                                if (!ifname_valid(*s)) {
+                                if (!ifname_valid_full(*s, IFNAME_VALID_ALTERNATIVE)) {
                                         log_full(LOG_WARNING, "Invalid interface name, ignoring: %s", *s);
                                         continue;
                                 }
index 4daa1cefd334a5d9b0fc9c6da39a4e564c45f879..2c6dce0a0882b1c0a30706ed812be46b11ac495b 100644 (file)
@@ -67,6 +67,7 @@ static BUS_DEFINE_PROPERTY_GET(property_get_cpu_sched_policy, "i", ExecContext,
 static BUS_DEFINE_PROPERTY_GET(property_get_cpu_sched_priority, "i", ExecContext, exec_context_get_cpu_sched_priority);
 static BUS_DEFINE_PROPERTY_GET(property_get_coredump_filter, "t", ExecContext, exec_context_get_coredump_filter);
 static BUS_DEFINE_PROPERTY_GET(property_get_timer_slack_nsec, "t", ExecContext, exec_context_get_timer_slack_nsec);
+static BUS_DEFINE_PROPERTY_GET(property_get_set_login_environment, "b", ExecContext, exec_context_get_set_login_environment);
 
 static int property_get_environment_files(
                 sd_bus *bus,
@@ -1038,7 +1039,7 @@ const sd_bus_vtable bus_exec_vtable[] = {
         SD_BUS_PROPERTY("User", "s", NULL, offsetof(ExecContext, user), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("Group", "s", NULL, offsetof(ExecContext, group), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("DynamicUser", "b", bus_property_get_bool, offsetof(ExecContext, dynamic_user), SD_BUS_VTABLE_PROPERTY_CONST),
-        SD_BUS_PROPERTY("SetLoginEnvironment", "b", bus_property_get_tristate, offsetof(ExecContext, set_login_environment), SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("SetLoginEnvironment", "b", property_get_set_login_environment, 0, SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("RemoveIPC", "b", bus_property_get_bool, offsetof(ExecContext, remove_ipc), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("SetCredential", "a(say)", property_get_set_credential, 0, SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("SetCredentialEncrypted", "a(say)", property_get_set_credential, 0, SD_BUS_VTABLE_PROPERTY_CONST),
index 41f4ee399ef748d29d74017f42c12a7abe026a04..77cf6f003d7ada1d92a53f8f4a319d5a5c4a7ce8 100644 (file)
@@ -166,9 +166,7 @@ static int bus_service_method_mount(sd_bus_message *message, void *userdata, sd_
         r = bus_verify_manage_units_async_full(
                         u,
                         is_image ? "mount-image" : "bind-mount",
-                        CAP_SYS_ADMIN,
                         N_("Authentication is required to mount on '$(unit)'."),
-                        true,
                         message,
                         error);
         if (r < 0)
index 48b7e10ea56cdf63ab06df32603bb72fed2b98d3..8b4983dcb5f9e874c9fd3a52c23b71eb3028e0e1 100644 (file)
@@ -408,9 +408,7 @@ int bus_unit_method_start_generic(
         r = bus_verify_manage_units_async_full(
                         u,
                         verb,
-                        CAP_SYS_ADMIN,
                         polkit_message_for_job[job_type],
-                        true,
                         message,
                         error);
         if (r < 0)
@@ -491,9 +489,7 @@ int bus_unit_method_enqueue_job(sd_bus_message *message, void *userdata, sd_bus_
         r = bus_verify_manage_units_async_full(
                         u,
                         jtype,
-                        CAP_SYS_ADMIN,
                         polkit_message_for_job[type],
-                        true,
                         message,
                         error);
         if (r < 0)
@@ -549,9 +545,7 @@ int bus_unit_method_kill(sd_bus_message *message, void *userdata, sd_bus_error *
         r = bus_verify_manage_units_async_full(
                         u,
                         "kill",
-                        CAP_KILL,
                         N_("Authentication is required to send a UNIX signal to the processes of '$(unit)'."),
-                        true,
                         message,
                         error);
         if (r < 0)
@@ -579,9 +573,7 @@ int bus_unit_method_reset_failed(sd_bus_message *message, void *userdata, sd_bus
         r = bus_verify_manage_units_async_full(
                         u,
                         "reset-failed",
-                        CAP_SYS_ADMIN,
                         N_("Authentication is required to reset the \"failed\" state of '$(unit)'."),
-                        true,
                         message,
                         error);
         if (r < 0)
@@ -611,9 +603,7 @@ int bus_unit_method_set_properties(sd_bus_message *message, void *userdata, sd_b
         r = bus_verify_manage_units_async_full(
                         u,
                         "set-property",
-                        CAP_SYS_ADMIN,
                         N_("Authentication is required to set properties on '$(unit)'."),
-                        true,
                         message,
                         error);
         if (r < 0)
@@ -641,9 +631,7 @@ int bus_unit_method_ref(sd_bus_message *message, void *userdata, sd_bus_error *e
         r = bus_verify_manage_units_async_full(
                         u,
                         "ref",
-                        CAP_SYS_ADMIN,
-                        NULL,
-                        false,
+                        /* polkit_message= */ NULL,
                         message,
                         error);
         if (r < 0)
@@ -712,9 +700,7 @@ int bus_unit_method_clean(sd_bus_message *message, void *userdata, sd_bus_error
         r = bus_verify_manage_units_async_full(
                         u,
                         "clean",
-                        CAP_DAC_OVERRIDE,
                         N_("Authentication is required to delete files and directories associated with '$(unit)'."),
-                        true,
                         message,
                         error);
         if (r < 0)
@@ -760,9 +746,7 @@ static int bus_unit_method_freezer_generic(sd_bus_message *message, void *userda
         r = bus_verify_manage_units_async_full(
                         u,
                         perm,
-                        CAP_SYS_ADMIN,
                         N_("Authentication is required to freeze or thaw the processes of '$(unit)' unit."),
-                        true,
                         message,
                         error);
         if (r < 0)
index d680a64268670c1108bd3505aee5f817751d065a..822a17e49ff28cbf8cd0a1085923ae9b8f029cbc 100644 (file)
@@ -151,9 +151,7 @@ int bus_set_transient_usec_internal(
 int bus_verify_manage_units_async_full(
                 Unit *u,
                 const char *verb,
-                int capability,
                 const char *polkit_message,
-                bool interactive,
                 sd_bus_message *call,
                 sd_bus_error *error) {
 
@@ -171,11 +169,8 @@ int bus_verify_manage_units_async_full(
 
         return bus_verify_polkit_async(
                         call,
-                        capability,
                         "org.freedesktop.systemd1.manage-units",
                         details,
-                        interactive,
-                        UID_INVALID,
                         &u->manager->polkit_registry,
                         error);
 }
index 9464b25516d96ed94e4eb39fd18b89a72261f4e0..ee944c166ce7226bf7637a8fdb8d45978a1b3563 100644 (file)
@@ -249,7 +249,7 @@ static inline int bus_set_transient_usec(Unit *u, const char *name, usec_t *p, s
 static inline int bus_set_transient_usec_fix_0(Unit *u, const char *name, usec_t *p, sd_bus_message *message, UnitWriteFlags flags, sd_bus_error *error) {
         return bus_set_transient_usec_internal(u, name, p, true, message, flags, error);
 }
-int bus_verify_manage_units_async_full(Unit *u, const char *verb, int capability, const char *polkit_message, bool interactive, sd_bus_message *call, sd_bus_error *error);
+int bus_verify_manage_units_async_full(Unit *u, const char *verb, const char *polkit_message, sd_bus_message *call, sd_bus_error *error);
 
 int bus_read_mount_options(sd_bus_message *message, sd_bus_error *error, MountOptions **ret_options, char **ret_format_str, const char *separator);
 
index ba2cec4d7718ad3299108ab2d865f8d8f4665e51..f7d4a97096215dd4a8a4d27e836b7a4e0b73845a 100644 (file)
@@ -1189,22 +1189,46 @@ int bus_track_coldplug(Manager *m, sd_bus_track **t, bool recursive, char **l) {
 }
 
 int bus_verify_manage_units_async(Manager *m, sd_bus_message *call, sd_bus_error *error) {
-        return bus_verify_polkit_async(call, CAP_SYS_ADMIN, "org.freedesktop.systemd1.manage-units", NULL, false, UID_INVALID, &m->polkit_registry, error);
+        return bus_verify_polkit_async(
+                        call,
+                        "org.freedesktop.systemd1.manage-units",
+                        /* details= */ NULL,
+                        &m->polkit_registry,
+                        error);
 }
 
 int bus_verify_manage_unit_files_async(Manager *m, sd_bus_message *call, sd_bus_error *error) {
-        return bus_verify_polkit_async(call, CAP_SYS_ADMIN, "org.freedesktop.systemd1.manage-unit-files", NULL, false, UID_INVALID, &m->polkit_registry, error);
+        return bus_verify_polkit_async(
+                        call,
+                        "org.freedesktop.systemd1.manage-unit-files",
+                        /* details= */ NULL,
+                        &m->polkit_registry,
+                        error);
 }
 
 int bus_verify_reload_daemon_async(Manager *m, sd_bus_message *call, sd_bus_error *error) {
-        return bus_verify_polkit_async(call, CAP_SYS_ADMIN, "org.freedesktop.systemd1.reload-daemon", NULL, false, UID_INVALID, &m->polkit_registry, error);
+        return bus_verify_polkit_async(
+                        call,
+                        "org.freedesktop.systemd1.reload-daemon",
+                        /* details= */ NULL,
+                        &m->polkit_registry, error);
 }
 
 int bus_verify_set_environment_async(Manager *m, sd_bus_message *call, sd_bus_error *error) {
-        return bus_verify_polkit_async(call, CAP_SYS_ADMIN, "org.freedesktop.systemd1.set-environment", NULL, false, UID_INVALID, &m->polkit_registry, error);
+        return bus_verify_polkit_async(
+                        call,
+                        "org.freedesktop.systemd1.set-environment",
+                        /* details= */ NULL,
+                        &m->polkit_registry,
+                        error);
 }
 int bus_verify_bypass_dump_ratelimit_async(Manager *m, sd_bus_message *call, sd_bus_error *error) {
-        return bus_verify_polkit_async(call, CAP_SYS_ADMIN, "org.freedesktop.systemd1.bypass-dump-ratelimit", NULL, false, UID_INVALID, &m->polkit_registry, error);
+        return bus_verify_polkit_async(
+                        call,
+                        "org.freedesktop.systemd1.bypass-dump-ratelimit",
+                        /* details= */ NULL,
+                        &m->polkit_registry,
+                        error);
 }
 
 uint64_t manager_bus_n_queued_write(Manager *m) {
index d07adb2d2aebdae862128e094fb67f80becec301..6b2d7c3e247f045cd111dfa60738ec943860da84 100644 (file)
@@ -1291,6 +1291,7 @@ const UnitVTable device_vtable = {
         .status_message_formats = {
                 .starting_stopping = {
                         [0] = "Expecting device %s...",
+                        [1] = "Waiting for device %s to disappear...",
                 },
                 .finished_start_job = {
                         [JOB_DONE]       = "Found device %s.",
index 7ce26582969645ae7a3ab70d25b574b60133f124..61f66b6914a65b913ec9b3acc611cdb7fd7b7079 100644 (file)
@@ -1883,7 +1883,7 @@ static int build_environment(
                                                     "Failed to determine user credentials for root: %m");
         }
 
-        bool set_user_login_env = c->set_login_environment >= 0 ? c->set_login_environment : (c->user || c->dynamic_user);
+        bool set_user_login_env = exec_context_get_set_login_environment(c);
 
         if (username) {
                 x = strjoin("USER=", username);
index 1e48e88ee8c01a5463f1e002d54898b80a1073cf..7b661d70c9f02894aacb449092a75fa1653b416e 100644 (file)
@@ -161,9 +161,10 @@ void exec_context_tty_reset(const ExecContext *context, const ExecParameters *p)
          * systemd-vconsole-setup.service also takes the lock to avoid being interrupted. We open a new fd
          * that will be closed automatically, and operate on it for convenience. */
         lock_fd = lock_dev_console();
-        if (lock_fd < 0)
-                return (void) log_debug_errno(lock_fd,
-                                              "Failed to lock /dev/console: %m");
+        if (ERRNO_IS_NEG_PRIVILEGE(lock_fd))
+                log_debug_errno(lock_fd, "No privileges to lock /dev/console, proceeding without: %m");
+        else if (lock_fd < 0)
+                return (void) log_debug_errno(lock_fd, "Failed to lock /dev/console: %m");
 
         if (context->tty_vhangup)
                 (void) terminal_vhangup_fd(fd);
@@ -1453,7 +1454,7 @@ void exec_context_revert_tty(ExecContext *c) {
         assert(c);
 
         /* First, reset the TTY (possibly kicking everybody else from the TTY) */
-        exec_context_tty_reset(c, NULL);
+        exec_context_tty_reset(c, /* parameters= */ NULL);
 
         /* And then undo what chown_terminal() did earlier. Note that we only do this if we have a path
          * configured. If the TTY was passed to us as file descriptor we assume the TTY is opened and managed
@@ -1465,7 +1466,7 @@ void exec_context_revert_tty(ExecContext *c) {
         if (!path)
                 return;
 
-        fd = open(path, O_PATH|O_CLOEXEC);
+        fd = open(path, O_PATH|O_CLOEXEC); /* Pin the inode */
         if (fd < 0)
                 return (void) log_full_errno(errno == ENOENT ? LOG_DEBUG : LOG_WARNING, errno,
                                              "Failed to open TTY inode of '%s' to adjust ownership/access mode, ignoring: %m",
@@ -1484,7 +1485,7 @@ void exec_context_revert_tty(ExecContext *c) {
 
         r = fchmod_and_chown(fd, TTY_MODE, 0, TTY_GID);
         if (r < 0)
-                log_warning_errno(r, "Failed to reset TTY ownership/access mode of %s, ignoring: %m", path);
+                log_warning_errno(r, "Failed to reset TTY ownership/access mode of %s to " UID_FMT ":" GID_FMT ", ignoring: %m", path, (uid_t) 0, (gid_t) TTY_GID);
 }
 
 int exec_context_get_clean_directories(
@@ -1660,6 +1661,15 @@ uint64_t exec_context_get_timer_slack_nsec(const ExecContext *c) {
         return (uint64_t) MAX(r, 0);
 }
 
+bool exec_context_get_set_login_environment(const ExecContext *c) {
+        assert(c);
+
+        if (c->set_login_environment >= 0)
+                return c->set_login_environment;
+
+        return c->user || c->dynamic_user || c->pam_name;
+}
+
 char** exec_context_get_syscall_filter(const ExecContext *c) {
         _cleanup_strv_free_ char **l = NULL;
 
@@ -1954,8 +1964,7 @@ static char *destroy_tree(char *path) {
 }
 
 void exec_shared_runtime_done(ExecSharedRuntime *rt) {
-        if (!rt)
-                return;
+        assert(rt);
 
         if (rt->manager)
                 (void) hashmap_remove(rt->manager->exec_shared_runtime_by_id, rt->id);
@@ -1968,8 +1977,10 @@ void exec_shared_runtime_done(ExecSharedRuntime *rt) {
 }
 
 static ExecSharedRuntime* exec_shared_runtime_free(ExecSharedRuntime *rt) {
-        exec_shared_runtime_done(rt);
+        if (!rt)
+                return NULL;
 
+        exec_shared_runtime_done(rt);
         return mfree(rt);
 }
 
@@ -2093,15 +2104,13 @@ static int exec_shared_runtime_make(
                         return r;
         }
 
-        if (exec_needs_network_namespace(c)) {
+        if (exec_needs_network_namespace(c))
                 if (socketpair(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, netns_storage_socket) < 0)
                         return -errno;
-        }
 
-        if (exec_needs_ipc_namespace(c)) {
+        if (exec_needs_ipc_namespace(c))
                 if (socketpair(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, ipcns_storage_socket) < 0)
                         return -errno;
-        }
 
         r = exec_shared_runtime_add(m, id, &tmp_dir, &var_tmp_dir, netns_storage_socket, ipcns_storage_socket, ret);
         if (r < 0)
index 5a6927aa02730a25a733a4ecb6608fd6004cb377..e3708e1b0145feb3c3fce96aa2ac8837e858b9e6 100644 (file)
@@ -527,6 +527,7 @@ int exec_context_get_nice(const ExecContext *c);
 int exec_context_get_cpu_sched_policy(const ExecContext *c);
 int exec_context_get_cpu_sched_priority(const ExecContext *c);
 uint64_t exec_context_get_timer_slack_nsec(const ExecContext *c);
+bool exec_context_get_set_login_environment(const ExecContext *c);
 char** exec_context_get_syscall_filter(const ExecContext *c);
 char** exec_context_get_syscall_archs(const ExecContext *c);
 char** exec_context_get_syscall_log(const ExecContext *c);
index e7d1f65dbccd8481b05562f479b732f7c00efcd3..e78c2a70db6015c15fc0f4823cfab4c96fa40464 100644 (file)
@@ -833,13 +833,12 @@ static int job_perform_on_unit(Job **j) {
         Manager *m;
         JobType t;
         Unit *u;
+        bool wait_only;
         int r;
 
-        /* While we execute this operation the job might go away (for
-         * example: because it finishes immediately or is replaced by
-         * a new, conflicting job.) To make sure we don't access a
-         * freed job later on we store the id here, so that we can
-         * verify the job is still valid. */
+        /* While we execute this operation the job might go away (for example: because it finishes immediately
+         * or is replaced by a new, conflicting job). To make sure we don't access a freed job later on we
+         * store the id here, so that we can verify the job is still valid. */
 
         assert(j);
         assert(*j);
@@ -853,6 +852,7 @@ static int job_perform_on_unit(Job **j) {
         switch (t) {
                 case JOB_START:
                         r = unit_start(u, a);
+                        wait_only = r == -EBADR; /* If the unit type does not support starting, then simply wait. */
                         break;
 
                 case JOB_RESTART:
@@ -860,24 +860,28 @@ static int job_perform_on_unit(Job **j) {
                         _fallthrough_;
                 case JOB_STOP:
                         r = unit_stop(u);
+                        wait_only = r == -EBADR; /* If the unit type does not support stopping, then simply wait. */
                         break;
 
                 case JOB_RELOAD:
                         r = unit_reload(u);
+                        wait_only = false; /* A clear error is generated if reload is not supported. */
                         break;
 
                 default:
                         assert_not_reached();
         }
 
-        /* Log if the job still exists and the start/stop/reload function actually did something. Note that this means
-         * for units for which there's no 'activating' phase (i.e. because we transition directly from 'inactive' to
-         * 'active') we'll possibly skip the "Starting..." message. */
+        /* Log if the job still exists and the start/stop/reload function actually did something or we're
+         * only waiting for unit status change (common for device units). The latter ensures that job start
+         * messages for device units are correctly shown. Note that if the job disappears too quickly, e.g.
+         * for units for which there's no 'activating' phase (i.e. because we transition directly from
+         * 'inactive' to 'active'), we'll possibly skip the "Starting..." message. */
         *j = manager_get_job(m, id);
-        if (*j && r > 0)
+        if (*j && (r > 0 || wait_only))
                 job_emit_start_message(u, id, t);
 
-        return r;
+        return wait_only ? 0 : r;
 }
 
 int job_run_and_invalidate(Job *j) {
@@ -919,13 +923,6 @@ int job_run_and_invalidate(Job *j) {
                 case JOB_START:
                 case JOB_STOP:
                 case JOB_RESTART:
-                        r = job_perform_on_unit(&j);
-
-                        /* If the unit type does not support starting/stopping, then simply wait. */
-                        if (r == -EBADR)
-                                r = 0;
-                        break;
-
                 case JOB_RELOAD:
                         r = job_perform_on_unit(&j);
                         break;
index 05843662c75f0e6ec57b4c24c490b6b8e003eafb..b424ef06207de53c8a9614662779568616afddf7 100644 (file)
@@ -6096,7 +6096,7 @@ int config_parse_restrict_network_interfaces(
                         break;
                 }
 
-                if (!ifname_valid(word)) {
+                if (!ifname_valid_full(word, IFNAME_VALID_ALTERNATIVE)) {
                         log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid interface name, ignoring: %s", word);
                         continue;
                 }
index 6ca643e6932b341239b688c7aa2800f13d7378c1..c07f537b9f2ca4223753816cf0bbed642e01cee0 100644 (file)
@@ -1804,7 +1804,7 @@ static void manager_distribute_fds(Manager *m, FDSet *fds) {
 
         HASHMAP_FOREACH(u, m->units) {
 
-                if (fdset_size(fds) <= 0)
+                if (fdset_isempty(fds))
                         break;
 
                 if (!UNIT_VTABLE(u)->distribute_fds)
@@ -2745,7 +2745,7 @@ static int manager_dispatch_notify_fd(sd_event_source *source, int fd, uint32_t
         if (!found)
                 log_warning("Cannot find unit for notify message of PID "PID_FMT", ignoring.", ucred->pid);
 
-        if (fdset_size(fds) > 0)
+        if (!fdset_isempty(fds))
                 log_warning("Got extra auxiliary fds with notification message, closing them.");
 
         return 0;
@@ -3810,7 +3810,7 @@ void manager_check_finished(Manager *m) {
 
         manager_check_basic_target(m);
 
-        if (hashmap_size(m->jobs) > 0) {
+        if (!hashmap_isempty(m->jobs)) {
                 if (m->jobs_in_progress_event_source)
                         /* Ignore any failure, this is only for feedback */
                         (void) sd_event_source_set_time(m->jobs_in_progress_event_source,
@@ -4545,7 +4545,7 @@ ManagerState manager_state(Manager *m) {
         }
 
         /* Are there any failed units? If so, we are in degraded mode */
-        if (set_size(m->failed_units) > 0)
+        if (!set_isempty(m->failed_units))
                 return MANAGER_DEGRADED;
 
         return MANAGER_RUNNING;
index b407387fc134a03ff22c1f4b1a71c734b230c8c5..0772a67001c5932b01f9b102bd5f9d0660c925ce 100644 (file)
 #endif
 
 /* Thresholds for logging at INFO level about resource consumption */
-#define MENTIONWORTHY_CPU_NSEC (1 * NSEC_PER_SEC)
-#define MENTIONWORTHY_IO_BYTES (1024 * 1024ULL)
-#define MENTIONWORTHY_IP_BYTES (0ULL)
+#define MENTIONWORTHY_CPU_NSEC     (1 * NSEC_PER_SEC)
+#define MENTIONWORTHY_MEMORY_BYTES (64 * U64_MB)
+#define MENTIONWORTHY_IO_BYTES     (1 * U64_MB)
+#define MENTIONWORTHY_IP_BYTES     UINT64_C(0)
 
-/* Thresholds for logging at INFO level about resource consumption */
-#define NOTICEWORTHY_CPU_NSEC (10*60 * NSEC_PER_SEC) /* 10 minutes */
-#define NOTICEWORTHY_IO_BYTES (10 * 1024 * 1024ULL)  /* 10 MB */
-#define NOTICEWORTHY_IP_BYTES (128 * 1024 * 1024ULL) /* 128 MB */
+/* Thresholds for logging at NOTICE level about resource consumption */
+#define NOTICEWORTHY_CPU_NSEC     (10 * NSEC_PER_MINUTE)
+#define NOTICEWORTHY_MEMORY_BYTES (512 * U64_MB)
+#define NOTICEWORTHY_IO_BYTES     (10 * U64_MB)
+#define NOTICEWORTHY_IP_BYTES     (128 * U64_MB)
 
 const UnitVTable * const unit_vtable[_UNIT_TYPE_MAX] = {
         [UNIT_SERVICE] = &service_vtable,
@@ -2339,273 +2341,167 @@ static int raise_level(int log_level, bool condition_info, bool condition_notice
 }
 
 static int unit_log_resources(Unit *u) {
-        struct iovec iovec[1 + 2 + _CGROUP_IP_ACCOUNTING_METRIC_MAX + _CGROUP_IO_ACCOUNTING_METRIC_MAX + 4];
-        bool any_traffic = false, have_ip_accounting = false, any_io = false, have_io_accounting = false;
-        _cleanup_free_ char *igress = NULL, *egress = NULL, *rr = NULL, *wr = NULL;
-        int log_level = LOG_DEBUG; /* May be raised if resources consumed over a threshold */
-        size_t n_message_parts = 0, n_iovec = 0;
-        char* message_parts[1 + 2 + 2 + 2 + 1], *t;
-        nsec_t nsec = NSEC_INFINITY;
-        uint64_t memory_peak = UINT64_MAX, memory_swap_peak = UINT64_MAX;
-        int r;
-        const char* const ip_fields[_CGROUP_IP_ACCOUNTING_METRIC_MAX] = {
-                [CGROUP_IP_INGRESS_BYTES]   = "IP_METRIC_INGRESS_BYTES",
-                [CGROUP_IP_INGRESS_PACKETS] = "IP_METRIC_INGRESS_PACKETS",
-                [CGROUP_IP_EGRESS_BYTES]    = "IP_METRIC_EGRESS_BYTES",
-                [CGROUP_IP_EGRESS_PACKETS]  = "IP_METRIC_EGRESS_PACKETS",
-        };
-        const char* const io_fields[_CGROUP_IO_ACCOUNTING_METRIC_MAX] = {
-                [CGROUP_IO_READ_BYTES]       = "IO_METRIC_READ_BYTES",
-                [CGROUP_IO_WRITE_BYTES]      = "IO_METRIC_WRITE_BYTES",
-                [CGROUP_IO_READ_OPERATIONS]  = "IO_METRIC_READ_OPERATIONS",
-                [CGROUP_IO_WRITE_OPERATIONS] = "IO_METRIC_WRITE_OPERATIONS",
+
+        static const struct {
+                const char *journal_field;
+                const char *message_suffix;
+        } memory_fields[_CGROUP_MEMORY_ACCOUNTING_METRIC_CACHED_LAST + 1] = {
+                [CGROUP_MEMORY_PEAK]         = { "MEMORY_PEAK",                "memory peak"         },
+                [CGROUP_MEMORY_SWAP_PEAK]    = { "MEMORY_SWAP_PEAK",           "memory swap peak"    },
+        }, ip_fields[_CGROUP_IP_ACCOUNTING_METRIC_MAX] = {
+                [CGROUP_IP_INGRESS_BYTES]    = { "IP_METRIC_INGRESS_BYTES",    "incoming IP traffic" },
+                [CGROUP_IP_EGRESS_BYTES]     = { "IP_METRIC_EGRESS_BYTES",     "outgoing IP traffic" },
+                [CGROUP_IP_INGRESS_PACKETS]  = { "IP_METRIC_INGRESS_PACKETS",  NULL                  },
+                [CGROUP_IP_EGRESS_PACKETS]   = { "IP_METRIC_EGRESS_PACKETS",   NULL                  },
+        }, io_fields[_CGROUP_IO_ACCOUNTING_METRIC_MAX] = {
+                [CGROUP_IO_READ_BYTES]       = { "IO_METRIC_READ_BYTES",       "read from disk"      },
+                [CGROUP_IO_WRITE_BYTES]      = { "IO_METRIC_WRITE_BYTES",      "written to disk"     },
+                [CGROUP_IO_READ_OPERATIONS]  = { "IO_METRIC_READ_OPERATIONS",  NULL                  },
+                [CGROUP_IO_WRITE_OPERATIONS] = { "IO_METRIC_WRITE_OPERATIONS", NULL                  },
         };
 
+        struct iovec *iovec = NULL;
+        size_t n_iovec = 0;
+        _cleanup_free_ char *message = NULL, *t = NULL;
+        nsec_t cpu_nsec = NSEC_INFINITY;
+        int log_level = LOG_DEBUG; /* May be raised if resources consumed over a threshold */
+
         assert(u);
 
+        CLEANUP_ARRAY(iovec, n_iovec, iovec_array_free);
+
+        iovec = new(struct iovec, 1 + (_CGROUP_MEMORY_ACCOUNTING_METRIC_CACHED_LAST + 1) +
+                                  _CGROUP_IP_ACCOUNTING_METRIC_MAX + _CGROUP_IO_ACCOUNTING_METRIC_MAX + 4);
+        if (!iovec)
+                return log_oom();
+
         /* Invoked whenever a unit enters failed or dead state. Logs information about consumed resources if resource
          * accounting was enabled for a unit. It does this in two ways: a friendly human readable string with reduced
          * information and the complete data in structured fields. */
 
-        (void) unit_get_cpu_usage(u, &nsec);
-        if (nsec != NSEC_INFINITY) {
+        (void) unit_get_cpu_usage(u, &cpu_nsec);
+        if (cpu_nsec != NSEC_INFINITY) {
                 /* Format the CPU time for inclusion in the structured log message */
-                if (asprintf(&t, "CPU_USAGE_NSEC=%" PRIu64, nsec) < 0) {
-                        r = log_oom();
-                        goto finish;
-                }
-                iovec[n_iovec++] = IOVEC_MAKE_STRING(t);
+                if (asprintf(&t, "CPU_USAGE_NSEC=%" PRIu64, cpu_nsec) < 0)
+                        return log_oom();
+                iovec[n_iovec++] = IOVEC_MAKE_STRING(TAKE_PTR(t));
 
                 /* Format the CPU time for inclusion in the human language message string */
-                t = strjoin("consumed ", FORMAT_TIMESPAN(nsec / NSEC_PER_USEC, USEC_PER_MSEC), " CPU time");
-                if (!t) {
-                        r = log_oom();
-                        goto finish;
-                }
-
-                message_parts[n_message_parts++] = t;
+                if (strextendf_with_separator(&message, ", ",
+                                              "Consumed %s CPU time",
+                                              FORMAT_TIMESPAN(cpu_nsec / NSEC_PER_USEC, USEC_PER_MSEC)) < 0)
+                        return log_oom();
 
                 log_level = raise_level(log_level,
-                                        nsec > MENTIONWORTHY_CPU_NSEC,
-                                        nsec > NOTICEWORTHY_CPU_NSEC);
+                                        cpu_nsec > MENTIONWORTHY_CPU_NSEC,
+                                        cpu_nsec > NOTICEWORTHY_CPU_NSEC);
         }
 
-        (void) unit_get_memory_accounting(u, CGROUP_MEMORY_PEAK, &memory_peak);
-        if (memory_peak != UINT64_MAX) {
-                /* Format peak memory for inclusion in the structured log message */
-                if (asprintf(&t, "MEMORY_PEAK=%" PRIu64, memory_peak) < 0) {
-                        r = log_oom();
-                        goto finish;
-                }
-                iovec[n_iovec++] = IOVEC_MAKE_STRING(t);
+        for (CGroupMemoryAccountingMetric metric = 0; metric <= _CGROUP_MEMORY_ACCOUNTING_METRIC_CACHED_LAST; metric++) {
+                uint64_t v = UINT64_MAX;
 
-                /* Format peak memory for inclusion in the human language message string */
-                t = strjoin(FORMAT_BYTES(memory_peak), " memory peak");
-                if (!t) {
-                        r = log_oom();
-                        goto finish;
-                }
-                message_parts[n_message_parts++] = t;
-        }
+                assert(memory_fields[metric].journal_field);
+                assert(memory_fields[metric].message_suffix);
 
-        (void) unit_get_memory_accounting(u, CGROUP_MEMORY_SWAP_PEAK, &memory_swap_peak);
-        if (memory_swap_peak != UINT64_MAX) {
-                /* Format peak swap memory for inclusion in the structured log message */
-                if (asprintf(&t, "MEMORY_SWAP_PEAK=%" PRIu64, memory_swap_peak) < 0) {
-                        r = log_oom();
-                        goto finish;
-                }
-                iovec[n_iovec++] = IOVEC_MAKE_STRING(t);
+                (void) unit_get_memory_accounting(u, metric, &v);
+                if (v == UINT64_MAX)
+                        continue;
 
-                /* Format peak swap memory for inclusion in the human language message string */
-                t = strjoin(FORMAT_BYTES(memory_swap_peak), " memory swap peak");
-                if (!t) {
-                        r = log_oom();
-                        goto finish;
-                }
-                message_parts[n_message_parts++] = t;
+                if (asprintf(&t, "%s=%" PRIu64, memory_fields[metric].journal_field, v) < 0)
+                        return log_oom();
+                iovec[n_iovec++] = IOVEC_MAKE_STRING(TAKE_PTR(t));
+
+                if (strextendf_with_separator(&message, ", ", "%s %s",
+                                              FORMAT_BYTES(v), memory_fields[metric].message_suffix) < 0)
+                        return log_oom();
+
+                log_level = raise_level(log_level,
+                                        v > MENTIONWORTHY_MEMORY_BYTES,
+                                        v > NOTICEWORTHY_MEMORY_BYTES);
         }
 
         for (CGroupIOAccountingMetric k = 0; k < _CGROUP_IO_ACCOUNTING_METRIC_MAX; k++) {
                 uint64_t value = UINT64_MAX;
 
-                assert(io_fields[k]);
+                assert(io_fields[k].journal_field);
 
                 (void) unit_get_io_accounting(u, k, k > 0, &value);
                 if (value == UINT64_MAX)
                         continue;
 
-                have_io_accounting = true;
-                if (value > 0)
-                        any_io = true;
-
                 /* Format IO accounting data for inclusion in the structured log message */
-                if (asprintf(&t, "%s=%" PRIu64, io_fields[k], value) < 0) {
-                        r = log_oom();
-                        goto finish;
-                }
-                iovec[n_iovec++] = IOVEC_MAKE_STRING(t);
+                if (asprintf(&t, "%s=%" PRIu64, io_fields[k].journal_field, value) < 0)
+                        return log_oom();
+                iovec[n_iovec++] = IOVEC_MAKE_STRING(TAKE_PTR(t));
 
                 /* Format the IO accounting data for inclusion in the human language message string, but only
                  * for the bytes counters (and not for the operations counters) */
-                if (k == CGROUP_IO_READ_BYTES) {
-                        assert(!rr);
-                        rr = strjoin("read ", strna(FORMAT_BYTES(value)), " from disk");
-                        if (!rr) {
-                                r = log_oom();
-                                goto finish;
-                        }
-                } else if (k == CGROUP_IO_WRITE_BYTES) {
-                        assert(!wr);
-                        wr = strjoin("written ", strna(FORMAT_BYTES(value)), " to disk");
-                        if (!wr) {
-                                r = log_oom();
-                                goto finish;
-                        }
-                }
+                if (io_fields[k].message_suffix) {
+                        if (strextendf_with_separator(&message, ", ", "%s %s",
+                                                      FORMAT_BYTES(value), io_fields[k].message_suffix) < 0)
+                                return log_oom();
 
-                if (IN_SET(k, CGROUP_IO_READ_BYTES, CGROUP_IO_WRITE_BYTES))
                         log_level = raise_level(log_level,
                                                 value > MENTIONWORTHY_IO_BYTES,
                                                 value > NOTICEWORTHY_IO_BYTES);
-        }
-
-        if (have_io_accounting) {
-                if (any_io) {
-                        if (rr)
-                                message_parts[n_message_parts++] = TAKE_PTR(rr);
-                        if (wr)
-                                message_parts[n_message_parts++] = TAKE_PTR(wr);
-
-                } else {
-                        char *k;
-
-                        k = strdup("no IO");
-                        if (!k) {
-                                r = log_oom();
-                                goto finish;
-                        }
-
-                        message_parts[n_message_parts++] = k;
                 }
         }
 
         for (CGroupIPAccountingMetric m = 0; m < _CGROUP_IP_ACCOUNTING_METRIC_MAX; m++) {
                 uint64_t value = UINT64_MAX;
 
-                assert(ip_fields[m]);
+                assert(ip_fields[m].journal_field);
 
                 (void) unit_get_ip_accounting(u, m, &value);
                 if (value == UINT64_MAX)
                         continue;
 
-                have_ip_accounting = true;
-                if (value > 0)
-                        any_traffic = true;
-
                 /* Format IP accounting data for inclusion in the structured log message */
-                if (asprintf(&t, "%s=%" PRIu64, ip_fields[m], value) < 0) {
-                        r = log_oom();
-                        goto finish;
-                }
-                iovec[n_iovec++] = IOVEC_MAKE_STRING(t);
-
-                /* Format the IP accounting data for inclusion in the human language message string, but only for the
-                 * bytes counters (and not for the packets counters) */
-                if (m == CGROUP_IP_INGRESS_BYTES) {
-                        assert(!igress);
-                        igress = strjoin("received ", strna(FORMAT_BYTES(value)), " IP traffic");
-                        if (!igress) {
-                                r = log_oom();
-                                goto finish;
-                        }
-                } else if (m == CGROUP_IP_EGRESS_BYTES) {
-                        assert(!egress);
-                        egress = strjoin("sent ", strna(FORMAT_BYTES(value)), " IP traffic");
-                        if (!egress) {
-                                r = log_oom();
-                                goto finish;
-                        }
-                }
+                if (asprintf(&t, "%s=%" PRIu64, ip_fields[m].journal_field, value) < 0)
+                        return log_oom();
+                iovec[n_iovec++] = IOVEC_MAKE_STRING(TAKE_PTR(t));
+
+                /* Format the IP accounting data for inclusion in the human language message string, but only
+                 * for the bytes counters (and not for the packets counters) */
+                if (ip_fields[m].message_suffix) {
+                        if (strextendf_with_separator(&message, ", ", "%s %s",
+                                                      FORMAT_BYTES(value), ip_fields[m].message_suffix) < 0)
+                                return log_oom();
 
-                if (IN_SET(m, CGROUP_IP_INGRESS_BYTES, CGROUP_IP_EGRESS_BYTES))
                         log_level = raise_level(log_level,
                                                 value > MENTIONWORTHY_IP_BYTES,
                                                 value > NOTICEWORTHY_IP_BYTES);
-        }
-
-        /* This check is here because it is the earliest point following all possible log_level assignments. If
-         * log_level is assigned anywhere after this point, move this check. */
-        if (!unit_log_level_test(u, log_level)) {
-                r = 0;
-                goto finish;
-        }
-
-        if (have_ip_accounting) {
-                if (any_traffic) {
-                        if (igress)
-                                message_parts[n_message_parts++] = TAKE_PTR(igress);
-                        if (egress)
-                                message_parts[n_message_parts++] = TAKE_PTR(egress);
-
-                } else {
-                        char *k;
-
-                        k = strdup("no IP traffic");
-                        if (!k) {
-                                r = log_oom();
-                                goto finish;
-                        }
-
-                        message_parts[n_message_parts++] = k;
                 }
         }
 
+        /* This check is here because it is the earliest point following all possible log_level assignments.
+         * (If log_level is assigned anywhere after this point, move this check.) */
+        if (!unit_log_level_test(u, log_level))
+                return 0;
+
         /* Is there any accounting data available at all? */
         if (n_iovec == 0) {
-                r = 0;
-                goto finish;
-        }
-
-        if (n_message_parts == 0)
-                t = strjoina("MESSAGE=", u->id, ": Completed.");
-        else {
-                _cleanup_free_ char *joined = NULL;
-
-                message_parts[n_message_parts] = NULL;
-
-                joined = strv_join(message_parts, ", ");
-                if (!joined) {
-                        r = log_oom();
-                        goto finish;
-                }
-
-                joined[0] = ascii_toupper(joined[0]);
-                t = strjoina("MESSAGE=", u->id, ": ", joined, ".");
+                assert(!message);
+                return 0;
         }
 
-        /* The following four fields we allocate on the stack or are static strings, we hence don't want to free them,
-         * and hence don't increase n_iovec for them */
-        iovec[n_iovec] = IOVEC_MAKE_STRING(t);
-        iovec[n_iovec + 1] = IOVEC_MAKE_STRING("MESSAGE_ID=" SD_MESSAGE_UNIT_RESOURCES_STR);
-
-        t = strjoina(u->manager->unit_log_field, u->id);
-        iovec[n_iovec + 2] = IOVEC_MAKE_STRING(t);
-
-        t = strjoina(u->manager->invocation_log_field, u->invocation_id_string);
-        iovec[n_iovec + 3] = IOVEC_MAKE_STRING(t);
+        t = strjoin("MESSAGE=", u->id, ": ", message ?: "Completed", ".");
+        if (!t)
+                return log_oom();
+        iovec[n_iovec++] = IOVEC_MAKE_STRING(TAKE_PTR(t));
 
-        log_unit_struct_iovec(u, log_level, iovec, n_iovec + 4);
-        r = 0;
+        if (!set_iovec_string_field(iovec, &n_iovec, "MESSAGE_ID=", SD_MESSAGE_UNIT_RESOURCES_STR))
+                return log_oom();
 
-finish:
-        free_many_charp(message_parts, n_message_parts);
+        if (!set_iovec_string_field(iovec, &n_iovec, u->manager->unit_log_field, u->id))
+                return log_oom();
 
-        for (size_t i = 0; i < n_iovec; i++)
-                free(iovec[i].iov_base);
+        if (!set_iovec_string_field(iovec, &n_iovec, u->manager->invocation_log_field, u->invocation_id_string))
+                return log_oom();
 
-        return r;
+        log_unit_struct_iovec(u, log_level, iovec, n_iovec);
 
+        return 0;
 }
 
 static void unit_update_on_console(Unit *u) {
index 10d117118fbca592f2ba761ed85462c108142da7..c9bf2e1e36cee4dba373f5db14977bea47a1a775 100644 (file)
@@ -24,6 +24,9 @@
 #include "terminal-util.h"
 #include "tpm2-pcr.h"
 #include "tpm2-util.h"
+#include "user-util.h"
+#include "varlink.h"
+#include "varlink-io.systemd.Credentials.h"
 #include "verbs.h"
 
 typedef enum TranscodeMode {
@@ -54,6 +57,7 @@ static usec_t arg_timestamp = USEC_INFINITY;
 static usec_t arg_not_after = USEC_INFINITY;
 static bool arg_pretty = false;
 static bool arg_quiet = false;
+static bool arg_varlink = false;
 
 STATIC_DESTRUCTOR_REGISTER(arg_tpm2_public_key, freep);
 STATIC_DESTRUCTOR_REGISTER(arg_tpm2_signature, freep);
@@ -933,6 +937,11 @@ static int parse_argv(int argc, char *argv[]) {
         if (arg_tpm2_public_key_pcr_mask == UINT32_MAX)
                 arg_tpm2_public_key_pcr_mask = UINT32_C(1) << TPM2_PCR_KERNEL_BOOT;
 
+        r = varlink_invocation(VARLINK_ALLOW_ACCEPT);
+        if (r < 0)
+                return log_error_errno(r, "Failed to check if invoked in Varlink mode: %m");
+        arg_varlink = r;
+
         return 1;
 }
 
@@ -952,6 +961,150 @@ static int creds_main(int argc, char *argv[]) {
         return dispatch_verb(argc, argv, verbs, NULL);
 }
 
+typedef struct MethodEncryptParameters {
+        const char *name;
+        const char *text;
+        struct iovec data;
+        uint64_t timestamp;
+        uint64_t not_after;
+} MethodEncryptParameters;
+
+static void method_encrypt_parameters_done(MethodEncryptParameters *p) {
+        assert(p);
+
+        iovec_done_erase(&p->data);
+}
+
+static int vl_method_encrypt(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata) {
+
+        static const JsonDispatch dispatch_table[] = {
+                { "name",      JSON_VARIANT_STRING,        json_dispatch_const_string,   offsetof(MethodEncryptParameters, name),      0 },
+                { "text",      JSON_VARIANT_STRING,        json_dispatch_const_string,   offsetof(MethodEncryptParameters, text),      0 },
+                { "data",      JSON_VARIANT_STRING,        json_dispatch_unbase64_iovec, offsetof(MethodEncryptParameters, data),      0 },
+                { "timestamp", _JSON_VARIANT_TYPE_INVALID, json_dispatch_uint64,         offsetof(MethodEncryptParameters, timestamp), 0 },
+                { "notAfter",  _JSON_VARIANT_TYPE_INVALID, json_dispatch_uint64,         offsetof(MethodEncryptParameters, not_after), 0 },
+                {}
+        };
+        _cleanup_(method_encrypt_parameters_done) MethodEncryptParameters p = {
+                .timestamp = UINT64_MAX,
+                .not_after = UINT64_MAX,
+        };
+        _cleanup_(iovec_done) struct iovec output = {};
+        int r;
+
+        assert(link);
+
+        json_variant_sensitive(parameters);
+
+        r = varlink_dispatch(link, parameters, dispatch_table, &p);
+        if (r != 0)
+                return r;
+
+        if (p.name && !credential_name_valid(p.name))
+                return varlink_error_invalid_parameter_name(link, "name");
+        /* Specifying both or neither the text string and the binary data is not allowed */
+        if (!!p.text == !!p.data.iov_base)
+                return varlink_error_invalid_parameter_name(link, "data");
+        if (p.timestamp == UINT64_MAX)
+                p.timestamp = now(CLOCK_REALTIME);
+        if (p.not_after != UINT64_MAX && p.not_after < p.timestamp)
+                return varlink_error_invalid_parameter_name(link, "notAfter");
+
+        r = encrypt_credential_and_warn(
+                        arg_with_key,
+                        p.name,
+                        p.timestamp,
+                        p.not_after,
+                        arg_tpm2_device,
+                        arg_tpm2_pcr_mask,
+                        arg_tpm2_public_key,
+                        arg_tpm2_public_key_pcr_mask,
+                        p.text ?: p.data.iov_base, p.text ? strlen(p.text) : p.data.iov_len,
+                        &output.iov_base, &output.iov_len);
+        if (r < 0)
+                return r;
+
+        _cleanup_(json_variant_unrefp) JsonVariant *reply = NULL;
+
+        r = json_build(&reply, JSON_BUILD_OBJECT(JSON_BUILD_PAIR_IOVEC_BASE64("blob", &output)));
+        if (r < 0)
+                return r;
+
+        /* Let's also mark the (theoretically encrypted) reply as sensitive, in case the NULL encryption scheme was used. */
+        json_variant_sensitive(reply);
+
+        return varlink_reply(link, reply);
+}
+
+typedef struct MethodDecryptParameters {
+        const char *name;
+        struct iovec blob;
+        uint64_t timestamp;
+} MethodDecryptParameters;
+
+static void method_decrypt_parameters_done(MethodDecryptParameters *p) {
+        assert(p);
+
+        iovec_done_erase(&p->blob);
+}
+
+static int vl_method_decrypt(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata) {
+
+        static const JsonDispatch dispatch_table[] = {
+                { "name",      JSON_VARIANT_STRING,        json_dispatch_const_string,   offsetof(MethodDecryptParameters, name),      0 },
+                { "blob",      JSON_VARIANT_STRING,        json_dispatch_unbase64_iovec, offsetof(MethodDecryptParameters, blob),      0 },
+                { "timestamp", _JSON_VARIANT_TYPE_INVALID, json_dispatch_uint64,         offsetof(MethodDecryptParameters, timestamp), 0 },
+                {}
+        };
+        _cleanup_(method_decrypt_parameters_done) MethodDecryptParameters p = {
+                .timestamp = UINT64_MAX,
+        };
+        _cleanup_(iovec_done_erase) struct iovec output = {};
+        int r;
+
+        assert(link);
+
+        /* Let's also mark the (theoretically encrypted) input as sensitive, in case the NULL encryption scheme was used. */
+        json_variant_sensitive(parameters);
+
+        r = varlink_dispatch(link, parameters, dispatch_table, &p);
+        if (r != 0)
+                return r;
+
+        if (p.name && !credential_name_valid(p.name))
+                return varlink_error_invalid_parameter_name(link, "name");
+        if (!p.blob.iov_base)
+                return varlink_error_invalid_parameter_name(link, "blob");
+        if (p.timestamp == UINT64_MAX)
+                p.timestamp = now(CLOCK_REALTIME);
+
+        r = decrypt_credential_and_warn(
+                        p.name,
+                        p.timestamp,
+                        arg_tpm2_device,
+                        arg_tpm2_signature,
+                        p.blob.iov_base, p.blob.iov_len,
+                        &output.iov_base, &output.iov_len);
+        if (r == -EBADMSG)
+                return varlink_error(link, "io.systemd.Credentials.BadFormat", NULL);
+        if (r == -EREMOTE)
+                return varlink_error(link, "io.systemd.Credentials.NameMismatch", NULL);
+        if (r == -ESTALE)
+                return varlink_error(link, "io.systemd.Credentials.TimeMismatch", NULL);
+        if (r < 0)
+                return r;
+
+        _cleanup_(json_variant_unrefp) JsonVariant *reply = NULL;
+
+        r = json_build(&reply, JSON_BUILD_OBJECT(JSON_BUILD_PAIR_IOVEC_BASE64("data", &output)));
+        if (r < 0)
+                return r;
+
+        json_variant_sensitive(reply);
+
+        return varlink_reply(link, reply);
+}
+
 static int run(int argc, char *argv[]) {
         int r;
 
@@ -961,6 +1114,33 @@ static int run(int argc, char *argv[]) {
         if (r <= 0)
                 return r;
 
+        if (arg_varlink) {
+                _cleanup_(varlink_server_unrefp) VarlinkServer *varlink_server = NULL;
+
+                /* Invocation as Varlink service */
+
+                r = varlink_server_new(&varlink_server, VARLINK_SERVER_ROOT_ONLY|VARLINK_SERVER_INHERIT_USERDATA);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to allocate Varlink server: %m");
+
+                r = varlink_server_add_interface(varlink_server, &vl_interface_io_systemd_Credentials);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to add Varlink interface: %m");
+
+                r = varlink_server_bind_method_many(
+                                varlink_server,
+                                "io.systemd.Credentials.Encrypt", vl_method_encrypt,
+                                "io.systemd.Credentials.Decrypt", vl_method_decrypt);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to bind Varlink methods: %m");
+
+                r = varlink_server_loop_auto(varlink_server);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to run Varlink event loop: %m");
+
+                return 0;
+        }
+
         return creds_main(argc, argv);
 }
 
index 54b6b8624281db74177adfefde6f54d48d4a9f5b..7d6112e40271cb1d87ad7c334e2a1e88a53bbec8 100644 (file)
@@ -6,7 +6,6 @@
 #include "memory-util.h"
 #include "openssl-util.h"
 #include "pkcs11-util.h"
-#include "random-util.h"
 
 int enroll_pkcs11(
                 struct crypt_device *cd,
@@ -18,12 +17,11 @@ int enroll_pkcs11(
         _cleanup_(erase_and_freep) char *base64_encoded = NULL;
         _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
         _cleanup_free_ char *keyslot_as_string = NULL;
-        size_t decrypted_key_size, encrypted_key_size;
-        _cleanup_free_ void *encrypted_key = NULL;
+        size_t decrypted_key_size, saved_key_size;
+        _cleanup_free_ void *saved_key = NULL;
         _cleanup_(X509_freep) X509 *cert = NULL;
         ssize_t base64_encoded_size;
         const char *node;
-        EVP_PKEY *pkey;
         int keyslot, r;
 
         assert_se(cd);
@@ -37,27 +35,9 @@ int enroll_pkcs11(
         if (r < 0)
                 return r;
 
-        pkey = X509_get0_pubkey(cert);
-        if (!pkey)
-                return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to extract public key from X.509 certificate.");
-
-        r = rsa_pkey_to_suitable_key_size(pkey, &decrypted_key_size);
-        if (r < 0)
-                return log_error_errno(r, "Failed to determine RSA public key size.");
-
-        log_debug("Generating %zu bytes random key.", decrypted_key_size);
-
-        decrypted_key = malloc(decrypted_key_size);
-        if (!decrypted_key)
-                return log_oom();
-
-        r = crypto_random_bytes(decrypted_key, decrypted_key_size);
-        if (r < 0)
-                return log_error_errno(r, "Failed to generate random key: %m");
-
-        r = rsa_encrypt_bytes(pkey, decrypted_key, decrypted_key_size, &encrypted_key, &encrypted_key_size);
+        r = x509_generate_volume_keys(cert, &decrypted_key, &decrypted_key_size, &saved_key, &saved_key_size);
         if (r < 0)
-                return log_error_errno(r, "Failed to encrypt key: %m");
+                return log_error_errno(r, "Failed to generate volume keys: %m");
 
         /* Let's base64 encode the key to use, for compat with homed (and it's easier to type it in by
          * keyboard, if that might ever end up being necessary.) */
@@ -87,7 +67,7 @@ int enroll_pkcs11(
                                        JSON_BUILD_PAIR("type", JSON_BUILD_CONST_STRING("systemd-pkcs11")),
                                        JSON_BUILD_PAIR("keyslots", JSON_BUILD_ARRAY(JSON_BUILD_STRING(keyslot_as_string))),
                                        JSON_BUILD_PAIR("pkcs11-uri", JSON_BUILD_STRING(uri)),
-                                       JSON_BUILD_PAIR("pkcs11-key", JSON_BUILD_BASE64(encrypted_key, encrypted_key_size))));
+                                       JSON_BUILD_PAIR("pkcs11-key", JSON_BUILD_BASE64(saved_key, saved_key_size))));
         if (r < 0)
                 return log_error_errno(r, "Failed to prepare PKCS#11 JSON token object: %m");
 
index dc753b461cdc4bdd2b93fe5166442aea7c448704..f9f47cf7aacf670070486d6642d31d83f7863512 100644 (file)
@@ -827,16 +827,15 @@ static int action_dissect(DissectedImage *m, LoopDevice *d) {
         if (arg_json_format_flags & (JSON_FORMAT_OFF|JSON_FORMAT_PRETTY|JSON_FORMAT_PRETTY_AUTO))
                 pager_open(arg_pager_flags);
 
-        if (arg_json_format_flags & JSON_FORMAT_OFF)
-                printf("      Name: %s%s%s\n", ansi_highlight(), bn, ansi_normal());
+        if (arg_json_format_flags & JSON_FORMAT_OFF) {
+                printf("      Name: %s%s%s\n",
+                       ansi_highlight(), bn, ansi_normal());
 
-        if (ioctl(d->fd, BLKGETSIZE64, &size) < 0)
-                log_debug_errno(errno, "Failed to query size of loopback device: %m");
-        else if (arg_json_format_flags & JSON_FORMAT_OFF)
-                printf("      Size: %s\n", FORMAT_BYTES(size));
+                printf("      Size: %s\n",
+                       FORMAT_BYTES(m->image_size));
 
-        if (arg_json_format_flags & JSON_FORMAT_OFF) {
-                printf(" Sec. Size: %" PRIu32 "\n", m->sector_size);
+                printf(" Sec. Size: %" PRIu32 "\n",
+                       m->sector_size);
 
                 printf("     Arch.: %s\n",
                        strna(architecture_to_string(dissected_image_architecture(m))));
@@ -960,7 +959,12 @@ static int action_dissect(DissectedImage *m, LoopDevice *d) {
                 return log_oom();
 
         table_set_ersatz_string(t, TABLE_ERSATZ_DASH);
-        (void) table_set_align_percent(t, table_get_cell(t, 0, 7), 100);
+        (void) table_set_align_percent(t, table_get_cell(t, 0, 9), 100);
+
+        /* Hide the device path if this is a loopback device that is not relinquished, since that means the
+         * device node is not going to be useful the instant our command exits */
+        if ((!d || d->created) && (arg_json_format_flags & JSON_FORMAT_OFF))
+                table_hide_column_from_display(t, 8);
 
         for (PartitionDesignator i = 0; i < _PARTITION_DESIGNATOR_MAX; i++) {
                 DissectedPartition *p = m->partitions + i;
index 17d344e98037800875ba4ee1d70c37dd55325d61..5dd7bdb1e7ac0caa5e71dd80fc7ed42966a8b479 100644 (file)
@@ -1622,7 +1622,7 @@ static int reload_vconsole(sd_bus **bus) {
         if (r < 0)
                 return bus_log_parse_error(r);
 
-        r = bus_wait_for_jobs_one(w, object, false, NULL);
+        r = bus_wait_for_jobs_one(w, object, BUS_WAIT_JOBS_LOG_ERROR, NULL);
         if (r < 0)
                 return log_error_errno(r, "Failed to wait for systemd-vconsole-setup.service/restart: %m");
         return 0;
@@ -1655,8 +1655,8 @@ static int run(int argc, char *argv[]) {
                 if (r < 0)
                         return log_error_errno(r, "Failed to parse systemd.firstboot= kernel command line argument, ignoring: %m");
                 if (r > 0 && !enabled) {
-                        log_debug("Found systemd.firstboot=no kernel command line argument, terminating.");
-                        return 0; /* disabled */
+                        log_debug("Found systemd.firstboot=no kernel command line argument, turning off all prompts.");
+                        arg_prompt_locale = arg_prompt_keymap = arg_prompt_timezone = arg_prompt_hostname = arg_prompt_root_password = arg_prompt_root_shell = false;
                 }
         }
 
index 000ed69667d51ccde248244a0408bf2db38b0932..729209fc89d5016c99c7cfcb2743d2cde52a2921 100644 (file)
@@ -177,7 +177,7 @@ static int process_progress(int fd, FILE* console) {
                         else if (feof(f))
                                 r = 0;
                         else
-                                r = log_warning_errno(SYNTHETIC_ERRNO(errno), "Failed to parse progress pipe data");
+                                r = log_warning_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to parse progress pipe data.");
 
                         break;
                 }
index 797330dd97d6cabb2da7fc30fcb9999afc3b804f..041649a03bca54ba0f3ff684a48d2c9433dcf708 100644 (file)
                 __atomic_exchange_n(&(o), true, __ATOMIC_SEQ_CST); \
         })
 
+#define U64_KB UINT64_C(1024)
+#define U64_MB (UINT64_C(1024) * U64_KB)
+#define U64_GB (UINT64_C(1024) * U64_MB)
+
 #undef MAX
 #define MAX(a, b) __MAX(UNIQ, (a), UNIQ, (b))
 #define __MAX(aq, a, bq, b)                             \
index c1c21a2c13d0d2503c9c5d821a53ce831ac84838..01684283942f902cf074b0e0b30a8ac814d21d5b 100644 (file)
@@ -54,26 +54,30 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
         return 0;
 }
 
-static int process_resume(const char *device) {
+static int process_resume(const HibernateInfo *info) {
         _cleanup_free_ char *device_unit = NULL;
         int r;
 
-        assert(device);
+        assert(info);
 
-        r = unit_name_from_path(device, ".device", &device_unit);
+        r = unit_name_from_path(info->device, ".device", &device_unit);
         if (r < 0)
-                return log_error_errno(r, "Failed to generate device unit name from path '%s': %m", device);
+                return log_error_errno(r, "Failed to generate device unit name from path '%s': %m", info->device);
 
-        r = write_drop_in(arg_dest, device_unit, 40, "device-timeout",
-                          "# Automatically generated by systemd-hibernate-resume-generator\n\n"
-                          "[Unit]\n"
-                          "JobTimeoutSec=infinity\n");
+        /* If hibernate info is acquired from EFI variable, don't wait forever by default. Otherwise, if
+         * swap device is not present and HibernateLocation was not correctly cleared, we end up blocking
+         * the boot process infinitely. */
+        r = write_drop_in_format(arg_dest, device_unit, 40, "device-timeout",
+                                 "# Automatically generated by systemd-hibernate-resume-generator\n\n"
+                                 "[Unit]\n"
+                                 "JobTimeoutSec=%s\n",
+                                 info->cmdline ? "infinity" : "2min");
         if (r < 0)
                 log_warning_errno(r, "Failed to write device timeout drop-in, ignoring: %m");
 
         r = generator_write_timeouts(arg_dest,
-                                     device,
-                                     device,
+                                     info->device,
+                                     info->device,
                                      arg_resume_options ?: arg_root_options,
                                      NULL);
         if (r < 0)
@@ -120,7 +124,7 @@ static int run(const char *dest, const char *dest_early, const char *dest_late)
         if (r < 0)
                 return r;
 
-        return process_resume(info.device);
+        return process_resume(&info);
 }
 
 DEFINE_MAIN_GENERATOR_FUNCTION(run);
index 2539af063118690de030ca398ad816fbfeb107a9..6ae291ed932df5431c941d9432264e8907e602d7 100644 (file)
@@ -8,7 +8,6 @@
 #include "memory-util.h"
 #include "openssl-util.h"
 #include "pkcs11-util.h"
-#include "random-util.h"
 #include "strv.h"
 
 static int add_pkcs11_encrypted_key(
@@ -158,11 +157,10 @@ static int acquire_pkcs11_certificate(
 }
 
 int identity_add_pkcs11_key_data(JsonVariant **v, const char *uri) {
-        _cleanup_(erase_and_freep) void *decrypted_key = NULL, *encrypted_key = NULL;
+        _cleanup_(erase_and_freep) void *decrypted_key = NULL, *saved_key = NULL;
         _cleanup_(erase_and_freep) char *pin = NULL;
-        size_t decrypted_key_size, encrypted_key_size;
+        size_t decrypted_key_size, saved_key_size;
         _cleanup_(X509_freep) X509 *cert = NULL;
-        EVP_PKEY *pkey;
         int r;
 
         assert(v);
@@ -171,27 +169,9 @@ int identity_add_pkcs11_key_data(JsonVariant **v, const char *uri) {
         if (r < 0)
                 return r;
 
-        pkey = X509_get0_pubkey(cert);
-        if (!pkey)
-                return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to extract public key from X.509 certificate.");
-
-        r = rsa_pkey_to_suitable_key_size(pkey, &decrypted_key_size);
-        if (r < 0)
-                return log_error_errno(r, "Failed to extract RSA key size from X509 certificate.");
-
-        log_debug("Generating %zu bytes random key.", decrypted_key_size);
-
-        decrypted_key = malloc(decrypted_key_size);
-        if (!decrypted_key)
-                return log_oom();
-
-        r = crypto_random_bytes(decrypted_key, decrypted_key_size);
-        if (r < 0)
-                return log_error_errno(r, "Failed to generate random key: %m");
-
-        r = rsa_encrypt_bytes(pkey, decrypted_key, decrypted_key_size, &encrypted_key, &encrypted_key_size);
+        r = x509_generate_volume_keys(cert, &decrypted_key, &decrypted_key_size, &saved_key, &saved_key_size);
         if (r < 0)
-                return log_error_errno(r, "Failed to encrypt key: %m");
+                return log_error_errno(r, "Failed to generate volume keys: %m");
 
         /* Add the token URI to the public part of the record. */
         r = add_pkcs11_token_uri(v, uri);
@@ -202,7 +182,7 @@ int identity_add_pkcs11_key_data(JsonVariant **v, const char *uri) {
         r = add_pkcs11_encrypted_key(
                         v,
                         uri,
-                        encrypted_key, encrypted_key_size,
+                        saved_key, saved_key_size,
                         decrypted_key, decrypted_key_size);
         if (r < 0)
                 return r;
index a6951c8562a0edc6e650b09ec1b08940428a3680..f2fe90c75b0412c63c5500dcf3a062930e73c8b8 100644 (file)
@@ -12,6 +12,7 @@
 #include "cap-list.h"
 #include "capability-util.h"
 #include "cgroup-util.h"
+#include "creds-util.h"
 #include "dns-domain.h"
 #include "env-util.h"
 #include "fd-util.h"
@@ -35,7 +36,9 @@
 #include "percent-util.h"
 #include "pkcs11-util.h"
 #include "pretty-print.h"
+#include "proc-cmdline.h"
 #include "process-util.h"
+#include "recurse-dir.h"
 #include "rlimit-util.h"
 #include "spawn-polkit-agent.h"
 #include "terminal-util.h"
@@ -45,6 +48,7 @@
 #include "user-record-show.h"
 #include "user-record-util.h"
 #include "user-util.h"
+#include "userdb.h"
 #include "verbs.h"
 
 static PagerFlags arg_pager_flags = 0;
@@ -80,6 +84,7 @@ static enum {
 } arg_export_format = EXPORT_FORMAT_FULL;
 static uint64_t arg_capability_bounding_set = UINT64_MAX;
 static uint64_t arg_capability_ambient_set = UINT64_MAX;
+static bool arg_prompt_new_user = false;
 
 STATIC_DESTRUCTOR_REGISTER(arg_identity_extra, json_variant_unrefp);
 STATIC_DESTRUCTOR_REGISTER(arg_identity_extra_this_machine, json_variant_unrefp);
@@ -1092,7 +1097,7 @@ static int add_disposition(JsonVariant **v) {
         return 1;
 }
 
-static int acquire_new_home_record(UserRecord **ret) {
+static int acquire_new_home_record(JsonVariant *input, UserRecord **ret) {
         _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
         _cleanup_(user_record_unrefp) UserRecord *hr = NULL;
         int r;
@@ -1102,12 +1107,16 @@ static int acquire_new_home_record(UserRecord **ret) {
         if (arg_identity) {
                 unsigned line, column;
 
+                if (input)
+                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Two identity records specified, refusing.");
+
                 r = json_parse_file(
                                 streq(arg_identity, "-") ? stdin : NULL,
                                 streq(arg_identity, "-") ? "<stdin>" : arg_identity, JSON_PARSE_SENSITIVE, &v, &line, &column);
                 if (r < 0)
                         return log_error_errno(r, "Failed to parse identity at %u:%u: %m", line, column);
-        }
+        } else
+                v = json_variant_ref(input);
 
         r = apply_identity_changes(&v);
         if (r < 0)
@@ -1146,7 +1155,18 @@ static int acquire_new_home_record(UserRecord **ret) {
         if (!hr)
                 return log_oom();
 
-        r = user_record_load(hr, v, USER_RECORD_REQUIRE_REGULAR|USER_RECORD_ALLOW_SECRET|USER_RECORD_ALLOW_PRIVILEGED|USER_RECORD_ALLOW_PER_MACHINE|USER_RECORD_ALLOW_SIGNATURE|USER_RECORD_LOG|USER_RECORD_PERMISSIVE);
+        r = user_record_load(
+                        hr,
+                        v,
+                        USER_RECORD_REQUIRE_REGULAR|
+                        USER_RECORD_ALLOW_SECRET|
+                        USER_RECORD_ALLOW_PRIVILEGED|
+                        USER_RECORD_ALLOW_PER_MACHINE|
+                        USER_RECORD_STRIP_BINDING|
+                        USER_RECORD_STRIP_STATUS|
+                        USER_RECORD_STRIP_SIGNATURE|
+                        USER_RECORD_LOG|
+                        USER_RECORD_PERMISSIVE);
         if (r < 0)
                 return r;
 
@@ -1247,7 +1267,7 @@ static int acquire_new_password(
         }
 }
 
-static int create_home(int argc, char *argv[], void *userdata) {
+static int create_home_common(JsonVariant *input) {
         _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
         _cleanup_(user_record_unrefp) UserRecord *hr = NULL;
         int r;
@@ -1258,36 +1278,7 @@ static int create_home(int argc, char *argv[], void *userdata) {
 
         (void) polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
 
-        if (argc >= 2) {
-                /* If a username was specified, use it */
-
-                if (valid_user_group_name(argv[1], 0))
-                        r = json_variant_set_field_string(&arg_identity_extra, "userName", argv[1]);
-                else {
-                        _cleanup_free_ char *un = NULL, *rr = NULL;
-
-                        /* Before we consider the user name invalid, let's check if we can split it? */
-                        r = split_user_name_realm(argv[1], &un, &rr);
-                        if (r < 0)
-                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "User name '%s' is not valid: %m", argv[1]);
-
-                        if (rr) {
-                                r = json_variant_set_field_string(&arg_identity_extra, "realm", rr);
-                                if (r < 0)
-                                        return log_error_errno(r, "Failed to set realm field: %m");
-                        }
-
-                        r = json_variant_set_field_string(&arg_identity_extra, "userName", un);
-                }
-                if (r < 0)
-                        return log_error_errno(r, "Failed to set userName field: %m");
-        } else {
-                /* If neither a username nor an identity have been specified we cannot operate. */
-                if (!arg_identity)
-                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "User name required.");
-        }
-
-        r = acquire_new_home_record(&hr);
+        r = acquire_new_home_record(input, &hr);
         if (r < 0)
                 return r;
 
@@ -1374,6 +1365,41 @@ static int create_home(int argc, char *argv[], void *userdata) {
         return 0;
 }
 
+static int create_home(int argc, char *argv[], void *userdata) {
+        int r;
+
+        if (argc >= 2) {
+                /* If a username was specified, use it */
+
+                if (valid_user_group_name(argv[1], 0))
+                        r = json_variant_set_field_string(&arg_identity_extra, "userName", argv[1]);
+                else {
+                        _cleanup_free_ char *un = NULL, *rr = NULL;
+
+                        /* Before we consider the user name invalid, let's check if we can split it? */
+                        r = split_user_name_realm(argv[1], &un, &rr);
+                        if (r < 0)
+                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "User name '%s' is not valid: %m", argv[1]);
+
+                        if (rr) {
+                                r = json_variant_set_field_string(&arg_identity_extra, "realm", rr);
+                                if (r < 0)
+                                        return log_error_errno(r, "Failed to set realm field: %m");
+                        }
+
+                        r = json_variant_set_field_string(&arg_identity_extra, "userName", un);
+                }
+                if (r < 0)
+                        return log_error_errno(r, "Failed to set userName field: %m");
+        } else {
+                /* If neither a username nor an identity have been specified we cannot operate. */
+                if (!arg_identity)
+                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "User name required.");
+        }
+
+        return create_home_common(/* input= */ NULL);
+}
+
 static int remove_home(int argc, char *argv[], void *userdata) {
         _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
         int r, ret = 0;
@@ -2131,6 +2157,190 @@ static int rebalance(int argc, char *argv[], void *userdata) {
         return 0;
 }
 
+static int create_from_credentials(void) {
+        _cleanup_close_ int fd = -EBADF;
+        int ret = 0, n_created = 0, r;
+
+        fd = open_credentials_dir();
+        if (IN_SET(fd, -ENXIO, -ENOENT)) /* Credential env var not set, or dir doesn't exist. */
+                return 0;
+        if (fd < 0)
+                return log_error_errno(fd, "Failed to open credentials directory: %m");
+
+        _cleanup_free_ DirectoryEntries *des = NULL;
+        r = readdir_all(fd, RECURSE_DIR_SORT|RECURSE_DIR_IGNORE_DOT|RECURSE_DIR_ENSURE_TYPE, &des);
+        if (r < 0)
+                return log_error_errno(r, "Failed to enumerate credentials: %m");
+
+        FOREACH_ARRAY(i, des->entries, des->n_entries) {
+                _cleanup_(json_variant_unrefp) JsonVariant *identity = NULL;
+                struct dirent *de = *i;
+                const char *e;
+
+                if (de->d_type != DT_REG)
+                        continue;
+
+                e = startswith(de->d_name, "home.create.");
+                if (!e)
+                        continue;
+
+                if (!valid_user_group_name(e, 0)) {
+                        log_notice("Skipping over credential with name that is not a suitable user name: %s", de->d_name);
+                        continue;
+                }
+
+                r = json_parse_file_at(
+                                /* f= */ NULL,
+                                fd,
+                                de->d_name,
+                                /* flags= */ 0,
+                                &identity,
+                                /* ret_line= */ NULL,
+                                /* ret_column= */ NULL);
+                if (r < 0) {
+                        log_warning_errno(r, "Failed to parse user record in credential '%s', ignoring: %m", de->d_name);
+                        continue;
+                }
+
+                JsonVariant *un;
+                un = json_variant_by_key(identity, "userName");
+                if (un) {
+                        if (!json_variant_is_string(un)) {
+                                log_warning("User record from credential '%s' contains 'userName' field of invalid type, ignoring.", de->d_name);
+                                continue;
+                        }
+
+                        if (!streq(json_variant_string(un), e)) {
+                                log_warning("User record from credential '%s' contains 'userName' field (%s) that doesn't match credential name (%s), ignoring.", de->d_name, json_variant_string(un), e);
+                                continue;
+                        }
+                } else {
+                        r = json_variant_set_field_string(&identity, "userName", e);
+                        if (r < 0)
+                                return log_warning_errno(r, "Failed to set userName field: %m");
+                }
+
+                log_notice("Processing user '%s' from credentials.", e);
+
+                r = create_home_common(identity);
+                if (r >= 0)
+                        n_created++;
+
+                RET_GATHER(ret, r);
+        }
+
+        return ret < 0 ? ret : n_created;
+}
+
+static int has_regular_user(void) {
+        _cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
+        int r;
+
+        r = userdb_all(USERDB_SUPPRESS_SHADOW, &iterator);
+        if (r < 0)
+                return log_error_errno(r, "Failed to create user enumerator: %m");
+
+        for (;;) {
+                _cleanup_(user_record_unrefp) UserRecord *ur = NULL;
+
+                r = userdb_iterator_get(iterator, &ur);
+                if (r == -ESRCH)
+                        break;
+                if (r < 0)
+                        return log_error_errno(r, "Failed to enumerate users: %m");
+
+                if (user_record_disposition(ur) == USER_REGULAR)
+                        return true;
+        }
+
+        return false;
+}
+
+static int create_interactively(void) {
+        _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
+        _cleanup_free_ char *username = NULL;
+        int r;
+
+        if (!arg_prompt_new_user) {
+                log_debug("Prompting for user creation was not requested.");
+                return 0;
+        }
+
+        r = acquire_bus(&bus);
+        if (r < 0)
+                return r;
+
+        (void) polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
+
+        (void) reset_terminal_fd(STDIN_FILENO, /* switch_to_text= */ false);
+
+        for (;;) {
+                username = mfree(username);
+
+                r = ask_string(&username,
+                               "%s Please enter user name to create (empty to skip): ",
+                               special_glyph(SPECIAL_GLYPH_TRIANGULAR_BULLET));
+                if (r < 0)
+                        return log_error_errno(r, "Failed to query user for username: %m");
+
+                if (isempty(username)) {
+                        log_info("No data entered, skipping.");
+                        return 0;
+                }
+
+                if (!valid_user_group_name(username, /* flags= */ 0)) {
+                        log_notice("Specified user name is not a valid UNIX user name, try again: %s", username);
+                        continue;
+                }
+
+                r = userdb_by_name(username, USERDB_SUPPRESS_SHADOW, /* ret= */ NULL);
+                if (r == -ESRCH)
+                        break;
+                if (r < 0)
+                        return log_error_errno(r, "Failed to check if specified user '%s' already exists: %m", username);
+
+                log_notice("Specified user '%s' exists already, try again.", username);
+        }
+
+        r = json_variant_set_field_string(&arg_identity_extra, "userName", username);
+        if (r < 0)
+                return log_error_errno(r, "Failed to set userName field: %m");
+
+        return create_home_common(/* input= */ NULL);
+}
+
+static int verb_firstboot(int argc, char *argv[], void *userdata) {
+        int r;
+
+        /* Let's honour the systemd.firstboot kernel command line option, just like the systemd-firstboot
+         * tool. */
+
+        bool enabled;
+        r = proc_cmdline_get_bool("systemd.firstboot", /* flags = */ 0, &enabled);
+        if (r < 0)
+                return log_error_errno(r, "Failed to parse systemd.firstboot= kernel command line argument, ignoring: %m");
+        if (r > 0 && !enabled) {
+                log_debug("Found systemd.firstboot=no kernel command line argument, turning off all prompts.");
+                arg_prompt_new_user = false;
+        }
+
+        r = create_from_credentials();
+        if (r < 0)
+                return r;
+        if (r > 0) /* Already created users from credentials */
+                return 0;
+
+        r = has_regular_user();
+        if (r < 0)
+                return r;
+        if (r > 0) {
+                log_info("Regular user already present in user database, skipping user creation.");
+                return 0;
+        }
+
+        return create_interactively();
+}
+
 static int drop_from_identity(const char *field) {
         int r;
 
@@ -2187,6 +2397,7 @@ static int help(int argc, char *argv[], void *userdata) {
                "  deactivate-all               Deactivate all active home areas\n"
                "  rebalance                    Rebalance free space between home areas\n"
                "  with USER [COMMAND…]         Run shell or command with access to a home area\n"
+               "  firstboot                    Run first-boot home area creation wizard\n"
                "\n%4$sOptions:%5$s\n"
                "  -h --help                    Show this help\n"
                "     --version                 Show package version\n"
@@ -2205,6 +2416,8 @@ static int help(int argc, char *argv[], void *userdata) {
                "  -E                           When specified once equals -j --export-format=\n"
                "                               stripped, when specified twice equals\n"
                "                               -j --export-format=minimal\n"
+               "     --prompt-new-user         firstboot: Query user interactively for user\n"
+               "                               to create\n"
                "\n%4$sGeneral User Record Properties:%5$s\n"
                "  -c --real-name=REALNAME      Real name for user\n"
                "     --realm=REALM             Realm to create user in\n"
@@ -2412,6 +2625,7 @@ static int parse_argv(int argc, char *argv[]) {
                 ARG_FIDO2_CRED_ALG,
                 ARG_CAPABILITY_BOUNDING_SET,
                 ARG_CAPABILITY_AMBIENT_SET,
+                ARG_PROMPT_NEW_USER,
         };
 
         static const struct option options[] = {
@@ -2504,6 +2718,7 @@ static int parse_argv(int argc, char *argv[]) {
                 { "rebalance-weight",            required_argument, NULL, ARG_REBALANCE_WEIGHT            },
                 { "capability-bounding-set",     required_argument, NULL, ARG_CAPABILITY_BOUNDING_SET     },
                 { "capability-ambient-set",      required_argument, NULL, ARG_CAPABILITY_AMBIENT_SET      },
+                { "prompt-new-user",             no_argument,       NULL, ARG_PROMPT_NEW_USER             },
                 {}
         };
 
@@ -3788,6 +4003,10 @@ static int parse_argv(int argc, char *argv[]) {
                         break;
                 }
 
+                case ARG_PROMPT_NEW_USER:
+                        arg_prompt_new_user = true;
+                        break;
+
                 case '?':
                         return -EINVAL;
 
@@ -3854,6 +4073,7 @@ static int run(int argc, char *argv[]) {
                 { "lock-all",       VERB_ANY, 1,        0,            lock_all_homes       },
                 { "deactivate-all", VERB_ANY, 1,        0,            deactivate_all_homes },
                 { "rebalance",      VERB_ANY, 1,        0,            rebalance            },
+                { "firstboot",      VERB_ANY, 1,        0,            verb_firstboot       },
                 {}
         };
 
index a47f4d8a844483f134a3b58ac215ee767d56f9ce..413a706f4c363367b586fc2c7c22c422760f23ca 100644 (file)
@@ -203,11 +203,8 @@ int bus_home_method_unregister(
 
         r = bus_verify_polkit_async(
                         message,
-                        CAP_SYS_ADMIN,
                         "org.freedesktop.home1.remove-home",
-                        NULL,
-                        true,
-                        UID_INVALID,
+                        /* details= */ NULL,
                         &h->manager->polkit_registry,
                         error);
         if (r < 0)
@@ -243,11 +240,8 @@ int bus_home_method_realize(
 
         r = bus_verify_polkit_async(
                         message,
-                        CAP_SYS_ADMIN,
                         "org.freedesktop.home1.create-home",
-                        NULL,
-                        true,
-                        UID_INVALID,
+                        /* details= */ NULL,
                         &h->manager->polkit_registry,
                         error);
         if (r < 0)
@@ -283,11 +277,8 @@ int bus_home_method_remove(
 
         r = bus_verify_polkit_async(
                         message,
-                        CAP_SYS_ADMIN,
                         "org.freedesktop.home1.remove-home",
-                        NULL,
-                        true,
-                        UID_INVALID,
+                        /* details= */ NULL,
                         &h->manager->polkit_registry,
                         error);
         if (r < 0)
@@ -354,12 +345,11 @@ int bus_home_method_authenticate(
         if (r < 0)
                 return r;
 
-        r = bus_verify_polkit_async(
+        r = bus_verify_polkit_async_full(
                         message,
-                        CAP_SYS_ADMIN,
                         "org.freedesktop.home1.authenticate-home",
-                        NULL,
-                        true,
+                        /* details= */ NULL,
+                        /* interactive= */ false,
                         h->uid,
                         &h->manager->polkit_registry,
                         error);
@@ -395,11 +385,8 @@ int bus_home_method_update_record(Home *h, sd_bus_message *message, UserRecord *
 
         r = bus_verify_polkit_async(
                         message,
-                        CAP_SYS_ADMIN,
                         "org.freedesktop.home1.update-home",
-                        NULL,
-                        true,
-                        UID_INVALID,
+                        /* details= */ NULL,
                         &h->manager->polkit_registry,
                         error);
         if (r < 0)
@@ -461,11 +448,8 @@ int bus_home_method_resize(
 
         r = bus_verify_polkit_async(
                         message,
-                        CAP_SYS_ADMIN,
                         "org.freedesktop.home1.resize-home",
-                        NULL,
-                        true,
-                        UID_INVALID,
+                        /* details= */ NULL,
                         &h->manager->polkit_registry,
                         error);
         if (r < 0)
@@ -506,12 +490,11 @@ int bus_home_method_change_password(
         if (r < 0)
                 return r;
 
-        r = bus_verify_polkit_async(
+        r = bus_verify_polkit_async_full(
                         message,
-                        CAP_SYS_ADMIN,
                         "org.freedesktop.home1.passwd-home",
-                        NULL,
-                        true,
+                        /* details= */ NULL,
+                        /* interactive= */ false,
                         h->uid,
                         &h->manager->polkit_registry,
                         error);
index 7cf543922f9ae299c580628791de5833a7ba9c4d..b5dffb2c695af826743c5892f6b8101ee9490e98 100644 (file)
@@ -396,11 +396,8 @@ static int method_register_home(
 
         r = bus_verify_polkit_async(
                         message,
-                        CAP_SYS_ADMIN,
                         "org.freedesktop.home1.create-home",
-                        NULL,
-                        true,
-                        UID_INVALID,
+                        /* details= */ NULL,
                         &m->polkit_registry,
                         error);
         if (r < 0)
@@ -443,11 +440,8 @@ static int method_create_home(
 
         r = bus_verify_polkit_async(
                         message,
-                        CAP_SYS_ADMIN,
                         "org.freedesktop.home1.create-home",
-                        NULL,
-                        true,
-                        UID_INVALID,
+                        /* details= */ NULL,
                         &m->polkit_registry,
                         error);
         if (r < 0)
index 5bd78a03ed4d6049a40f76aaf090f93cc0c6e563..57016fb14b0ce171829d7dcb3fec3466ab8841b1 100644 (file)
@@ -199,7 +199,7 @@ static int block_get_size_by_fd(int fd, uint64_t *ret) {
         if (!S_ISBLK(st.st_mode))
                 return -ENOTBLK;
 
-        return RET_NERRNO(ioctl(fd, BLKGETSIZE64, ret));
+        return blockdev_get_device_size(fd, ret);
 }
 
 static int block_get_size_by_path(const char *path, uint64_t *ret) {
@@ -1295,9 +1295,6 @@ int home_setup_luks(
                         if (!IN_SET(errno, ENOTTY, EINVAL))
                                 return log_error_errno(errno, "Failed to get block device metrics of %s: %m", n);
 
-                        if (ioctl(setup->loop->fd, BLKGETSIZE64, &size) < 0)
-                                return log_error_errno(r, "Failed to read block device size of %s: %m", n);
-
                         if (fstat(setup->loop->fd, &st) < 0)
                                 return log_error_errno(r, "Failed to stat block device %s: %m", n);
                         assert(S_ISBLK(st.st_mode));
@@ -1329,6 +1326,8 @@ int home_setup_luks(
 
                                 offset *= 512U;
                         }
+
+                        size = setup->loop->device_size;
                 } else {
 #if HAVE_VALGRIND_MEMCHECK_H
                         VALGRIND_MAKE_MEM_DEFINED(&info, sizeof(info));
@@ -2227,8 +2226,9 @@ int home_create_luks(
                 if (flock(setup->image_fd, LOCK_EX) < 0) /* make sure udev doesn't read from it while we operate on the device */
                         return log_error_errno(errno, "Failed to lock block device %s: %m", ip);
 
-                if (ioctl(setup->image_fd, BLKGETSIZE64, &block_device_size) < 0)
-                        return log_error_errno(errno, "Failed to read block device size: %m");
+                r = blockdev_get_device_size(setup->image_fd, &block_device_size);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to read block device size: %m");
 
                 if (h->disk_size == UINT64_MAX) {
 
@@ -3183,8 +3183,9 @@ int home_resize_luks(
                 } else
                         log_info("Operating on whole block device %s.", ip);
 
-                if (ioctl(image_fd, BLKGETSIZE64, &old_image_size) < 0)
-                        return log_error_errno(errno, "Failed to determine size of original block device: %m");
+                r = blockdev_get_device_size(image_fd, &old_image_size);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to determine size of original block device: %m");
 
                 if (flock(image_fd, LOCK_EX) < 0) /* make sure udev doesn't read from it while we operate on the device */
                         return log_error_errno(errno, "Failed to lock block device %s: %m", ip);
index fc7a97fb99b9e5a0c1bffbdceaa7045111ad589b..893eb4cc0f15be02265cdc8f037419fdea805d62 100644 (file)
@@ -1054,13 +1054,12 @@ static int method_set_hostname(sd_bus_message *m, void *userdata, sd_bus_error *
 
         context_read_etc_hostname(c);
 
-        r = bus_verify_polkit_async(
+        r = bus_verify_polkit_async_full(
                         m,
-                        CAP_SYS_ADMIN,
                         "org.freedesktop.hostname1.set-hostname",
-                        NULL,
+                        /* details= */ NULL,
                         interactive,
-                        UID_INVALID,
+                        /* good_user= */ UID_INVALID,
                         &c->polkit_registry,
                         error);
         if (r < 0)
@@ -1101,13 +1100,12 @@ static int method_set_static_hostname(sd_bus_message *m, void *userdata, sd_bus_
         if (name && !hostname_is_valid(name, 0))
                 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid static hostname '%s'", name);
 
-        r = bus_verify_polkit_async(
+        r = bus_verify_polkit_async_full(
                         m,
-                        CAP_SYS_ADMIN,
                         "org.freedesktop.hostname1.set-static-hostname",
-                        NULL,
+                        /* details= */ NULL,
                         interactive,
-                        UID_INVALID,
+                        /* good_user= */ UID_INVALID,
                         &c->polkit_registry,
                         error);
         if (r < 0)
@@ -1177,17 +1175,15 @@ static int set_machine_info(Context *c, sd_bus_message *m, int prop, sd_bus_mess
                         return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid location '%s'", name);
         }
 
-        /* Since the pretty hostname should always be changed at the
-         * same time as the static one, use the same policy action for
-         * both... */
+        /* Since the pretty hostname should always be changed at the same time as the static one, use the
+         * same policy action for both... */
 
-        r = bus_verify_polkit_async(
+        r = bus_verify_polkit_async_full(
                         m,
-                        CAP_SYS_ADMIN,
                         prop == PROP_PRETTY_HOSTNAME ? "org.freedesktop.hostname1.set-static-hostname" : "org.freedesktop.hostname1.set-machine-info",
-                        NULL,
+                        /* details= */ NULL,
                         interactive,
-                        UID_INVALID,
+                        /* good_user= */ UID_INVALID,
                         &c->polkit_registry,
                         error);
         if (r < 0)
@@ -1259,13 +1255,12 @@ static int method_get_product_uuid(sd_bus_message *m, void *userdata, sd_bus_err
         if (r < 0)
                 return r;
 
-        r = bus_verify_polkit_async(
+        r = bus_verify_polkit_async_full(
                         m,
-                        CAP_SYS_ADMIN,
                         "org.freedesktop.hostname1.get-product-uuid",
-                        NULL,
+                        /* details= */ NULL,
                         interactive,
-                        UID_INVALID,
+                        /* good_user= */ UID_INVALID,
                         &c->polkit_registry,
                         error);
         if (r < 0)
@@ -1306,11 +1301,8 @@ static int method_get_hardware_serial(sd_bus_message *m, void *userdata, sd_bus_
 
         r = bus_verify_polkit_async(
                         m,
-                        CAP_SYS_ADMIN,
                         "org.freedesktop.hostname1.get-hardware-serial",
-                        NULL,
-                        false,
-                        UID_INVALID,
+                        /* details= */ NULL,
                         &c->polkit_registry,
                         error);
         if (r < 0)
@@ -1350,11 +1342,8 @@ static int method_describe(sd_bus_message *m, void *userdata, sd_bus_error *erro
 
         r = bus_verify_polkit_async(
                         m,
-                        CAP_SYS_ADMIN,
                         "org.freedesktop.hostname1.get-description",
-                        NULL,
-                        false,
-                        UID_INVALID,
+                        /* details= */ NULL,
                         &c->polkit_registry,
                         error);
         if (r == 0)
index 4287b1f066015c11d5a3eccc3b437afe7600094d..861dac0fd154ca5e8bb9ea46c613b98d81f2014d 100644 (file)
@@ -22,6 +22,9 @@ static int verb_query(int argc, char *argv[], void *userdata) {
 }
 
 static int verb_update(int argc, char *argv[], void *userdata) {
+        if (hwdb_bypass())
+                return 0;
+
         return hwdb_update(arg_root, arg_hwdb_bin_dir, arg_strict, false);
 }
 
index e1a1ddc2ee186bf3b716dfda28fccaa7ac83f798..e9bbbb628dae84697b66a2bfc72d7287a3d07558 100644 (file)
@@ -704,11 +704,8 @@ static int method_import_tar_or_raw(sd_bus_message *msg, void *userdata, sd_bus_
 
         r = bus_verify_polkit_async(
                         msg,
-                        CAP_SYS_ADMIN,
                         "org.freedesktop.import1.import",
-                        NULL,
-                        false,
-                        UID_INVALID,
+                        /* details= */ NULL,
                         &m->polkit_registry,
                         error);
         if (r < 0)
@@ -775,11 +772,8 @@ static int method_import_fs(sd_bus_message *msg, void *userdata, sd_bus_error *e
 
         r = bus_verify_polkit_async(
                         msg,
-                        CAP_SYS_ADMIN,
                         "org.freedesktop.import1.import",
-                        NULL,
-                        false,
-                        UID_INVALID,
+                        /* details= */ NULL,
                         &m->polkit_registry,
                         error);
         if (r < 0)
@@ -843,11 +837,8 @@ static int method_export_tar_or_raw(sd_bus_message *msg, void *userdata, sd_bus_
 
         r = bus_verify_polkit_async(
                         msg,
-                        CAP_SYS_ADMIN,
                         "org.freedesktop.import1.export",
-                        NULL,
-                        false,
-                        UID_INVALID,
+                        /* details= */ NULL,
                         &m->polkit_registry,
                         error);
         if (r < 0)
@@ -916,11 +907,8 @@ static int method_pull_tar_or_raw(sd_bus_message *msg, void *userdata, sd_bus_er
 
         r = bus_verify_polkit_async(
                         msg,
-                        CAP_SYS_ADMIN,
                         "org.freedesktop.import1.pull",
-                        NULL,
-                        false,
-                        UID_INVALID,
+                        /* details= */ NULL,
                         &m->polkit_registry,
                         error);
         if (r < 0)
@@ -1036,11 +1024,8 @@ static int method_cancel(sd_bus_message *msg, void *userdata, sd_bus_error *erro
 
         r = bus_verify_polkit_async(
                         msg,
-                        CAP_SYS_ADMIN,
                         "org.freedesktop.import1.pull",
-                        NULL,
-                        false,
-                        UID_INVALID,
+                        /* details= */ NULL,
                         &t->manager->polkit_registry,
                         error);
         if (r < 0)
@@ -1065,11 +1050,8 @@ static int method_cancel_transfer(sd_bus_message *msg, void *userdata, sd_bus_er
 
         r = bus_verify_polkit_async(
                         msg,
-                        CAP_SYS_ADMIN,
                         "org.freedesktop.import1.pull",
-                        NULL,
-                        false,
-                        UID_INVALID,
+                        /* details= */ NULL,
                         &m->polkit_registry,
                         error);
         if (r < 0)
index de1534befd2c894db37d0fd1a39852e997d242eb..fe68ec4310b3dc40358e30d3228bca5d4e69c588 100644 (file)
@@ -1088,9 +1088,9 @@ static int parse_argv(int argc, char *argv[]) {
                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
                                        "--since= must be before --until=.");
 
-        if (!!arg_cursor + !!arg_after_cursor + !!arg_since_set > 1)
+        if (!!arg_cursor + !!arg_after_cursor + !!arg_cursor_file + !!arg_since_set > 1)
                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
-                                       "Please specify only one of --since=, --cursor=, and --after-cursor=.");
+                                       "Please specify only one of --since=, --cursor=, --cursor-file=, and --after-cursor=.");
 
         if (arg_follow && arg_reverse)
                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
index f5f6ec590ff02b4cc04a9456154601ace6433fd8..78401f9857c0e174527b5652f83fe07c29cebfc5 100644 (file)
@@ -650,8 +650,8 @@ void client_context_flush_all(Server *s) {
 
         client_context_flush_regular(s);
 
-        assert(prioq_size(s->client_contexts_lru) == 0);
-        assert(hashmap_size(s->client_contexts) == 0);
+        assert(prioq_isempty(s->client_contexts_lru));
+        assert(hashmap_isempty(s->client_contexts));
 
         s->client_contexts_lru = prioq_free(s->client_contexts_lru);
         s->client_contexts = hashmap_free(s->client_contexts);
index d1f8485661e4b78b3bb9418e8e2fa071f1f0e868..476da317fe764e8a976b17020bd3ccda7f0e490f 100644 (file)
@@ -2633,7 +2633,7 @@ int server_init(Server *s, const char *namespace) {
         /* Try to restore streams, but don't bother if this fails */
         (void) server_restore_streams(s, fds);
 
-        if (fdset_size(fds) > 0) {
+        if (!fdset_isempty(fds)) {
                 log_warning("%u unknown file descriptors passed, closing.", fdset_size(fds));
                 fds = fdset_free(fds);
         }
index 978f1795444faa20b7a8b170df94d67c9c6c763b..e10dc0f8699822af299b6dd3fe3908a3bbe7c1fd 100755 (executable)
@@ -21,8 +21,8 @@
 
 import argparse
 import os
-import runpy
 import shlex
+import types
 from shutil import which
 from pathlib import Path
 from typing import Optional
@@ -209,22 +209,20 @@ def initrd_list(opts) -> list[Path]:
     return [*microcode, *opts.initrd, *initrd]
 
 
-def call_ukify(opts):
-    # Punish me harder.
-    # We want this:
-    #   ukify = importlib.machinery.SourceFileLoader('ukify', UKIFY).load_module()
-    # but it throws a DeprecationWarning.
-    # https://stackoverflow.com/questions/67631/how-can-i-import-a-module-dynamically-given-the-full-path
-    # https://github.com/python/cpython/issues/65635
-    # offer "explanations", but to actually load a python file without a .py extension,
-    # the "solution" is 4+ incomprehensible lines.
-    # The solution with runpy gives a dictionary, which isn't great, but will do.
-    ukify = runpy.run_path(UKIFY, run_name='ukify')
+def load_module(name: str, path: str) -> types.ModuleType:
+    module = types.ModuleType(name)
+    text = open(path).read()
+    exec(compile(text, path, 'exec'), module.__dict__)
+    return module
+
+
+def call_ukify(opts) -> None:
+    ukify = load_module('ukify', UKIFY)
 
     # Create "empty" namespace. We want to override just a few settings, so it
     # doesn't make sense to configure everything. We pretend to parse an empty
     # argument set to prepopulate the namespace with the defaults.
-    opts2 = ukify['create_parser']().parse_args(['build'])
+    opts2 = ukify.create_parser().parse_args(['build'])
 
     opts2.config = uki_conf_location()
     opts2.uname = opts.kernel_version
@@ -240,12 +238,10 @@ def call_ukify(opts):
     if BOOT_STUB:
         opts2.stub = BOOT_STUB
 
-    # opts2.summary = True
-
-    ukify['apply_config'](opts2)
-    ukify['finalize_options'](opts2)
-    ukify['check_inputs'](opts2)
-    ukify['make_uki'](opts2)
+    ukify.apply_config(opts2)
+    ukify.finalize_options(opts2)
+    ukify.check_inputs(opts2)
+    ukify.make_uki(opts2)
 
     log(f'{opts2.output} has been created')
 
index f9c59a1007c729d17a44ef68141bba131b358041..6f6fa2d943aa621c29192ac962e66a4eb076f3ce 100644 (file)
@@ -69,7 +69,7 @@ static void bus_track_add_to_queue(sd_bus_track *track) {
                 return;
 
         /* still referenced? */
-        if (hashmap_size(track->names) > 0)
+        if (!hashmap_isempty(track->names))
                 return;
 
         /* Nothing to call? */
index 13801becc90565a39f3946c8f52fd9b624557608..d18ce88a25612bfb13c0a982e8b8e38a92752819 100644 (file)
@@ -12,7 +12,7 @@ int main(int argc, char *argv[]) {
 
         test_setup_logging(LOG_DEBUG);
 
-        if (cg_unified() == -ENOMEDIUM)
+        if (IN_SET(cg_unified(), -ENOMEDIUM, -ENOENT))
                 return log_tests_skipped("/sys/fs/cgroup/ not available");
 
         r = sd_bus_creds_new_from_pid(&creds, 0, _SD_BUS_CREDS_ALL);
index e124e0021c352240641330001978c4b65c3087e0..4f80c4e57941caaf74b0f4c24791d6c73dea6df4 100644 (file)
@@ -10,6 +10,7 @@
 #include "device-private.h"
 #include "device-util.h"
 #include "macro.h"
+#include "mountpoint-util.h"
 #include "path-util.h"
 #include "stat-util.h"
 #include "string-util.h"
@@ -298,6 +299,9 @@ int main(int argc, char *argv[]) {
         if (getuid() != 0)
                 return log_tests_skipped("not root");
 
+        if (path_is_mount_point("/sys", NULL, 0) <= 0)
+                return log_tests_skipped("/sys is not mounted");
+
         if (path_is_read_only_fs("/sys") > 0)
                 return log_tests_skipped("Running in container");
 
index c99d179b3351a079807106e7deebe9b20e837149..539dabd7a6b75d4b2d09bdfcc2def80334d3b72b 100644 (file)
@@ -8,6 +8,7 @@
 #include "sd-device.h"
 
 #include "device-util.h"
+#include "tests.h"
 
 #define handle_error_errno(error, msg)                          \
         ({                                                      \
@@ -30,6 +31,8 @@ int main(int argc, char *argv[]) {
         int r;
 
         r = sd_device_new_from_syspath(&loopback, "/sys/class/net/lo");
+        if (r == -ENODEV)
+                return log_tests_skipped("Loopback device not found");
         if (r < 0)
                 return handle_error_errno(r, "Failed to create loopback device object");
 
index bce99b55186a2392d2511d0444577228ca76200f..f64f6013cf723b2f2790f69e6d3c8f0e6938db3b 100644 (file)
@@ -11,6 +11,7 @@
 #include "errno-util.h"
 #include "fd-util.h"
 #include "hashmap.h"
+#include "mountpoint-util.h"
 #include "nulstr-util.h"
 #include "path-util.h"
 #include "rm-rf.h"
@@ -675,4 +676,11 @@ TEST(devname_from_devnum) {
         }
 }
 
-DEFINE_TEST_MAIN(LOG_INFO);
+static int intro(void) {
+        if (path_is_mount_point("/sys", NULL, 0) <= 0)
+                return log_tests_skipped("/sys is not mounted");
+
+        return EXIT_SUCCESS;
+}
+
+DEFINE_TEST_MAIN_WITH_INTRO(LOG_INFO, intro);
index 4c5562ba488182fa35503bb79dc6f9e0020379c4..832b678bf33423d214c17437b841f795bb0f3cb7 100644 (file)
@@ -452,11 +452,12 @@ int catalog_update(const char* database, const char* root, const char* const* di
                         return log_error_errno(r, "Failed to import file '%s': %m", *f);
         }
 
-        if (ordered_hashmap_size(h) <= 0) {
+        if (ordered_hashmap_isempty(h)) {
                 log_info("No items in catalog.");
                 return 0;
-        } else
-                log_debug("Found %u items in catalog.", ordered_hashmap_size(h));
+        }
+
+        log_debug("Found %u items in catalog.", ordered_hashmap_size(h));
 
         items = new(CatalogItem, ordered_hashmap_size(h));
         if (!items)
index 2812eb386a220f6c40f575984573891a3d50a0d4..cef59c8944ed337ba1b551407506faf4833c3a3d 100644 (file)
 #define DEFAULT_COMPRESS_THRESHOLD (512ULL)
 #define MIN_COMPRESS_THRESHOLD (8ULL)
 
-#define U64_KB UINT64_C(1024)
-#define U64_MB (UINT64_C(1024) * U64_KB)
-#define U64_GB (UINT64_C(1024) * U64_MB)
-
 /* This is the minimum journal file size */
 #define JOURNAL_FILE_SIZE_MIN (512 * U64_KB)             /* 512 KiB */
 #define JOURNAL_COMPACT_SIZE_MAX ((uint64_t) UINT32_MAX) /* 4 GiB */
index 6b9ff0a4ed880557b940a9d37b79ca88119c6b6b..f2a0c666703be94be7657459414e99ad453c0a69 100644 (file)
@@ -932,8 +932,8 @@ static int real_journal_next(sd_journal *j, direction_t direction) {
         if (r < 0)
                 return r;
 
-        for (unsigned i = 0; i < n_files; i++) {
-                JournalFile *f = (JournalFile *)files[i];
+        FOREACH_ARRAY(_f, files, n_files) {
+                JournalFile *f = (JournalFile*) *_f;
                 bool found;
 
                 r = next_beyond_location(j, f, direction);
index 819f86f7446558026dac76677d4a7712e894dbfb..313e5e339da45ad135c8888d70a02d0e7bc3549b 100644 (file)
@@ -10,6 +10,7 @@
 #include "format-util.h"
 #include "log.h"
 #include "missing_syscall.h"
+#include "mountpoint-util.h"
 #include "process-util.h"
 #include "string-util.h"
 #include "strv.h"
@@ -327,6 +328,9 @@ TEST(monitor) {
 }
 
 static int intro(void) {
+        if (IN_SET(cg_unified(), -ENOENT, -ENOMEDIUM))
+                return log_tests_skipped("cgroupfs is not mounted");
+
         log_info("/* Information printed is from the live system */");
         return EXIT_SUCCESS;
 }
index 008e8022b1dbe376b7e2b6197d24658a567a888c..09116b14380ce445451feb8fc37d3028e9c81818 100644 (file)
@@ -336,7 +336,7 @@ int sd_rtnl_message_new_nexthop(sd_netlink *rtnl, sd_netlink_message **ret,
                 return r;
 
         if (nlmsg_type == RTM_NEWNEXTHOP)
-                (*ret)->hdr->nlmsg_flags |= NLM_F_CREATE | NLM_F_APPEND;
+                (*ret)->hdr->nlmsg_flags |= NLM_F_CREATE | NLM_F_REPLACE;
 
         nhm = NLMSG_DATA((*ret)->hdr);
 
index b6730b71dc15a907f01ecaa67405e2b81afab047..b347ee6339530bf7b9de9738eccb9b289439f3f5 100644 (file)
@@ -176,7 +176,7 @@ static int dispatch_rqueue(sd_netlink *nl, sd_netlink_message **ret) {
         assert(nl);
         assert(ret);
 
-        if (ordered_set_size(nl->rqueue) <= 0) {
+        if (ordered_set_isempty(nl->rqueue)) {
                 /* Try to read a new message */
                 r = socket_read_message(nl);
                 if (r == -ENOBUFS) /* FIXME: ignore buffer overruns for now */
@@ -443,7 +443,7 @@ int sd_netlink_wait(sd_netlink *nl, uint64_t timeout_usec) {
         assert_return(nl, -EINVAL);
         assert_return(!netlink_pid_changed(nl), -ECHILD);
 
-        if (ordered_set_size(nl->rqueue) > 0)
+        if (!ordered_set_isempty(nl->rqueue))
                 return 0;
 
         r = netlink_poll(nl, false, timeout_usec);
@@ -623,7 +623,7 @@ int sd_netlink_get_events(sd_netlink *nl) {
         assert_return(nl, -EINVAL);
         assert_return(!netlink_pid_changed(nl), -ECHILD);
 
-        return ordered_set_size(nl->rqueue) == 0 ? POLLIN : 0;
+        return ordered_set_isempty(nl->rqueue) ? POLLIN : 0;
 }
 
 int sd_netlink_get_timeout(sd_netlink *nl, uint64_t *timeout_usec) {
@@ -633,7 +633,7 @@ int sd_netlink_get_timeout(sd_netlink *nl, uint64_t *timeout_usec) {
         assert_return(timeout_usec, -EINVAL);
         assert_return(!netlink_pid_changed(nl), -ECHILD);
 
-        if (ordered_set_size(nl->rqueue) > 0) {
+        if (!ordered_set_isempty(nl->rqueue)) {
                 *timeout_usec = 0;
                 return 1;
         }
index c082fdca46843e03fc078df71b254e5854f3b43d..fdf0818863135d3125faedca6cb0ebc24fe2e8f9 100644 (file)
@@ -6,6 +6,7 @@
 #include <stdlib.h>
 
 #include "libudev.h"
+#include "tests.h"
 
 #define handle_error_errno(error, msg)                          \
         ({                                                      \
@@ -29,8 +30,12 @@ int main(int argc, char *argv[]) {
         int r;
 
         loopback = udev_device_new_from_syspath(NULL, "/sys/class/net/lo");
-        if (!loopback)
+        if (!loopback) {
+                if (errno == ENODEV)
+                        return log_tests_skipped_errno(errno, "Loopback device not found");
+
                 return handle_error_errno(errno, "Failed to create loopback device object");
+        }
 
         entry = udev_device_get_properties_list_entry(loopback);
         udev_list_entry_foreach(e, entry)
index 5d96237fae6290b4b9f0abc028211781ae22f294..8ce8c0d08fc5eff6003fcbe78555ee870c6d0125 100644 (file)
@@ -281,13 +281,12 @@ static int method_set_locale(sd_bus_message *m, void *userdata, sd_bus_error *er
                 return sd_bus_reply_method_return(m, NULL);
         }
 
-        r = bus_verify_polkit_async(
+        r = bus_verify_polkit_async_full(
                         m,
-                        CAP_SYS_ADMIN,
                         "org.freedesktop.locale1.set-locale",
-                        NULL,
+                        /* details= */ NULL,
                         interactive,
-                        UID_INVALID,
+                        /* good_user= */ UID_INVALID,
                         &c->polkit_registry,
                         error);
         if (r < 0)
@@ -386,13 +385,12 @@ static int method_set_vc_keyboard(sd_bus_message *m, void *userdata, sd_bus_erro
         if (vc_context_equal(&c->vc, &in) && !x_needs_update)
                 return sd_bus_reply_method_return(m, NULL);
 
-        r = bus_verify_polkit_async(
+        r = bus_verify_polkit_async_full(
                         m,
-                        CAP_SYS_ADMIN,
                         "org.freedesktop.locale1.set-keyboard",
-                        NULL,
+                        /* details= */ NULL,
                         interactive,
-                        UID_INVALID,
+                        /* good_user= */ UID_INVALID,
                         &c->polkit_registry,
                         error);
         if (r < 0)
@@ -506,13 +504,12 @@ static int method_set_x11_keyboard(sd_bus_message *m, void *userdata, sd_bus_err
         if (x11_context_equal(&c->x11_from_vc, &in) && x11_context_equal(&c->x11_from_xorg, &in) && !convert)
                 return sd_bus_reply_method_return(m, NULL);
 
-        r = bus_verify_polkit_async(
+        r = bus_verify_polkit_async_full(
                         m,
-                        CAP_SYS_ADMIN,
                         "org.freedesktop.locale1.set-keyboard",
-                        NULL,
+                        /* details= */ NULL,
                         interactive,
-                        UID_INVALID,
+                        /* good_user= */ UID_INVALID,
                         &c->polkit_registry,
                         error);
         if (r < 0)
index 34992b5681059a00215754bd9aa1a589e61a444a..898c6f752b6e70434f8e6c8244d8c7b15b8fb670 100644 (file)
@@ -236,7 +236,6 @@ int manager_get_seat_from_creds(
 
 static int return_test_polkit(
                 sd_bus_message *message,
-                int capability,
                 const char *action,
                 const char **details,
                 uid_t good_user,
@@ -246,7 +245,7 @@ static int return_test_polkit(
         bool challenge;
         int r;
 
-        r = bus_test_polkit(message, capability, action, details, good_user, &challenge, e);
+        r = bus_test_polkit(message, action, details, good_user, &challenge, e);
         if (r < 0)
                 return r;
 
@@ -1245,11 +1244,8 @@ static int method_lock_sessions(sd_bus_message *message, void *userdata, sd_bus_
 
         r = bus_verify_polkit_async(
                         message,
-                        CAP_SYS_ADMIN,
                         "org.freedesktop.login1.lock-sessions",
-                        NULL,
-                        false,
-                        UID_INVALID,
+                        /* details= */ NULL,
                         &m->polkit_registry,
                         error);
         if (r < 0)
@@ -1397,14 +1393,13 @@ static int method_set_user_linger(sd_bus_message *message, void *userdata, sd_bu
         if (!pw)
                 return errno_or_else(ENOENT);
 
-        r = bus_verify_polkit_async(
+        r = bus_verify_polkit_async_full(
                         message,
-                        CAP_SYS_ADMIN,
                         uid == auth_uid ? "org.freedesktop.login1.set-self-linger" :
                                           "org.freedesktop.login1.set-user-linger",
-                        NULL,
+                        /* details= */ NULL,
                         interactive,
-                        UID_INVALID,
+                        /* good_user= */ UID_INVALID,
                         &m->polkit_registry,
                         error);
         if (r < 0)
@@ -1565,13 +1560,12 @@ static int method_attach_device(sd_bus_message *message, void *userdata, sd_bus_
         } else if (!seat_name_is_valid(seat)) /* Note that a seat does not have to exist yet for this operation to succeed */
                 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Seat name %s is not valid", seat);
 
-        r = bus_verify_polkit_async(
+        r = bus_verify_polkit_async_full(
                         message,
-                        CAP_SYS_ADMIN,
                         "org.freedesktop.login1.attach-device",
-                        NULL,
+                        /* details= */ NULL,
                         interactive,
-                        UID_INVALID,
+                        /* good_user= */ UID_INVALID,
                         &m->polkit_registry,
                         error);
         if (r < 0)
@@ -1596,13 +1590,12 @@ static int method_flush_devices(sd_bus_message *message, void *userdata, sd_bus_
         if (r < 0)
                 return r;
 
-        r = bus_verify_polkit_async(
+        r = bus_verify_polkit_async_full(
                         message,
-                        CAP_SYS_ADMIN,
                         "org.freedesktop.login1.flush-devices",
-                        NULL,
+                        /* details= */ NULL,
                         interactive,
-                        UID_INVALID,
+                        /* good_user= */ UID_INVALID,
                         &m->polkit_registry,
                         error);
         if (r < 0)
@@ -1938,13 +1931,12 @@ static int verify_shutdown_creds(
         interactive = flags & SD_LOGIND_INTERACTIVE;
 
         if (multiple_sessions) {
-                r = bus_verify_polkit_async(
+                r = bus_verify_polkit_async_full(
                                 message,
-                                CAP_SYS_BOOT,
                                 a->polkit_action_multiple_sessions,
-                                NULL,
+                                /* details= */ NULL,
                                 interactive,
-                                UID_INVALID,
+                                /* good_user= */ UID_INVALID,
                                 &m->polkit_registry,
                                 error);
                 if (r < 0)
@@ -1959,12 +1951,12 @@ static int verify_shutdown_creds(
                         return sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED,
                                                  "Access denied to root due to active block inhibitor");
 
-                r = bus_verify_polkit_async(message,
-                                CAP_SYS_BOOT,
+                r = bus_verify_polkit_async_full(
+                                message,
                                 a->polkit_action_ignore_inhibit,
-                                NULL,
+                                /* details= */ NULL,
                                 interactive,
-                                UID_INVALID,
+                                /* good_user= */ UID_INVALID,
                                 &m->polkit_registry,
                                 error);
                 if (r < 0)
@@ -1974,12 +1966,12 @@ static int verify_shutdown_creds(
         }
 
         if (!multiple_sessions && !blocked) {
-                r = bus_verify_polkit_async(message,
-                                CAP_SYS_BOOT,
+                r = bus_verify_polkit_async_full(
+                                message,
                                 a->polkit_action,
-                                NULL,
+                                /* details= */ NULL,
                                 interactive,
-                                UID_INVALID,
+                                /* good_user= */ UID_INVALID,
                                 &m->polkit_registry,
                                 error);
                 if (r < 0)
@@ -2529,11 +2521,8 @@ static int method_cancel_scheduled_shutdown(sd_bus_message *message, void *userd
 
         r = bus_verify_polkit_async(
                         message,
-                        CAP_SYS_BOOT,
                         a->polkit_action,
-                        NULL,
-                        false,
-                        UID_INVALID,
+                        /* details= */ NULL,
                         &m->polkit_registry,
                         error);
         if (r < 0)
@@ -2640,7 +2629,13 @@ static int method_can_shutdown_or_sleep(
         }
 
         if (multiple_sessions) {
-                r = bus_test_polkit(message, CAP_SYS_BOOT, a->polkit_action_multiple_sessions, NULL, UID_INVALID, &challenge, error);
+                r = bus_test_polkit(
+                                message,
+                                a->polkit_action_multiple_sessions,
+                                /* details= */ NULL,
+                                /* good_user= */ UID_INVALID,
+                                &challenge,
+                                error);
                 if (r < 0)
                         return r;
 
@@ -2653,7 +2648,13 @@ static int method_can_shutdown_or_sleep(
         }
 
         if (blocked) {
-                r = bus_test_polkit(message, CAP_SYS_BOOT, a->polkit_action_ignore_inhibit, NULL, UID_INVALID, &challenge, error);
+                r = bus_test_polkit(
+                                message,
+                                a->polkit_action_ignore_inhibit,
+                                /* details= */ NULL,
+                                /* good_user= */ UID_INVALID,
+                                &challenge,
+                                error);
                 if (r < 0)
                         return r;
 
@@ -2671,7 +2672,13 @@ static int method_can_shutdown_or_sleep(
                 /* If neither inhibit nor multiple sessions
                  * apply then just check the normal policy */
 
-                r = bus_test_polkit(message, CAP_SYS_BOOT, a->polkit_action, NULL, UID_INVALID, &challenge, error);
+                r = bus_test_polkit(
+                                message,
+                                a->polkit_action,
+                                /* details= */ NULL,
+                                /* good_user= */ UID_INVALID,
+                                &challenge,
+                                error);
                 if (r < 0)
                         return r;
 
@@ -2779,14 +2786,12 @@ static int method_set_reboot_parameter(
                 return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED,
                                          "Reboot parameter not supported in containers, refusing.");
 
-        r = bus_verify_polkit_async(message,
-                                    CAP_SYS_ADMIN,
-                                    "org.freedesktop.login1.set-reboot-parameter",
-                                    NULL,
-                                    false,
-                                    UID_INVALID,
-                                    &m->polkit_registry,
-                                    error);
+        r = bus_verify_polkit_async(
+                        message,
+                        "org.freedesktop.login1.set-reboot-parameter",
+                        /* details= */ NULL,
+                        &m->polkit_registry,
+                        error);
         if (r < 0)
                 return r;
         if (r == 0)
@@ -2817,10 +2822,9 @@ static int method_can_reboot_parameter(
 
         return return_test_polkit(
                         message,
-                        CAP_SYS_ADMIN,
                         "org.freedesktop.login1.set-reboot-parameter",
-                        NULL,
-                        UID_INVALID,
+                        /* details= */ NULL,
+                        /* good_user= */ UID_INVALID,
                         error);
 }
 
@@ -2898,14 +2902,12 @@ static int method_set_reboot_to_firmware_setup(
                 /* non-EFI case: $SYSTEMD_REBOOT_TO_FIRMWARE_SETUP is set to on */
                 use_efi = false;
 
-        r = bus_verify_polkit_async(message,
-                                    CAP_SYS_ADMIN,
-                                    "org.freedesktop.login1.set-reboot-to-firmware-setup",
-                                    NULL,
-                                    false,
-                                    UID_INVALID,
-                                    &m->polkit_registry,
-                                    error);
+        r = bus_verify_polkit_async(
+                        message,
+                        "org.freedesktop.login1.set-reboot-to-firmware-setup",
+                        /* details= */ NULL,
+                        &m->polkit_registry,
+                        error);
         if (r < 0)
                 return r;
         if (r == 0)
@@ -2962,10 +2964,9 @@ static int method_can_reboot_to_firmware_setup(
 
         return return_test_polkit(
                         message,
-                        CAP_SYS_ADMIN,
                         "org.freedesktop.login1.set-reboot-to-firmware-setup",
-                        NULL,
-                        UID_INVALID,
+                        /* details= */ NULL,
+                        /* good_user= */ UID_INVALID,
                         error);
 }
 
@@ -3062,14 +3063,12 @@ static int method_set_reboot_to_boot_loader_menu(
                 /* non-EFI case: $SYSTEMD_REBOOT_TO_BOOT_LOADER_MENU is set to on */
                 use_efi = false;
 
-        r = bus_verify_polkit_async(message,
-                                    CAP_SYS_ADMIN,
-                                    "org.freedesktop.login1.set-reboot-to-boot-loader-menu",
-                                    NULL,
-                                    false,
-                                    UID_INVALID,
-                                    &m->polkit_registry,
-                                    error);
+        r = bus_verify_polkit_async(
+                        message,
+                        "org.freedesktop.login1.set-reboot-to-boot-loader-menu",
+                        /* details= */ NULL,
+                        &m->polkit_registry,
+                        error);
         if (r < 0)
                 return r;
         if (r == 0)
@@ -3137,10 +3136,9 @@ static int method_can_reboot_to_boot_loader_menu(
 
         return return_test_polkit(
                         message,
-                        CAP_SYS_ADMIN,
                         "org.freedesktop.login1.set-reboot-to-boot-loader-menu",
-                        NULL,
-                        UID_INVALID,
+                        /* details= */ NULL,
+                        /* good_user= */ UID_INVALID,
                         error);
 }
 
@@ -3261,14 +3259,12 @@ static int method_set_reboot_to_boot_loader_entry(
                 /* non-EFI case: $SYSTEMD_REBOOT_TO_BOOT_LOADER_ENTRY is set to on */
                 use_efi = false;
 
-        r = bus_verify_polkit_async(message,
-                                    CAP_SYS_ADMIN,
-                                    "org.freedesktop.login1.set-reboot-to-boot-loader-entry",
-                                    NULL,
-                                    false,
-                                    UID_INVALID,
-                                    &m->polkit_registry,
-                                    error);
+        r = bus_verify_polkit_async(
+                        message,
+                        "org.freedesktop.login1.set-reboot-to-boot-loader-entry",
+                        /* details= */ NULL,
+                        &m->polkit_registry,
+                        error);
         if (r < 0)
                 return r;
         if (r == 0)
@@ -3329,10 +3325,9 @@ static int method_can_reboot_to_boot_loader_entry(
 
         return return_test_polkit(
                         message,
-                        CAP_SYS_ADMIN,
                         "org.freedesktop.login1.set-reboot-to-boot-loader-entry",
-                        NULL,
-                        UID_INVALID,
+                        /* details= */ NULL,
+                        /* good_user= */ UID_INVALID,
                         error);
 }
 
@@ -3403,14 +3398,12 @@ static int method_set_wall_message(
             m->enable_wall_messages == enable_wall_messages)
                 goto done;
 
-        r = bus_verify_polkit_async(message,
-                                    CAP_SYS_ADMIN,
-                                    "org.freedesktop.login1.set-wall-message",
-                                    NULL,
-                                    false,
-                                    UID_INVALID,
-                                    &m->polkit_registry,
-                                    error);
+        r = bus_verify_polkit_async(
+                        message,
+                        "org.freedesktop.login1.set-wall-message",
+                        /* details= */ NULL,
+                        &m->polkit_registry,
+                        error);
         if (r < 0)
                 return r;
         if (r == 0)
@@ -3470,7 +3463,6 @@ static int method_inhibit(sd_bus_message *message, void *userdata, sd_bus_error
 
         r = bus_verify_polkit_async(
                         message,
-                        CAP_SYS_BOOT,
                         w == INHIBIT_SHUTDOWN             ? (mm == INHIBIT_BLOCK ? "org.freedesktop.login1.inhibit-block-shutdown" : "org.freedesktop.login1.inhibit-delay-shutdown") :
                         w == INHIBIT_SLEEP                ? (mm == INHIBIT_BLOCK ? "org.freedesktop.login1.inhibit-block-sleep"    : "org.freedesktop.login1.inhibit-delay-sleep") :
                         w == INHIBIT_IDLE                 ? "org.freedesktop.login1.inhibit-block-idle" :
@@ -3479,9 +3471,7 @@ static int method_inhibit(sd_bus_message *message, void *userdata, sd_bus_error
                         w == INHIBIT_HANDLE_REBOOT_KEY    ? "org.freedesktop.login1.inhibit-handle-reboot-key" :
                         w == INHIBIT_HANDLE_HIBERNATE_KEY ? "org.freedesktop.login1.inhibit-handle-hibernate-key" :
                                                             "org.freedesktop.login1.inhibit-handle-lid-switch",
-                        NULL,
-                        false,
-                        UID_INVALID,
+                        /* details= */ NULL,
                         &m->polkit_registry,
                         error);
         if (r < 0)
index e4efd6461673f1cfd1f631d7c85ccecb472464cd..2f1523ae04b70e5439a039707c8669e19048aaa7 100644 (file)
@@ -9,11 +9,8 @@ int check_polkit_chvt(sd_bus_message *message, Manager *manager, sd_bus_error *e
 #if ENABLE_POLKIT
         return bus_verify_polkit_async(
                         message,
-                        CAP_SYS_ADMIN,
                         "org.freedesktop.login1.chvt",
-                        NULL,
-                        false,
-                        UID_INVALID,
+                        /* details= */ NULL,
                         &manager->polkit_registry,
                         error);
 #else
index 877b9c1af14faf12468f1de36c9a75b4427f81d3..0a395c65095fad2967df01072685a244e92b48bd 100644 (file)
@@ -134,11 +134,8 @@ int bus_seat_method_terminate(sd_bus_message *message, void *userdata, sd_bus_er
 
         r = bus_verify_polkit_async(
                         message,
-                        CAP_KILL,
                         "org.freedesktop.login1.manage",
-                        NULL,
-                        false,
-                        UID_INVALID,
+                        /* details= */ NULL,
                         &s->manager->polkit_registry,
                         error);
         if (r < 0)
index a136ae418cee126d453a84f0f11897a87720b631..9348ccc4dd0e3f7a65306c12fa6a9febbf66f3ca 100644 (file)
@@ -158,12 +158,11 @@ int bus_session_method_terminate(sd_bus_message *message, void *userdata, sd_bus
 
         assert(message);
 
-        r = bus_verify_polkit_async(
+        r = bus_verify_polkit_async_full(
                         message,
-                        CAP_KILL,
                         "org.freedesktop.login1.manage",
-                        NULL,
-                        false,
+                        /* details= */ NULL,
+                        /* interactive= */ false,
                         s->user->user_record->uid,
                         &s->manager->polkit_registry,
                         error);
@@ -204,12 +203,11 @@ int bus_session_method_lock(sd_bus_message *message, void *userdata, sd_bus_erro
 
         assert(message);
 
-        r = bus_verify_polkit_async(
+        r = bus_verify_polkit_async_full(
                         message,
-                        CAP_SYS_ADMIN,
                         "org.freedesktop.login1.lock-sessions",
-                        NULL,
-                        false,
+                        /* details= */ NULL,
+                        /* interactive= */ false,
                         s->user->user_record->uid,
                         &s->manager->polkit_registry,
                         error);
@@ -309,12 +307,11 @@ int bus_session_method_kill(sd_bus_message *message, void *userdata, sd_bus_erro
         if (!SIGNAL_VALID(signo))
                 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid signal %i", signo);
 
-        r = bus_verify_polkit_async(
+        r = bus_verify_polkit_async_full(
                         message,
-                        CAP_KILL,
                         "org.freedesktop.login1.manage",
-                        NULL,
-                        false,
+                        /* details= */ NULL,
+                        /* interactive= */ false,
                         s->user->user_record->uid,
                         &s->manager->polkit_registry,
                         error);
index 88649b2f4be36b6f0b4576a2676461f3ed2c2ff1..0763a5b03f3acc1e10aa6d15c47f465bbbf7fa47 100644 (file)
@@ -192,12 +192,11 @@ int bus_user_method_terminate(sd_bus_message *message, void *userdata, sd_bus_er
 
         assert(message);
 
-        r = bus_verify_polkit_async(
+        r = bus_verify_polkit_async_full(
                         message,
-                        CAP_KILL,
                         "org.freedesktop.login1.manage",
-                        NULL,
-                        false,
+                        /* details= */ NULL,
+                        /* interactive= */ false,
                         u->user_record->uid,
                         &u->manager->polkit_registry,
                         error);
@@ -220,12 +219,11 @@ int bus_user_method_kill(sd_bus_message *message, void *userdata, sd_bus_error *
 
         assert(message);
 
-        r = bus_verify_polkit_async(
+        r = bus_verify_polkit_async_full(
                         message,
-                        CAP_KILL,
                         "org.freedesktop.login1.manage",
-                        NULL,
-                        false,
+                        /* details= */ NULL,
+                        /* interactive= */ false,
                         u->user_record->uid,
                         &u->manager->polkit_registry,
                         error);
index aa4525ddbdaeeeb5e718c0beecea212ecc29b063..69039de2e6c3fe645d7a60dd65687c5ee0a34474 100644 (file)
@@ -50,11 +50,8 @@ int bus_image_method_remove(
 
         r = bus_verify_polkit_async(
                         message,
-                        CAP_SYS_ADMIN,
                         "org.freedesktop.machine1.manage-images",
                         details,
-                        false,
-                        UID_INVALID,
                         &m->polkit_registry,
                         error);
         if (r < 0)
@@ -121,11 +118,8 @@ int bus_image_method_rename(
 
         r = bus_verify_polkit_async(
                         message,
-                        CAP_SYS_ADMIN,
                         "org.freedesktop.machine1.manage-images",
                         details,
-                        false,
-                        UID_INVALID,
                         &m->polkit_registry,
                         error);
         if (r < 0)
@@ -173,11 +167,8 @@ int bus_image_method_clone(
 
         r = bus_verify_polkit_async(
                         message,
-                        CAP_SYS_ADMIN,
                         "org.freedesktop.machine1.manage-images",
                         details,
-                        false,
-                        UID_INVALID,
                         &m->polkit_registry,
                         error);
         if (r < 0)
@@ -240,11 +231,8 @@ int bus_image_method_mark_read_only(
 
         r = bus_verify_polkit_async(
                         message,
-                        CAP_SYS_ADMIN,
                         "org.freedesktop.machine1.manage-images",
                         details,
-                        false,
-                        UID_INVALID,
                         &m->polkit_registry,
                         error);
         if (r < 0)
@@ -285,11 +273,8 @@ int bus_image_method_set_limit(
 
         r = bus_verify_polkit_async(
                         message,
-                        CAP_SYS_ADMIN,
                         "org.freedesktop.machine1.manage-images",
                         details,
-                        false,
-                        UID_INVALID,
                         &m->polkit_registry,
                         error);
         if (r < 0)
index 4620f32d627e351582f9b71a0f1519959221cf6a..6c2c2232fe3a6b46a908f52439409323f5a552ff 100644 (file)
@@ -73,11 +73,8 @@ int bus_machine_method_unregister(sd_bus_message *message, void *userdata, sd_bu
 
         r = bus_verify_polkit_async(
                         message,
-                        CAP_KILL,
                         "org.freedesktop.machine1.manage-machines",
                         details,
-                        false,
-                        UID_INVALID,
                         &m->manager->polkit_registry,
                         error);
         if (r < 0)
@@ -106,11 +103,8 @@ int bus_machine_method_terminate(sd_bus_message *message, void *userdata, sd_bus
 
         r = bus_verify_polkit_async(
                         message,
-                        CAP_KILL,
                         "org.freedesktop.machine1.manage-machines",
                         details,
-                        false,
-                        UID_INVALID,
                         &m->manager->polkit_registry,
                         error);
         if (r < 0)
@@ -157,11 +151,8 @@ int bus_machine_method_kill(sd_bus_message *message, void *userdata, sd_bus_erro
 
         r = bus_verify_polkit_async(
                         message,
-                        CAP_KILL,
                         "org.freedesktop.machine1.manage-machines",
                         details,
-                        false,
-                        UID_INVALID,
                         &m->manager->polkit_registry,
                         error);
         if (r < 0)
@@ -449,11 +440,8 @@ int bus_machine_method_open_pty(sd_bus_message *message, void *userdata, sd_bus_
 
         r = bus_verify_polkit_async(
                         message,
-                        CAP_SYS_ADMIN,
                         m->class == MACHINE_HOST ? "org.freedesktop.machine1.host-open-pty" : "org.freedesktop.machine1.open-pty",
                         details,
-                        false,
-                        UID_INVALID,
                         &m->manager->polkit_registry,
                         error);
         if (r < 0)
@@ -541,11 +529,8 @@ int bus_machine_method_open_login(sd_bus_message *message, void *userdata, sd_bu
 
         r = bus_verify_polkit_async(
                         message,
-                        CAP_SYS_ADMIN,
                         m->class == MACHINE_HOST ? "org.freedesktop.machine1.host-login" : "org.freedesktop.machine1.login",
                         details,
-                        false,
-                        UID_INVALID,
                         &m->manager->polkit_registry,
                         error);
         if (r < 0)
@@ -656,11 +641,8 @@ int bus_machine_method_open_shell(sd_bus_message *message, void *userdata, sd_bu
 
         r = bus_verify_polkit_async(
                         message,
-                        CAP_SYS_ADMIN,
                         m->class == MACHINE_HOST ? "org.freedesktop.machine1.host-shell" : "org.freedesktop.machine1.shell",
                         details,
-                        false,
-                        UID_INVALID,
                         &m->manager->polkit_registry,
                         error);
         if (r < 0)
@@ -861,11 +843,8 @@ int bus_machine_method_bind_mount(sd_bus_message *message, void *userdata, sd_bu
 
         r = bus_verify_polkit_async(
                         message,
-                        CAP_SYS_ADMIN,
                         "org.freedesktop.machine1.manage-machines",
                         details,
-                        false,
-                        UID_INVALID,
                         &m->manager->polkit_registry,
                         error);
         if (r < 0)
@@ -949,11 +928,8 @@ int bus_machine_method_copy(sd_bus_message *message, void *userdata, sd_bus_erro
 
         r = bus_verify_polkit_async(
                         message,
-                        CAP_SYS_ADMIN,
                         "org.freedesktop.machine1.manage-machines",
                         details,
-                        false,
-                        UID_INVALID,
                         &m->manager->polkit_registry,
                         error);
         if (r < 0)
@@ -1070,11 +1046,8 @@ int bus_machine_method_open_root_directory(sd_bus_message *message, void *userda
 
         r = bus_verify_polkit_async(
                         message,
-                        CAP_SYS_ADMIN,
                         "org.freedesktop.machine1.manage-machines",
                         details,
-                        false,
-                        UID_INVALID,
                         &m->manager->polkit_registry,
                         error);
         if (r < 0)
index 44ff5c190bb479d2b4179bebc28bdb52f59e2959..af8a88f26caea134fd3a8f922092deeda7e7fb71 100644 (file)
@@ -32,6 +32,7 @@
 #include "string-table.h"
 #include "terminal-util.h"
 #include "tmpfile-util.h"
+#include "uid-range.h"
 #include "unit-name.h"
 #include "user-util.h"
 
@@ -658,7 +659,7 @@ int machine_get_uid_shift(Machine *m, uid_t *ret) {
         uid_t uid_base, uid_shift, uid_range;
         gid_t gid_base, gid_shift, gid_range;
         _cleanup_fclose_ FILE *f = NULL;
-        int k, r;
+        int r;
 
         assert(m);
         assert(ret);
@@ -690,14 +691,9 @@ int machine_get_uid_shift(Machine *m, uid_t *ret) {
         }
 
         /* Read the first line. There's at least one. */
-        errno = 0;
-        k = fscanf(f, UID_FMT " " UID_FMT " " UID_FMT "\n", &uid_base, &uid_shift, &uid_range);
-        if (k != 3) {
-                if (ferror(f))
-                        return errno_or_else(EIO);
-
-                return -EBADMSG;
-        }
+        r = uid_map_read_one(f, &uid_base, &uid_shift, &uid_range);
+        if (r < 0)
+                return r;
 
         /* Not a mapping starting at 0? Then it's a complex mapping we can't expose here. */
         if (uid_base != 0)
@@ -722,13 +718,12 @@ int machine_get_uid_shift(Machine *m, uid_t *ret) {
 
         /* Read the first line. There's at least one. */
         errno = 0;
-        k = fscanf(f, GID_FMT " " GID_FMT " " GID_FMT "\n", &gid_base, &gid_shift, &gid_range);
-        if (k != 3) {
-                if (ferror(f))
-                        return errno_or_else(EIO);
-
+        r = fscanf(f, GID_FMT " " GID_FMT " " GID_FMT "\n", &gid_base, &gid_shift, &gid_range);
+        if (r == EOF)
+                return errno_or_else(ENOMSG);
+        assert(r >= 0);
+        if (r != 3)
                 return -EBADMSG;
-        }
 
         /* If there's more than one line, then we don't support this file. */
         r = safe_fgetc(f, NULL);
@@ -757,6 +752,7 @@ static int machine_owns_uid_internal(
 
         _cleanup_fclose_ FILE *f = NULL;
         const char *p;
+        int r;
 
         /* This is a generic implementation for both uids and gids, under the assumptions they have the same types and semantics. */
         assert_cc(sizeof(uid_t) == sizeof(gid_t));
@@ -778,18 +774,12 @@ static int machine_owns_uid_internal(
 
         for (;;) {
                 uid_t uid_base, uid_shift, uid_range, converted;
-                int k;
 
-                errno = 0;
-                k = fscanf(f, UID_FMT " " UID_FMT " " UID_FMT, &uid_base, &uid_shift, &uid_range);
-                if (k < 0 && feof(f))
+                r = uid_map_read_one(f, &uid_base, &uid_shift, &uid_range);
+                if (r == -ENOMSG)
                         break;
-                if (k != 3) {
-                        if (ferror(f))
-                                return errno_or_else(EIO);
-
-                        return -EIO;
-                }
+                if (r < 0)
+                        return r;
 
                 /* The private user namespace is disabled, ignoring. */
                 if (uid_shift == 0)
@@ -831,6 +821,7 @@ static int machine_translate_uid_internal(
 
         _cleanup_fclose_ FILE *f = NULL;
         const char *p;
+        int r;
 
         /* This is a generic implementation for both uids and gids, under the assumptions they have the same types and semantics. */
         assert_cc(sizeof(uid_t) == sizeof(gid_t));
@@ -850,18 +841,12 @@ static int machine_translate_uid_internal(
 
         for (;;) {
                 uid_t uid_base, uid_shift, uid_range, converted;
-                int k;
 
-                errno = 0;
-                k = fscanf(f, UID_FMT " " UID_FMT " " UID_FMT, &uid_base, &uid_shift, &uid_range);
-                if (k < 0 && feof(f))
+                r = uid_map_read_one(f, &uid_base, &uid_shift, &uid_range);
+                if (r == -ENOMSG)
                         break;
-                if (k != 3) {
-                        if (ferror(f))
-                                return errno_or_else(EIO);
-
-                        return -EIO;
-                }
+                if (r < 0)
+                        return r;
 
                 if (uid < uid_base || uid >= uid_base + uid_range)
                         continue;
@@ -872,6 +857,7 @@ static int machine_translate_uid_internal(
 
                 if (ret_host_uid)
                         *ret_host_uid = converted;
+
                 return 0;
         }
 
index 9fec047385b509dcabaea02c5790f5ea902ad36c..6b108dc0094071e4cc06e8bc37dc7899905faa67 100644 (file)
@@ -720,11 +720,8 @@ static int method_clean_pool(sd_bus_message *message, void *userdata, sd_bus_err
 
         r = bus_verify_polkit_async(
                         message,
-                        CAP_SYS_ADMIN,
                         "org.freedesktop.machine1.manage-machines",
                         details,
-                        false,
-                        UID_INVALID,
                         &m->polkit_registry,
                         error);
         if (r < 0)
@@ -855,11 +852,8 @@ static int method_set_pool_limit(sd_bus_message *message, void *userdata, sd_bus
 
         r = bus_verify_polkit_async(
                         message,
-                        CAP_SYS_ADMIN,
                         "org.freedesktop.machine1.manage-machines",
                         details,
-                        false,
-                        UID_INVALID,
                         &m->polkit_registry,
                         error);
         if (r < 0)
index f626f07af63ad662a831aa9d4ee4f746f8fda3ee..2c276ef22a4f2b54e7aaa3d5909dc9053781b936 100644 (file)
@@ -665,7 +665,7 @@ static int start_transient_mount(
                 if (r < 0)
                         return bus_log_parse_error(r);
 
-                r = bus_wait_for_jobs_one(w, object, arg_quiet, NULL);
+                r = bus_wait_for_jobs_one(w, object, arg_quiet ? 0 : BUS_WAIT_JOBS_LOG_ERROR, NULL);
                 if (r < 0)
                         return r;
         }
@@ -774,7 +774,7 @@ static int start_transient_automount(
                 if (r < 0)
                         return bus_log_parse_error(r);
 
-                r = bus_wait_for_jobs_one(w, object, arg_quiet, NULL);
+                r = bus_wait_for_jobs_one(w, object, arg_quiet ? 0 : BUS_WAIT_JOBS_LOG_ERROR, NULL);
                 if (r < 0)
                         return r;
         }
@@ -936,7 +936,7 @@ static int stop_mount(
                 if (r < 0)
                         return bus_log_parse_error(r);
 
-                r = bus_wait_for_jobs_one(w, object, arg_quiet, NULL);
+                r = bus_wait_for_jobs_one(w, object, arg_quiet ? 0 : BUS_WAIT_JOBS_LOG_ERROR, NULL);
                 if (r < 0)
                         return r;
         }
index 5c05eba095531dc8ba7bcaff653223ae61e2b87c..3d692abf443da67c8e68f345060c62a00f6406c1 100644 (file)
@@ -109,7 +109,10 @@ systemd_networkd_wait_online_sources = files(
         'wait-online/wait-online.c',
 )
 
-networkctl_sources = files('networkctl.c')
+networkctl_sources = files(
+        'networkctl.c',
+        'networkctl-config-file.c'
+)
 
 network_generator_sources = files(
         'generator/main.c',
diff --git a/src/network/networkctl-config-file.c b/src/network/networkctl-config-file.c
new file mode 100644 (file)
index 0000000..670f1c2
--- /dev/null
@@ -0,0 +1,628 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <unistd.h>
+
+#include "sd-daemon.h"
+#include "sd-device.h"
+#include "sd-netlink.h"
+#include "sd-network.h"
+
+#include "bus-error.h"
+#include "bus-locator.h"
+#include "bus-util.h"
+#include "bus-wait-for-jobs.h"
+#include "conf-files.h"
+#include "edit-util.h"
+#include "mkdir-label.h"
+#include "netlink-util.h"
+#include "networkctl.h"
+#include "networkctl-config-file.h"
+#include "pager.h"
+#include "path-lookup.h"
+#include "path-util.h"
+#include "pretty-print.h"
+#include "selinux-util.h"
+#include "strv.h"
+#include "virt.h"
+
+typedef enum ReloadFlags {
+        RELOAD_NETWORKD = 1 << 0,
+        RELOAD_UDEVD    = 1 << 1,
+} ReloadFlags;
+
+static int get_config_files_by_name(
+                const char *name,
+                bool allow_masked,
+                char **ret_path,
+                char ***ret_dropins) {
+
+        _cleanup_free_ char *path = NULL;
+        int r;
+
+        assert(name);
+        assert(ret_path);
+
+        STRV_FOREACH(i, NETWORK_DIRS) {
+                _cleanup_free_ char *p = NULL;
+
+                p = path_join(*i, name);
+                if (!p)
+                        return -ENOMEM;
+
+                r = RET_NERRNO(access(p, F_OK));
+                if (r >= 0) {
+                        if (!allow_masked) {
+                                r = null_or_empty_path(p);
+                                if (r < 0)
+                                        return log_debug_errno(r,
+                                                               "Failed to check if network config '%s' is masked: %m",
+                                                               name);
+                                if (r > 0)
+                                        return -ERFKILL;
+                        }
+
+                        path = TAKE_PTR(p);
+                        break;
+                }
+
+                if (r != -ENOENT)
+                        log_debug_errno(r, "Failed to determine whether '%s' exists, ignoring: %m", p);
+        }
+
+        if (!path)
+                return -ENOENT;
+
+        if (ret_dropins) {
+                _cleanup_free_ char *dropin_dirname = NULL;
+
+                dropin_dirname = strjoin(name, ".d");
+                if (!dropin_dirname)
+                        return -ENOMEM;
+
+                r = conf_files_list_dropins(ret_dropins, dropin_dirname, /* root = */ NULL, NETWORK_DIRS);
+                if (r < 0)
+                        return r;
+        }
+
+        *ret_path = TAKE_PTR(path);
+
+        return 0;
+}
+
+static int get_dropin_by_name(
+                const char *name,
+                char * const *dropins,
+                char **ret) {
+
+        assert(name);
+        assert(ret);
+
+        STRV_FOREACH(i, dropins)
+                if (path_equal_filename(*i, name)) {
+                        _cleanup_free_ char *d = NULL;
+
+                        d = strdup(*i);
+                        if (!d)
+                                return -ENOMEM;
+
+                        *ret = TAKE_PTR(d);
+                        return 1;
+                }
+
+        *ret = NULL;
+        return 0;
+}
+
+static int get_network_files_by_link(
+                sd_netlink **rtnl,
+                const char *link,
+                char **ret_path,
+                char ***ret_dropins) {
+
+        _cleanup_strv_free_ char **dropins = NULL;
+        _cleanup_free_ char *path = NULL;
+        int r, ifindex;
+
+        assert(rtnl);
+        assert(link);
+        assert(ret_path);
+        assert(ret_dropins);
+
+        ifindex = rtnl_resolve_interface_or_warn(rtnl, link);
+        if (ifindex < 0)
+                return ifindex;
+
+        r = sd_network_link_get_network_file(ifindex, &path);
+        if (r == -ENODATA)
+                return log_error_errno(SYNTHETIC_ERRNO(ENOENT),
+                                       "Link '%s' has no associated network file.", link);
+        if (r < 0)
+                return log_error_errno(r, "Failed to get network file for link '%s': %m", link);
+
+        r = sd_network_link_get_network_file_dropins(ifindex, &dropins);
+        if (r < 0 && r != -ENODATA)
+                return log_error_errno(r, "Failed to get network drop-ins for link '%s': %m", link);
+
+        *ret_path = TAKE_PTR(path);
+        *ret_dropins = TAKE_PTR(dropins);
+
+        return 0;
+}
+
+static int get_link_files_by_link(const char *link, char **ret_path, char ***ret_dropins) {
+        _cleanup_(sd_device_unrefp) sd_device *device = NULL;
+        _cleanup_strv_free_ char **dropins_split = NULL;
+        _cleanup_free_ char *p = NULL;
+        const char *path, *dropins;
+        int r;
+
+        assert(link);
+        assert(ret_path);
+        assert(ret_dropins);
+
+        r = sd_device_new_from_ifname(&device, link);
+        if (r < 0)
+                return log_error_errno(r, "Failed to create sd-device object for link '%s': %m", link);
+
+        r = sd_device_get_property_value(device, "ID_NET_LINK_FILE", &path);
+        if (r == -ENOENT)
+                return log_error_errno(r, "Link '%s' has no associated link file.", link);
+        if (r < 0)
+                return log_error_errno(r, "Failed to get link file for link '%s': %m", link);
+
+        r = sd_device_get_property_value(device, "ID_NET_LINK_FILE_DROPINS", &dropins);
+        if (r < 0 && r != -ENOENT)
+                return log_error_errno(r, "Failed to get link drop-ins for link '%s': %m", link);
+        if (r >= 0) {
+                r = strv_split_full(&dropins_split, dropins, ":", EXTRACT_CUNESCAPE);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to parse link drop-ins for link '%s': %m", link);
+        }
+
+        p = strdup(path);
+        if (!p)
+                return log_oom();
+
+        *ret_path = TAKE_PTR(p);
+        *ret_dropins = TAKE_PTR(dropins_split);
+
+        return 0;
+}
+
+static int get_config_files_by_link_config(
+                const char *link_config,
+                sd_netlink **rtnl,
+                char **ret_path,
+                char ***ret_dropins,
+                ReloadFlags *ret_reload) {
+
+        _cleanup_strv_free_ char **dropins = NULL, **link_config_split = NULL;
+        _cleanup_free_ char *path = NULL;
+        const char *ifname, *type;
+        ReloadFlags reload;
+        size_t n;
+        int r;
+
+        assert(link_config);
+        assert(rtnl);
+        assert(ret_path);
+        assert(ret_dropins);
+
+        link_config_split = strv_split(link_config, ":");
+        if (!link_config_split)
+                return log_oom();
+
+        n = strv_length(link_config_split);
+        if (n == 0 || isempty(link_config_split[0]))
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "No link name is given.");
+        if (n > 2)
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid link config '%s'.", link_config);
+
+        ifname = link_config_split[0];
+        type = n == 2 ? link_config_split[1] : "network";
+
+        if (streq(type, "network")) {
+                if (!networkd_is_running())
+                        return log_error_errno(SYNTHETIC_ERRNO(ESRCH),
+                                               "Cannot get network file for link if systemd-networkd is not running.");
+
+                r = get_network_files_by_link(rtnl, ifname, &path, &dropins);
+                if (r < 0)
+                        return r;
+
+                reload = RELOAD_NETWORKD;
+        } else if (streq(type, "link")) {
+                r = get_link_files_by_link(ifname, &path, &dropins);
+                if (r < 0)
+                        return r;
+
+                reload = RELOAD_UDEVD;
+        } else
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+                                       "Invalid config type '%s' for link '%s'.", type, ifname);
+
+        *ret_path = TAKE_PTR(path);
+        *ret_dropins = TAKE_PTR(dropins);
+
+        if (ret_reload)
+                *ret_reload = reload;
+
+        return 0;
+}
+
+static int add_config_to_edit(
+                EditFileContext *context,
+                const char *path,
+                char * const *dropins) {
+
+        _cleanup_free_ char *new_path = NULL, *dropin_path = NULL, *old_dropin = NULL;
+        _cleanup_strv_free_ char **comment_paths = NULL;
+        int r;
+
+        assert(context);
+        assert(path);
+
+        /* If we're supposed to edit main config file in /run/, but a config with the same name is present
+         * under /etc/, we bail out since the one in /etc/ always overrides that in /run/. */
+        if (arg_runtime && !arg_drop_in && path_startswith(path, "/etc"))
+                return log_error_errno(SYNTHETIC_ERRNO(EEXIST),
+                                       "Cannot edit runtime config file: overridden by %s", path);
+
+        if (path_startswith(path, "/usr") || arg_runtime != !!path_startswith(path, "/run")) {
+                _cleanup_free_ char *name = NULL;
+
+                r = path_extract_filename(path, &name);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to extract filename from '%s': %m", path);
+
+                new_path = path_join(NETWORK_DIRS[arg_runtime ? 1 : 0], name);
+                if (!new_path)
+                        return log_oom();
+        }
+
+        if (!arg_drop_in)
+                return edit_files_add(context, new_path ?: path, path, NULL);
+
+        bool need_new_dropin;
+
+        r = get_dropin_by_name(arg_drop_in, dropins, &old_dropin);
+        if (r < 0)
+                return log_error_errno(r, "Failed to acquire drop-in '%s': %m", arg_drop_in);
+        if (r > 0) {
+                /* See the explanation above */
+                if (arg_runtime && path_startswith(old_dropin, "/etc"))
+                        return log_error_errno(SYNTHETIC_ERRNO(EEXIST),
+                                               "Cannot edit runtime config file: overridden by %s", old_dropin);
+
+                need_new_dropin = path_startswith(old_dropin, "/usr") || arg_runtime != !!path_startswith(old_dropin, "/run");
+        } else
+                need_new_dropin = true;
+
+        if (!need_new_dropin)
+                /* An existing drop-in is found in the correct scope. Let's edit it directly. */
+                dropin_path = TAKE_PTR(old_dropin);
+        else {
+                /* No drop-in was found or an existing drop-in is in a different scope. Let's create a new
+                 * drop-in file. */
+                dropin_path = strjoin(new_path ?: path, ".d/", arg_drop_in);
+                if (!dropin_path)
+                        return log_oom();
+        }
+
+        comment_paths = strv_new(path);
+        if (!comment_paths)
+                return log_oom();
+
+        r = strv_extend_strv(&comment_paths, dropins, /* filter_duplicates = */ false);
+        if (r < 0)
+                return log_oom();
+
+        return edit_files_add(context, dropin_path, old_dropin, comment_paths);
+}
+
+static int udevd_reload(sd_bus *bus) {
+        _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
+        _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+        _cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *w = NULL;
+        const char *job_path;
+        int r;
+
+        assert(bus);
+
+        r = bus_wait_for_jobs_new(bus, &w);
+        if (r < 0)
+                return log_error_errno(r, "Could not watch jobs: %m");
+
+        r = bus_call_method(bus,
+                            bus_systemd_mgr,
+                            "ReloadUnit",
+                            &error,
+                            &reply,
+                            "ss",
+                            "systemd-udevd.service",
+                            "replace");
+        if (r < 0)
+                return log_error_errno(r, "Failed to reload systemd-udevd: %s", bus_error_message(&error, r));
+
+        r = sd_bus_message_read(reply, "o", &job_path);
+        if (r < 0)
+                return bus_log_parse_error(r);
+
+        r = bus_wait_for_jobs_one(w, job_path, /* flags = */ 0, NULL);
+        if (r == -ENOEXEC) {
+                log_debug("systemd-udevd is not running, skipping reload.");
+                return 0;
+        }
+        if (r < 0)
+                return log_error_errno(r, "Failed to reload systemd-udevd: %m");
+
+        return 1;
+}
+
+static int reload_daemons(ReloadFlags flags) {
+        _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
+        int r, ret = 1;
+
+        if (arg_no_reload)
+                return 0;
+
+        if (flags == 0)
+                return 0;
+
+        if (!sd_booted() || running_in_chroot() > 0) {
+                log_debug("System is not booted with systemd or is running in chroot, skipping reload.");
+                return 0;
+        }
+
+        r = sd_bus_open_system(&bus);
+        if (r < 0)
+                return log_error_errno(r, "Failed to connect to system bus: %m");
+
+        if (FLAGS_SET(flags, RELOAD_UDEVD))
+                RET_GATHER(ret, udevd_reload(bus));
+
+        if (FLAGS_SET(flags, RELOAD_NETWORKD)) {
+                if (networkd_is_running()) {
+                        _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+
+                        r = bus_call_method(bus, bus_network_mgr, "Reload", &error, NULL, NULL);
+                        if (r < 0)
+                                RET_GATHER(ret, log_error_errno(r, "Failed to reload systemd-networkd: %s", bus_error_message(&error, r)));
+                } else
+                        log_debug("systemd-networkd is not running, skipping reload.");
+        }
+
+        return ret;
+}
+
+int verb_edit(int argc, char *argv[], void *userdata) {
+        _cleanup_(edit_file_context_done) EditFileContext context = {
+                .marker_start = DROPIN_MARKER_START,
+                .marker_end = DROPIN_MARKER_END,
+                .remove_parent = !!arg_drop_in,
+        };
+        _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
+        ReloadFlags reload = 0;
+        int r;
+
+        if (!on_tty())
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Cannot edit network config files if not on a tty.");
+
+        r = mac_selinux_init();
+        if (r < 0)
+                return r;
+
+        STRV_FOREACH(name, strv_skip(argv, 1)) {
+                _cleanup_strv_free_ char **dropins = NULL;
+                _cleanup_free_ char *path = NULL;
+                const char *link_config;
+
+                link_config = startswith(*name, "@");
+                if (link_config) {
+                        ReloadFlags flags;
+
+                        r = get_config_files_by_link_config(link_config, &rtnl, &path, &dropins, &flags);
+                        if (r < 0)
+                                return r;
+
+                        reload |= flags;
+
+                        r = add_config_to_edit(&context, path, dropins);
+                        if (r < 0)
+                                return r;
+
+                        continue;
+                }
+
+                if (ENDSWITH_SET(*name, ".network", ".netdev"))
+                        reload |= RELOAD_NETWORKD;
+                else if (endswith(*name, ".link"))
+                        reload |= RELOAD_UDEVD;
+                else
+                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid network config name '%s'.", *name);
+
+                r = get_config_files_by_name(*name, /* allow_masked = */ false, &path, &dropins);
+                if (r == -ERFKILL)
+                        return log_error_errno(r, "Network config '%s' is masked.", *name);
+                if (r == -ENOENT) {
+                        if (arg_drop_in)
+                                return log_error_errno(r, "Cannot find network config '%s'.", *name);
+
+                        log_debug("No existing network config '%s' found, creating a new file.", *name);
+
+                        path = path_join(NETWORK_DIRS[arg_runtime ? 1 : 0], *name);
+                        if (!path)
+                                return log_oom();
+
+                        r = edit_files_add(&context, path, NULL, NULL);
+                        if (r < 0)
+                                return r;
+                        continue;
+                }
+                if (r < 0)
+                        return log_error_errno(r, "Failed to get the path of network config '%s': %m", *name);
+
+                r = add_config_to_edit(&context, path, dropins);
+                if (r < 0)
+                        return r;
+        }
+
+        r = do_edit_files_and_install(&context);
+        if (r < 0)
+                return r;
+
+        return reload_daemons(reload);
+}
+
+int verb_cat(int argc, char *argv[], void *userdata) {
+        _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
+        int r, ret = 0;
+
+        pager_open(arg_pager_flags);
+
+        bool first = true;
+        STRV_FOREACH(name, strv_skip(argv, 1)) {
+                _cleanup_strv_free_ char **dropins = NULL;
+                _cleanup_free_ char *path = NULL;
+                const char *link_config;
+
+                link_config = startswith(*name, "@");
+                if (link_config) {
+                        r = get_config_files_by_link_config(link_config, &rtnl, &path, &dropins, /* ret_reload = */ NULL);
+                        if (r < 0)
+                                return RET_GATHER(ret, r);
+                } else {
+                        r = get_config_files_by_name(*name, /* allow_masked = */ false, &path, &dropins);
+                        if (r == -ENOENT) {
+                                RET_GATHER(ret, log_error_errno(r, "Cannot find network config file '%s'.", *name));
+                                continue;
+                        }
+                        if (r == -ERFKILL) {
+                                RET_GATHER(ret, log_debug_errno(r, "Network config '%s' is masked, ignoring.", *name));
+                                continue;
+                        }
+                        if (r < 0) {
+                                log_error_errno(r, "Failed to get the path of network config '%s': %m", *name);
+                                return RET_GATHER(ret, r);
+                        }
+                }
+
+                if (!first)
+                        putchar('\n');
+
+                r = cat_files(path, dropins, /* flags = */ CAT_FORMAT_HAS_SECTIONS);
+                if (r < 0)
+                        return RET_GATHER(ret, r);
+
+                first = false;
+        }
+
+        return ret;
+}
+
+int verb_mask(int argc, char *argv[], void *userdata) {
+        ReloadFlags flags = 0;
+        int r;
+
+        r = mac_selinux_init();
+        if (r < 0)
+                return r;
+
+        STRV_FOREACH(name, strv_skip(argv, 1)) {
+                _cleanup_free_ char *config_path = NULL, *symlink_path = NULL;
+                ReloadFlags reload;
+
+                /* We update the real 'flags' at last, since the operation can be skipped. */
+                if (ENDSWITH_SET(*name, ".network", ".netdev"))
+                        reload = RELOAD_NETWORKD;
+                else if (endswith(*name, ".link"))
+                        reload = RELOAD_UDEVD;
+                else
+                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid network config name '%s'.", *name);
+
+                r = get_config_files_by_name(*name, /* allow_masked = */ true, &config_path, /* ret_dropins = */ NULL);
+                if (r == -ENOENT)
+                        log_warning("No existing network config '%s' found, proceeding anyway.", *name);
+                else if (r < 0)
+                        return log_error_errno(r, "Failed to get the path of network config '%s': %m", *name);
+                else if (!path_startswith(config_path, "/usr")) {
+                        r = null_or_empty_path(config_path);
+                        if (r < 0)
+                                return log_error_errno(r,
+                                                       "Failed to check if '%s' is masked: %m", config_path);
+                        if (r > 0) {
+                                log_debug("%s is already masked, skipping.", config_path);
+                                continue;
+                        }
+
+                        /* At this point, we have found a config under mutable dir (/run/ or /etc/),
+                         * so masking through /run/ (--runtime) is not possible. If it's under /etc/,
+                         * then it doesn't work without --runtime either. */
+                        if (arg_runtime || path_startswith(config_path, "/etc"))
+                                return log_error_errno(SYNTHETIC_ERRNO(EEXIST),
+                                                       "Cannot mask network config %s: %s exists",
+                                                       *name, config_path);
+                }
+
+                symlink_path = path_join(NETWORK_DIRS[arg_runtime ? 1 : 0], *name);
+                if (!symlink_path)
+                        return log_oom();
+
+                (void) mkdir_parents_label(symlink_path, 0755);
+
+                if (symlink("/dev/null", symlink_path) < 0)
+                        return log_error_errno(errno,
+                                               "Failed to create symlink '%s' to /dev/null: %m", symlink_path);
+
+                flags |= reload;
+                log_info("Successfully created symlink '%s' to /dev/null.", symlink_path);
+        }
+
+        return reload_daemons(flags);
+}
+
+int verb_unmask(int argc, char *argv[], void *userdata) {
+        ReloadFlags flags = 0;
+        int r;
+
+        STRV_FOREACH(name, strv_skip(argv, 1)) {
+                _cleanup_free_ char *path = NULL;
+                ReloadFlags reload;
+
+                if (ENDSWITH_SET(*name, ".network", ".netdev"))
+                        reload = RELOAD_NETWORKD;
+                else if (endswith(*name, ".link"))
+                        reload = RELOAD_UDEVD;
+                else
+                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid network config name '%s'.", *name);
+
+                r = get_config_files_by_name(*name, /* allow_masked = */ true, &path, /* ret_dropins = */ NULL);
+                if (r == -ENOENT) {
+                        log_debug_errno(r, "Network configuration '%s' doesn't exist, skipping.", *name);
+                        continue;
+                }
+                if (r < 0)
+                        return log_error_errno(r, "Failed to get the path of network config '%s': %m", *name);
+
+                r = null_or_empty_path(path);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to check if '%s' is masked: %m", path);
+                if (r == 0)
+                        continue;
+
+                if (path_startswith(path, "/usr"))
+                        return log_error_errno(r, "Cannot unmask network config under /usr/: %s", path);
+
+                if (unlink(path) < 0) {
+                        if (errno == ENOENT)
+                                continue;
+
+                        return log_error_errno(errno, "Failed to remove '%s': %m", path);
+                }
+
+                flags |= reload;
+                log_info("Successfully removed masked network config '%s'.", path);
+        }
+
+        return reload_daemons(flags);
+}
diff --git a/src/network/networkctl-config-file.h b/src/network/networkctl-config-file.h
new file mode 100644 (file)
index 0000000..38210a8
--- /dev/null
@@ -0,0 +1,8 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+int verb_edit(int argc, char *argv[], void *userdata);
+int verb_cat(int argc, char *argv[], void *userdata);
+
+int verb_mask(int argc, char *argv[], void *userdata);
+int verb_unmask(int argc, char *argv[], void *userdata);
index f8ee3931720106e4402660388b6a6a5931ef6560..cf9d17c8b2fe98c37a760fd3b623125f195126c9 100644 (file)
 #include "bus-common-errors.h"
 #include "bus-error.h"
 #include "bus-locator.h"
-#include "bus-wait-for-jobs.h"
-#include "conf-files.h"
 #include "device-util.h"
-#include "edit-util.h"
 #include "escape.h"
 #include "ether-addr-util.h"
 #include "ethtool-util.h"
@@ -51,6 +48,8 @@
 #include "netlink-util.h"
 #include "network-internal.h"
 #include "network-util.h"
+#include "networkctl.h"
+#include "networkctl-config-file.h"
 #include "pager.h"
 #include "parse-argument.h"
 #include "parse-util.h"
@@ -72,7 +71,6 @@
 #include "udev-util.h"
 #include "unit-def.h"
 #include "verbs.h"
-#include "virt.h"
 #include "wifi-util.h"
 
 /* Kernel defines MODULE_NAME_LEN as 64 - sizeof(unsigned long). So, 64 is enough. */
 /* use 128 kB for receive socket kernel queue, we shouldn't need more here */
 #define RCVBUF_SIZE    (128*1024)
 
-static PagerFlags arg_pager_flags = 0;
-static bool arg_legend = true;
-static bool arg_no_reload = false;
-static bool arg_all = false;
-static bool arg_stats = false;
-static bool arg_full = false;
-static bool arg_runtime = false;
-static unsigned arg_lines = 10;
-static char *arg_drop_in = NULL;
-static JsonFormatFlags arg_json_format_flags = JSON_FORMAT_OFF;
+PagerFlags arg_pager_flags = 0;
+bool arg_legend = true;
+bool arg_no_reload = false;
+bool arg_all = false;
+bool arg_stats = false;
+bool arg_full = false;
+bool arg_runtime = false;
+unsigned arg_lines = 10;
+char *arg_drop_in = NULL;
+JsonFormatFlags arg_json_format_flags = JSON_FORMAT_OFF;
 
 STATIC_DESTRUCTOR_REGISTER(arg_drop_in, freep);
 
@@ -122,7 +120,7 @@ static int check_netns_match(sd_bus *bus) {
         return 0;
 }
 
-static bool networkd_is_running(void) {
+bool networkd_is_running(void) {
         static int cached = -1;
         int r;
 
@@ -141,7 +139,7 @@ static bool networkd_is_running(void) {
         return cached;
 }
 
-static int acquire_bus(sd_bus **ret) {
+int acquire_bus(sd_bus **ret) {
         _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
         int r;
 
@@ -2855,471 +2853,6 @@ static int verb_reconfigure(int argc, char *argv[], void *userdata) {
         return 0;
 }
 
-typedef enum ReloadFlags {
-        RELOAD_NETWORKD = 1 << 0,
-        RELOAD_UDEVD    = 1 << 1,
-} ReloadFlags;
-
-static int get_config_files_by_name(const char *name, char **ret_path, char ***ret_dropins) {
-        _cleanup_free_ char *path = NULL;
-        int r;
-
-        assert(name);
-        assert(ret_path);
-
-        STRV_FOREACH(i, NETWORK_DIRS) {
-                _cleanup_free_ char *p = NULL;
-
-                p = path_join(*i, name);
-                if (!p)
-                        return -ENOMEM;
-
-                r = RET_NERRNO(access(p, F_OK));
-                if (r >= 0) {
-                        path = TAKE_PTR(p);
-                        break;
-                }
-
-                if (r != -ENOENT)
-                        log_debug_errno(r, "Failed to determine whether '%s' exists, ignoring: %m", p);
-        }
-
-        if (!path)
-                return -ENOENT;
-
-        if (ret_dropins) {
-                _cleanup_free_ char *dropin_dirname = NULL;
-
-                dropin_dirname = strjoin(name, ".d");
-                if (!dropin_dirname)
-                        return -ENOMEM;
-
-                r = conf_files_list_dropins(ret_dropins, dropin_dirname, /* root = */ NULL, NETWORK_DIRS);
-                if (r < 0)
-                        return r;
-        }
-
-        *ret_path = TAKE_PTR(path);
-
-        return 0;
-}
-
-static int get_dropin_by_name(
-                const char *name,
-                char * const *dropins,
-                char **ret) {
-
-        assert(name);
-        assert(ret);
-
-        STRV_FOREACH(i, dropins)
-                if (path_equal_filename(*i, name)) {
-                        _cleanup_free_ char *d = NULL;
-
-                        d = strdup(*i);
-                        if (!d)
-                                return -ENOMEM;
-
-                        *ret = TAKE_PTR(d);
-                        return 1;
-                }
-
-        *ret = NULL;
-        return 0;
-}
-
-static int get_network_files_by_link(
-                sd_netlink **rtnl,
-                const char *link,
-                char **ret_path,
-                char ***ret_dropins) {
-
-        _cleanup_strv_free_ char **dropins = NULL;
-        _cleanup_free_ char *path = NULL;
-        int r, ifindex;
-
-        assert(rtnl);
-        assert(link);
-        assert(ret_path);
-        assert(ret_dropins);
-
-        ifindex = rtnl_resolve_interface_or_warn(rtnl, link);
-        if (ifindex < 0)
-                return ifindex;
-
-        r = sd_network_link_get_network_file(ifindex, &path);
-        if (r == -ENODATA)
-                return log_error_errno(SYNTHETIC_ERRNO(ENOENT),
-                                       "Link '%s' has no associated network file.", link);
-        if (r < 0)
-                return log_error_errno(r, "Failed to get network file for link '%s': %m", link);
-
-        r = sd_network_link_get_network_file_dropins(ifindex, &dropins);
-        if (r < 0 && r != -ENODATA)
-                return log_error_errno(r, "Failed to get network drop-ins for link '%s': %m", link);
-
-        *ret_path = TAKE_PTR(path);
-        *ret_dropins = TAKE_PTR(dropins);
-
-        return 0;
-}
-
-static int get_link_files_by_link(const char *link, char **ret_path, char ***ret_dropins) {
-        _cleanup_(sd_device_unrefp) sd_device *device = NULL;
-        _cleanup_strv_free_ char **dropins_split = NULL;
-        _cleanup_free_ char *p = NULL;
-        const char *path, *dropins;
-        int r;
-
-        assert(link);
-        assert(ret_path);
-        assert(ret_dropins);
-
-        r = sd_device_new_from_ifname(&device, link);
-        if (r < 0)
-                return log_error_errno(r, "Failed to create sd-device object for link '%s': %m", link);
-
-        r = sd_device_get_property_value(device, "ID_NET_LINK_FILE", &path);
-        if (r == -ENOENT)
-                return log_error_errno(r, "Link '%s' has no associated link file.", link);
-        if (r < 0)
-                return log_error_errno(r, "Failed to get link file for link '%s': %m", link);
-
-        r = sd_device_get_property_value(device, "ID_NET_LINK_FILE_DROPINS", &dropins);
-        if (r < 0 && r != -ENOENT)
-                return log_error_errno(r, "Failed to get link drop-ins for link '%s': %m", link);
-        if (r >= 0) {
-                r = strv_split_full(&dropins_split, dropins, ":", EXTRACT_CUNESCAPE);
-                if (r < 0)
-                        return log_error_errno(r, "Failed to parse link drop-ins for link '%s': %m", link);
-        }
-
-        p = strdup(path);
-        if (!p)
-                return log_oom();
-
-        *ret_path = TAKE_PTR(p);
-        *ret_dropins = TAKE_PTR(dropins_split);
-
-        return 0;
-}
-
-static int get_config_files_by_link_config(
-                const char *link_config,
-                sd_netlink **rtnl,
-                char **ret_path,
-                char ***ret_dropins,
-                ReloadFlags *ret_reload) {
-
-        _cleanup_strv_free_ char **dropins = NULL, **link_config_split = NULL;
-        _cleanup_free_ char *path = NULL;
-        const char *ifname, *type;
-        ReloadFlags reload;
-        size_t n;
-        int r;
-
-        assert(link_config);
-        assert(rtnl);
-        assert(ret_path);
-        assert(ret_dropins);
-
-        link_config_split = strv_split(link_config, ":");
-        if (!link_config_split)
-                return log_oom();
-
-        n = strv_length(link_config_split);
-        if (n == 0 || isempty(link_config_split[0]))
-                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "No link name is given.");
-        if (n > 2)
-                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid link config '%s'.", link_config);
-
-        ifname = link_config_split[0];
-        type = n == 2 ? link_config_split[1] : "network";
-
-        if (streq(type, "network")) {
-                if (!networkd_is_running())
-                        return log_error_errno(SYNTHETIC_ERRNO(ESRCH),
-                                               "Cannot get network file for link if systemd-networkd is not running.");
-
-                r = get_network_files_by_link(rtnl, ifname, &path, &dropins);
-                if (r < 0)
-                        return r;
-
-                reload = RELOAD_NETWORKD;
-        } else if (streq(type, "link")) {
-                r = get_link_files_by_link(ifname, &path, &dropins);
-                if (r < 0)
-                        return r;
-
-                reload = RELOAD_UDEVD;
-        } else
-                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
-                                       "Invalid config type '%s' for link '%s'.", type, ifname);
-
-        *ret_path = TAKE_PTR(path);
-        *ret_dropins = TAKE_PTR(dropins);
-
-        if (ret_reload)
-                *ret_reload = reload;
-
-        return 0;
-}
-
-static int add_config_to_edit(
-                EditFileContext *context,
-                const char *path,
-                char * const *dropins) {
-
-        _cleanup_free_ char *new_path = NULL, *dropin_path = NULL, *old_dropin = NULL;
-        _cleanup_strv_free_ char **comment_paths = NULL;
-        int r;
-
-        assert(context);
-        assert(path);
-
-        /* If we're supposed to edit main config file in /run/, but a config with the same name is present
-         * under /etc/, we bail out since the one in /etc/ always overrides that in /run/. */
-        if (arg_runtime && !arg_drop_in && path_startswith(path, "/etc"))
-                return log_error_errno(SYNTHETIC_ERRNO(EEXIST),
-                                       "Cannot edit runtime config file: overridden by %s", path);
-
-        if (path_startswith(path, "/usr") || arg_runtime != !!path_startswith(path, "/run")) {
-                _cleanup_free_ char *name = NULL;
-
-                r = path_extract_filename(path, &name);
-                if (r < 0)
-                        return log_error_errno(r, "Failed to extract filename from '%s': %m", path);
-
-                new_path = path_join(NETWORK_DIRS[arg_runtime ? 1 : 0], name);
-                if (!new_path)
-                        return log_oom();
-        }
-
-        if (!arg_drop_in)
-                return edit_files_add(context, new_path ?: path, path, NULL);
-
-        bool need_new_dropin;
-
-        r = get_dropin_by_name(arg_drop_in, dropins, &old_dropin);
-        if (r < 0)
-                return log_error_errno(r, "Failed to acquire drop-in '%s': %m", arg_drop_in);
-        if (r > 0) {
-                /* See the explanation above */
-                if (arg_runtime && path_startswith(old_dropin, "/etc"))
-                        return log_error_errno(SYNTHETIC_ERRNO(EEXIST),
-                                               "Cannot edit runtime config file: overridden by %s", old_dropin);
-
-                need_new_dropin = path_startswith(old_dropin, "/usr") || arg_runtime != !!path_startswith(old_dropin, "/run");
-        } else
-                need_new_dropin = true;
-
-        if (!need_new_dropin)
-                /* An existing drop-in is found in the correct scope. Let's edit it directly. */
-                dropin_path = TAKE_PTR(old_dropin);
-        else {
-                /* No drop-in was found or an existing drop-in is in a different scope. Let's create a new
-                 * drop-in file. */
-                dropin_path = strjoin(new_path ?: path, ".d/", arg_drop_in);
-                if (!dropin_path)
-                        return log_oom();
-        }
-
-        comment_paths = strv_new(path);
-        if (!comment_paths)
-                return log_oom();
-
-        r = strv_extend_strv(&comment_paths, dropins, /* filter_duplicates = */ false);
-        if (r < 0)
-                return log_oom();
-
-        return edit_files_add(context, dropin_path, old_dropin, comment_paths);
-}
-
-static int udevd_reload(sd_bus *bus) {
-        _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
-        _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
-        _cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *w = NULL;
-        const char *job_path;
-        int r;
-
-        assert(bus);
-
-        r = bus_wait_for_jobs_new(bus, &w);
-        if (r < 0)
-                return log_error_errno(r, "Could not watch jobs: %m");
-
-        r = bus_call_method(bus,
-                            bus_systemd_mgr,
-                            "ReloadUnit",
-                            &error,
-                            &reply,
-                            "ss",
-                            "systemd-udevd.service",
-                            "replace");
-        if (r < 0)
-                return log_error_errno(r, "Failed to reload systemd-udevd: %s", bus_error_message(&error, r));
-
-        r = sd_bus_message_read(reply, "o", &job_path);
-        if (r < 0)
-                return bus_log_parse_error(r);
-
-        r = bus_wait_for_jobs_one(w, job_path, /* quiet = */ true, NULL);
-        if (r == -ENOEXEC) {
-                log_debug("systemd-udevd is not running, skipping reload.");
-                return 0;
-        }
-        if (r < 0)
-                return log_error_errno(r, "Failed to reload systemd-udevd: %m");
-
-        return 1;
-}
-
-static int verb_edit(int argc, char *argv[], void *userdata) {
-        _cleanup_(edit_file_context_done) EditFileContext context = {
-                .marker_start = DROPIN_MARKER_START,
-                .marker_end = DROPIN_MARKER_END,
-                .remove_parent = !!arg_drop_in,
-        };
-        _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
-        ReloadFlags reload = 0;
-        int r;
-
-        if (!on_tty())
-                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Cannot edit network config files if not on a tty.");
-
-        r = mac_selinux_init();
-        if (r < 0)
-                return r;
-
-        STRV_FOREACH(name, strv_skip(argv, 1)) {
-                _cleanup_strv_free_ char **dropins = NULL;
-                _cleanup_free_ char *path = NULL;
-                const char *link_config;
-
-                link_config = startswith(*name, "@");
-                if (link_config) {
-                        ReloadFlags flags;
-
-                        r = get_config_files_by_link_config(link_config, &rtnl, &path, &dropins, &flags);
-                        if (r < 0)
-                                return r;
-
-                        reload |= flags;
-
-                        r = add_config_to_edit(&context, path, dropins);
-                        if (r < 0)
-                                return r;
-
-                        continue;
-                }
-
-                if (ENDSWITH_SET(*name, ".network", ".netdev"))
-                        reload |= RELOAD_NETWORKD;
-                else if (endswith(*name, ".link"))
-                        reload |= RELOAD_UDEVD;
-                else
-                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid network config name '%s'.", *name);
-
-                r = get_config_files_by_name(*name, &path, &dropins);
-                if (r == -ENOENT) {
-                        if (arg_drop_in)
-                                return log_error_errno(r, "Cannot find network config '%s'.", *name);
-
-                        log_debug("No existing network config '%s' found, creating a new file.", *name);
-
-                        path = path_join(NETWORK_DIRS[arg_runtime ? 1 : 0], *name);
-                        if (!path)
-                                return log_oom();
-
-                        r = edit_files_add(&context, path, NULL, NULL);
-                        if (r < 0)
-                                return r;
-                        continue;
-                }
-                if (r < 0)
-                        return log_error_errno(r, "Failed to get the path of network config '%s': %m", *name);
-
-                r = add_config_to_edit(&context, path, dropins);
-                if (r < 0)
-                        return r;
-        }
-
-        r = do_edit_files_and_install(&context);
-        if (r < 0)
-                return r;
-
-        if (arg_no_reload)
-                return 0;
-
-        if (!sd_booted() || running_in_chroot() > 0) {
-                log_debug("System is not booted with systemd or is running in chroot, skipping reload.");
-                return 0;
-        }
-
-        _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
-
-        r = sd_bus_open_system(&bus);
-        if (r < 0)
-                return log_error_errno(r, "Failed to connect to system bus: %m");
-
-        if (FLAGS_SET(reload, RELOAD_UDEVD)) {
-                r = udevd_reload(bus);
-                if (r < 0)
-                        return r;
-        }
-
-        if (FLAGS_SET(reload, RELOAD_NETWORKD)) {
-                _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
-
-                if (!networkd_is_running()) {
-                        log_debug("systemd-networkd is not running, skipping reload.");
-                        return 0;
-                }
-
-                r = bus_call_method(bus, bus_network_mgr, "Reload", &error, NULL, NULL);
-                if (r < 0)
-                        return log_error_errno(r, "Failed to reload systemd-networkd: %s", bus_error_message(&error, r));
-        }
-
-        return 0;
-}
-
-static int verb_cat(int argc, char *argv[], void *userdata) {
-        _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
-        int r, ret = 0;
-
-        pager_open(arg_pager_flags);
-
-        STRV_FOREACH(name, strv_skip(argv, 1)) {
-                _cleanup_strv_free_ char **dropins = NULL;
-                _cleanup_free_ char *path = NULL;
-                const char *link_config;
-
-                link_config = startswith(*name, "@");
-                if (link_config) {
-                        r = get_config_files_by_link_config(link_config, &rtnl, &path, &dropins, /* ret_reload = */ NULL);
-                        if (r < 0)
-                                return RET_GATHER(ret, r);
-                } else {
-                        r = get_config_files_by_name(*name, &path, &dropins);
-                        if (r == -ENOENT) {
-                                RET_GATHER(ret, log_error_errno(r, "Cannot find network config file '%s'.", *name));
-                                continue;
-                        }
-                        if (r < 0) {
-                                log_error_errno(r, "Failed to get the path of network config '%s': %m", *name);
-                                return RET_GATHER(ret, r);
-                        }
-                }
-
-                r = cat_files(path, dropins, /* flags = */ CAT_FORMAT_HAS_SECTIONS);
-                if (r < 0)
-                        return RET_GATHER(ret, r);
-        }
-
-        return ret;
-}
-
 static int help(void) {
         _cleanup_free_ char *link = NULL;
         int r;
@@ -3344,6 +2877,8 @@ static int help(void) {
                "  reload                 Reload .network and .netdev files\n"
                "  edit FILES|DEVICES...  Edit network configuration files\n"
                "  cat FILES|DEVICES...   Show network configuration files\n"
+               "  mask FILES...          Mask network configuration files\n"
+               "  unmask FILES...        Unmask network configuration files\n"
                "\nOptions:\n"
                "  -h --help              Show this help\n"
                "     --version           Show package version\n"
@@ -3500,6 +3035,8 @@ static int networkctl_main(int argc, char *argv[]) {
                 { "reload",      1,        1,        VERB_ONLINE_ONLY,              verb_reload         },
                 { "edit",        2,        VERB_ANY, 0,                             verb_edit           },
                 { "cat",         2,        VERB_ANY, 0,                             verb_cat            },
+                { "mask",        2,        VERB_ANY, 0,                             verb_mask           },
+                { "unmask",      2,        VERB_ANY, 0,                             verb_unmask         },
                 {}
         };
 
diff --git a/src/network/networkctl.h b/src/network/networkctl.h
new file mode 100644 (file)
index 0000000..46b44f7
--- /dev/null
@@ -0,0 +1,23 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include <stdbool.h>
+
+#include "sd-bus.h"
+
+#include "output-mode.h"
+#include "pager.h"
+
+extern PagerFlags arg_pager_flags;
+extern bool arg_legend;
+extern bool arg_no_reload;
+extern bool arg_all;
+extern bool arg_stats;
+extern bool arg_full;
+extern bool arg_runtime;
+extern unsigned arg_lines;
+extern char *arg_drop_in;
+extern JsonFormatFlags arg_json_format_flags;
+
+bool networkd_is_running(void);
+int acquire_bus(sd_bus **ret);
index 29c424026e2734d0c32408a8a01d8e45343af4ea..e3bdc66c587efe5731f460864b2030513f647a17 100644 (file)
@@ -674,8 +674,8 @@ static void address_modify_nft_set_context(Address *address, bool add, NFTSetCon
                 }
 
                 if (r < 0)
-                        log_warning_errno(r, "Failed to %s NFT set: family %s, table %s, set %s, IP address %s, ignoring",
-                                          add? "add" : "delete",
+                        log_warning_errno(r, "Failed to %s NFT set: family %s, table %s, set %s, IP address %s, ignoring: %m",
+                                          add ? "add" : "delete",
                                           nfproto_to_string(nft_set->nfproto), nft_set->table, nft_set->set,
                                           IN_ADDR_PREFIX_TO_STRING(address->family, &address->in_addr, address->prefixlen));
                 else
@@ -1480,7 +1480,7 @@ static bool address_is_ready_to_configure(Link *link, const Address *address) {
 }
 
 static int address_process_request(Request *req, Link *link, Address *address) {
-        struct Address *existing;
+        Address *existing;
         struct ifa_cacheinfo c;
         int r;
 
@@ -2472,7 +2472,7 @@ int address_section_verify(Address *address) {
         if (address->flags != filtered_flags) {
                 _cleanup_free_ char *str = NULL;
 
-                (void) address_flags_to_string_alloc(filtered_flags, address->family, &str);
+                (void) address_flags_to_string_alloc(address->flags ^ filtered_flags, address->family, &str);
                 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
                                          "%s: unexpected address flags \"%s\" were configured. "
                                          "Ignoring [Address] section from line %u.",
index d84b6b34f6376841a9dfc8f940345ae7e6413799..3c052675217611d5c9179bb727a49eb98a33970f 100644 (file)
@@ -27,9 +27,18 @@ static void set_bit(unsigned nr, uint32_t *addr) {
         addr[nr / 32] |= (UINT32_C(1) << (nr % 32));
 }
 
-static int add_single(sd_netlink_message *m, uint16_t id, bool untagged, bool is_pvid) {
+static int add_single(sd_netlink_message *m, uint16_t id, bool untagged, bool is_pvid, char **str) {
         assert(m);
         assert(id < BRIDGE_VLAN_BITMAP_MAX);
+
+        if (DEBUG_LOGGING)
+                (void) strextendf_with_separator(str, ",", "%u%s%s%s%s%s", id,
+                                                 (untagged || is_pvid) ? "(" : "",
+                                                 untagged ? "untagged" : "",
+                                                 (untagged && is_pvid) ? "," : "",
+                                                 is_pvid ? "pvid" : "",
+                                                 (untagged || is_pvid) ? ")" : "");
+
         return sd_netlink_message_append_data(m, IFLA_BRIDGE_VLAN_INFO,
                                               &(struct bridge_vlan_info) {
                                                       .vid = id,
@@ -39,7 +48,7 @@ static int add_single(sd_netlink_message *m, uint16_t id, bool untagged, bool is
                                               sizeof(struct bridge_vlan_info));
 }
 
-static int add_range(sd_netlink_message *m, uint16_t begin, uint16_t end, bool untagged) {
+static int add_range(sd_netlink_message *m, uint16_t begin, uint16_t end, bool untagged, char **str) {
         int r;
 
         assert(m);
@@ -47,7 +56,10 @@ static int add_range(sd_netlink_message *m, uint16_t begin, uint16_t end, bool u
         assert(end < BRIDGE_VLAN_BITMAP_MAX);
 
         if (begin == end)
-                return add_single(m, begin, untagged, /* is_pvid = */ false);
+                return add_single(m, begin, untagged, /* is_pvid = */ false, str);
+
+        if (DEBUG_LOGGING)
+                (void) strextendf_with_separator(str, ",", "%u-%u%s", begin, end, untagged ? "(untagged)" : "");
 
         r = sd_netlink_message_append_data(m, IFLA_BRIDGE_VLAN_INFO,
                                            &(struct bridge_vlan_info) {
@@ -95,6 +107,7 @@ static uint16_t link_get_pvid(Link *link, bool *ret_untagged) {
 }
 
 static int bridge_vlan_append_set_info(Link *link, sd_netlink_message *m) {
+        _cleanup_free_ char *str = NULL;
         uint16_t pvid, begin = UINT16_MAX;
         bool untagged, pvid_is_untagged;
         int r;
@@ -112,14 +125,14 @@ static int bridge_vlan_append_set_info(Link *link, sd_netlink_message *m) {
                         if (begin != UINT16_MAX) {
                                 assert(begin < k);
 
-                                r = add_range(m, begin, k - 1, untagged);
+                                r = add_range(m, begin, k - 1, untagged, &str);
                                 if (r < 0)
                                         return r;
 
                                 begin = UINT16_MAX;
                         }
 
-                        r = add_single(m, pvid, pvid_is_untagged, /* is_pvid = */ true);
+                        r = add_single(m, pvid, pvid_is_untagged, /* is_pvid = */ true, &str);
                         if (r < 0)
                                 return r;
 
@@ -131,7 +144,7 @@ static int bridge_vlan_append_set_info(Link *link, sd_netlink_message *m) {
                         if (begin != UINT16_MAX) {
                                 assert(begin < k);
 
-                                r = add_range(m, begin, k - 1, untagged);
+                                r = add_range(m, begin, k - 1, untagged, &str);
                                 if (r < 0)
                                         return r;
 
@@ -151,7 +164,7 @@ static int bridge_vlan_append_set_info(Link *link, sd_netlink_message *m) {
                                 continue;
 
                         /* Tagging flag is changed from the previous bits. Finish them. */
-                        r = add_range(m, begin, k - 1, untagged);
+                        r = add_range(m, begin, k - 1, untagged, &str);
                         if (r < 0)
                                 return r;
 
@@ -170,10 +183,13 @@ static int bridge_vlan_append_set_info(Link *link, sd_netlink_message *m) {
          * the above loop, we run 0…4095. */
         assert_cc(BRIDGE_VLAN_BITMAP_MAX > VLANID_MAX);
         assert(begin == UINT16_MAX);
+
+        log_link_debug(link, "Setting Bridge VLAN IDs: %s", strna(str));
         return 0;
 }
 
 static int bridge_vlan_append_del_info(Link *link, sd_netlink_message *m) {
+        _cleanup_free_ char *str = NULL;
         uint16_t pvid, begin = UINT16_MAX;
         int r;
 
@@ -192,7 +208,7 @@ static int bridge_vlan_append_del_info(Link *link, sd_netlink_message *m) {
                         if (begin != UINT16_MAX) {
                                 assert(begin < k);
 
-                                r = add_range(m, begin, k - 1, /* untagged = */ false);
+                                r = add_range(m, begin, k - 1, /* untagged = */ false, &str);
                                 if (r < 0)
                                         return r;
 
@@ -211,6 +227,8 @@ static int bridge_vlan_append_del_info(Link *link, sd_netlink_message *m) {
 
         /* No pending bit sequence. */
         assert(begin == UINT16_MAX);
+
+        log_link_debug(link, "Removing Bridge VLAN IDs: %s", strna(str));
         return 0;
 }
 
index 8542ffa6b5d1ef00f9376fa828308c73a67a2148..c9e3c937f47ff7376be5b94528afccc31836c03b 100644 (file)
@@ -25,6 +25,7 @@ Network.SpeedMeter,                      config_parse_bool,
 Network.SpeedMeterIntervalSec,           config_parse_sec,                       0,          offsetof(Manager, speed_meter_interval_usec)
 Network.ManageForeignRoutingPolicyRules, config_parse_bool,                      0,          offsetof(Manager, manage_foreign_rules)
 Network.ManageForeignRoutes,             config_parse_bool,                      0,          offsetof(Manager, manage_foreign_routes)
+Network.ManageForeignNextHops,           config_parse_bool,                      0,          offsetof(Manager, manage_foreign_nexthops)
 Network.RouteTable,                      config_parse_route_table_names,         0,          0
 Network.IPv6PrivacyExtensions,           config_parse_ipv6_privacy_extensions,   0,          offsetof(Manager, ipv6_privacy_extensions)
 DHCPv4.DUIDType,                         config_parse_duid_type,                 0,          offsetof(Manager, dhcp_duid)
index eed8d9f7879aa481403b5eaf720f9ebb18077b77..b4defb7091c276734f18d18c46896533be850ccd 100644 (file)
@@ -185,16 +185,20 @@ static int nexthop_build_json(NextHop *n, JsonVariant **ret) {
                                 JSON_BUILD_PAIR_STRING("ConfigState", state)));
 }
 
-static int nexthops_append_json(Set *nexthops, JsonVariant **v) {
+static int nexthops_append_json(Manager *manager, int ifindex, JsonVariant **v) {
         _cleanup_(json_variant_unrefp) JsonVariant *array = NULL;
         NextHop *nexthop;
         int r;
 
+        assert(manager);
         assert(v);
 
-        SET_FOREACH(nexthop, nexthops) {
+        HASHMAP_FOREACH(nexthop, manager->nexthops_by_id) {
                 _cleanup_(json_variant_unrefp) JsonVariant *e = NULL;
 
+                if (nexthop->ifindex != ifindex)
+                        continue;
+
                 r = nexthop_build_json(nexthop, &e);
                 if (r < 0)
                         return r;
@@ -1354,7 +1358,7 @@ int link_build_json(Link *link, JsonVariant **ret) {
         if (r < 0)
                 return r;
 
-        r = nexthops_append_json(link->nexthops, &v);
+        r = nexthops_append_json(link->manager, link->ifindex, &v);
         if (r < 0)
                 return r;
 
@@ -1417,7 +1421,7 @@ int manager_build_json(Manager *manager, JsonVariant **ret) {
         if (r < 0)
                 return r;
 
-        r = nexthops_append_json(manager->nexthops, &v);
+        r = nexthops_append_json(manager, /* ifindex = */ 0, &v);
         if (r < 0)
                 return r;
 
index 58d487570a7c13609f44e3c9ffa59932ef61274b..743957d27c67e26a5ca3e73b64e9d116a277ecc9 100644 (file)
@@ -100,10 +100,12 @@ int bus_link_method_set_ntp_servers(sd_bus_message *message, void *userdata, sd_
                         return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid NTP server: %s", *i);
         }
 
-        r = bus_verify_polkit_async(message, CAP_NET_ADMIN,
-                                    "org.freedesktop.network1.set-ntp-servers",
-                                    NULL, true, UID_INVALID,
-                                    &l->manager->polkit_registry, error);
+        r = bus_verify_polkit_async(
+                        message,
+                        "org.freedesktop.network1.set-ntp-servers",
+                        /* details= */ NULL,
+                        &l->manager->polkit_registry,
+                        error);
         if (r < 0)
                 return r;
         if (r == 0)
@@ -134,10 +136,12 @@ static int bus_link_method_set_dns_servers_internal(sd_bus_message *message, voi
         if (r < 0)
                 return r;
 
-        r = bus_verify_polkit_async(message, CAP_NET_ADMIN,
-                                    "org.freedesktop.network1.set-dns-servers",
-                                    NULL, true, UID_INVALID,
-                                    &l->manager->polkit_registry, error);
+        r = bus_verify_polkit_async(
+                        message,
+                        "org.freedesktop.network1.set-dns-servers",
+                        /* details= */ NULL,
+                        &l->manager->polkit_registry,
+                        error);
         if (r < 0)
                 goto finalize;
         if (r == 0) {
@@ -231,10 +235,12 @@ int bus_link_method_set_domains(sd_bus_message *message, void *userdata, sd_bus_
         if (r < 0)
                 return r;
 
-        r = bus_verify_polkit_async(message, CAP_NET_ADMIN,
-                                    "org.freedesktop.network1.set-domains",
-                                    NULL, true, UID_INVALID,
-                                    &l->manager->polkit_registry, error);
+        r = bus_verify_polkit_async(
+                        message,
+                        "org.freedesktop.network1.set-domains",
+                        /* details= */ NULL,
+                        &l->manager->polkit_registry,
+                        error);
         if (r < 0)
                 return r;
         if (r == 0)
@@ -266,10 +272,12 @@ int bus_link_method_set_default_route(sd_bus_message *message, void *userdata, s
         if (r < 0)
                 return r;
 
-        r = bus_verify_polkit_async(message, CAP_NET_ADMIN,
-                                    "org.freedesktop.network1.set-default-route",
-                                    NULL, true, UID_INVALID,
-                                    &l->manager->polkit_registry, error);
+        r = bus_verify_polkit_async(
+                        message,
+                        "org.freedesktop.network1.set-default-route",
+                        /* details= */ NULL,
+                        &l->manager->polkit_registry,
+                        error);
         if (r < 0)
                 return r;
         if (r == 0)
@@ -310,10 +318,12 @@ int bus_link_method_set_llmnr(sd_bus_message *message, void *userdata, sd_bus_er
                         return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid LLMNR setting: %s", llmnr);
         }
 
-        r = bus_verify_polkit_async(message, CAP_NET_ADMIN,
-                                    "org.freedesktop.network1.set-llmnr",
-                                    NULL, true, UID_INVALID,
-                                    &l->manager->polkit_registry, error);
+        r = bus_verify_polkit_async(
+                        message,
+                        "org.freedesktop.network1.set-llmnr",
+                        /* details= */ NULL,
+                        &l->manager->polkit_registry,
+                        error);
         if (r < 0)
                 return r;
         if (r == 0)
@@ -354,10 +364,12 @@ int bus_link_method_set_mdns(sd_bus_message *message, void *userdata, sd_bus_err
                         return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid MulticastDNS setting: %s", mdns);
         }
 
-        r = bus_verify_polkit_async(message, CAP_NET_ADMIN,
-                                    "org.freedesktop.network1.set-mdns",
-                                    NULL, true, UID_INVALID,
-                                    &l->manager->polkit_registry, error);
+        r = bus_verify_polkit_async(
+                        message,
+                        "org.freedesktop.network1.set-mdns",
+                        /* details= */ NULL,
+                        &l->manager->polkit_registry,
+                        error);
         if (r < 0)
                 return r;
         if (r == 0)
@@ -398,10 +410,12 @@ int bus_link_method_set_dns_over_tls(sd_bus_message *message, void *userdata, sd
                         return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid DNSOverTLS setting: %s", dns_over_tls);
         }
 
-        r = bus_verify_polkit_async(message, CAP_NET_ADMIN,
-                                    "org.freedesktop.network1.set-dns-over-tls",
-                                    NULL, true, UID_INVALID,
-                                    &l->manager->polkit_registry, error);
+        r = bus_verify_polkit_async(
+                        message,
+                        "org.freedesktop.network1.set-dns-over-tls",
+                        /* details= */ NULL,
+                        &l->manager->polkit_registry,
+                        error);
         if (r < 0)
                 return r;
         if (r == 0)
@@ -442,10 +456,12 @@ int bus_link_method_set_dnssec(sd_bus_message *message, void *userdata, sd_bus_e
                         return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid DNSSEC setting: %s", dnssec);
         }
 
-        r = bus_verify_polkit_async(message, CAP_NET_ADMIN,
-                                    "org.freedesktop.network1.set-dnssec",
-                                    NULL, true, UID_INVALID,
-                                    &l->manager->polkit_registry, error);
+        r = bus_verify_polkit_async(
+                        message,
+                        "org.freedesktop.network1.set-dnssec",
+                        /* details= */ NULL,
+                        &l->manager->polkit_registry,
+                        error);
         if (r < 0)
                 return r;
         if (r == 0)
@@ -496,10 +512,12 @@ int bus_link_method_set_dnssec_negative_trust_anchors(sd_bus_message *message, v
                         return r;
         }
 
-        r = bus_verify_polkit_async(message, CAP_NET_ADMIN,
-                                    "org.freedesktop.network1.set-dnssec-negative-trust-anchors",
-                                    NULL, true, UID_INVALID,
-                                    &l->manager->polkit_registry, error);
+        r = bus_verify_polkit_async(
+                        message,
+                        "org.freedesktop.network1.set-dnssec-negative-trust-anchors",
+                        /* details= */ NULL,
+                        &l->manager->polkit_registry,
+                        error);
         if (r < 0)
                 return r;
         if (r == 0)
@@ -525,10 +543,11 @@ int bus_link_method_revert_ntp(sd_bus_message *message, void *userdata, sd_bus_e
         if (r < 0)
                 return r;
 
-        r = bus_verify_polkit_async(message, CAP_NET_ADMIN,
-                                    "org.freedesktop.network1.revert-ntp",
-                                    NULL, true, UID_INVALID,
-                                    &l->manager->polkit_registry, error);
+        r = bus_verify_polkit_async(
+                        message,
+                        "org.freedesktop.network1.revert-ntp",
+                        /* details= */ NULL,
+                        &l->manager->polkit_registry, error);
         if (r < 0)
                 return r;
         if (r == 0)
@@ -553,10 +572,12 @@ int bus_link_method_revert_dns(sd_bus_message *message, void *userdata, sd_bus_e
         if (r < 0)
                 return r;
 
-        r = bus_verify_polkit_async(message, CAP_NET_ADMIN,
-                                    "org.freedesktop.network1.revert-dns",
-                                    NULL, true, UID_INVALID,
-                                    &l->manager->polkit_registry, error);
+        r = bus_verify_polkit_async(
+                        message,
+                        "org.freedesktop.network1.revert-dns",
+                        /* details= */ NULL,
+                        &l->manager->polkit_registry,
+                        error);
         if (r < 0)
                 return r;
         if (r == 0)
@@ -580,10 +601,12 @@ int bus_link_method_force_renew(sd_bus_message *message, void *userdata, sd_bus_
                                          "Interface %s is not managed by systemd-networkd",
                                          l->ifname);
 
-        r = bus_verify_polkit_async(message, CAP_NET_ADMIN,
-                                    "org.freedesktop.network1.forcerenew",
-                                    NULL, true, UID_INVALID,
-                                    &l->manager->polkit_registry, error);
+        r = bus_verify_polkit_async(
+                        message,
+                        "org.freedesktop.network1.forcerenew",
+                        /* details= */ NULL,
+                        &l->manager->polkit_registry,
+                        error);
         if (r < 0)
                 return r;
         if (r == 0)
@@ -607,10 +630,12 @@ int bus_link_method_renew(sd_bus_message *message, void *userdata, sd_bus_error
                                          "Interface %s is not managed by systemd-networkd",
                                          l->ifname);
 
-        r = bus_verify_polkit_async(message, CAP_NET_ADMIN,
-                                    "org.freedesktop.network1.renew",
-                                    NULL, true, UID_INVALID,
-                                    &l->manager->polkit_registry, error);
+        r = bus_verify_polkit_async(
+                        message,
+                        "org.freedesktop.network1.renew",
+                        /* details= */ NULL,
+                        &l->manager->polkit_registry,
+                        error);
         if (r < 0)
                 return r;
         if (r == 0)
@@ -629,10 +654,12 @@ int bus_link_method_reconfigure(sd_bus_message *message, void *userdata, sd_bus_
 
         assert(message);
 
-        r = bus_verify_polkit_async(message, CAP_NET_ADMIN,
-                                    "org.freedesktop.network1.reconfigure",
-                                    NULL, true, UID_INVALID,
-                                    &l->manager->polkit_registry, error);
+        r = bus_verify_polkit_async(
+                        message,
+                        "org.freedesktop.network1.reconfigure",
+                        /* details= */ NULL,
+                        &l->manager->polkit_registry,
+                        error);
         if (r < 0)
                 return r;
         if (r == 0)
index aad86eed9911281ef97de3bf4f5332fef742d7db..47433ef1ab0b59fe0fc56df8a66fdfc8597054c9 100644 (file)
@@ -209,7 +209,6 @@ static Link *link_free(Link *link) {
         link_dns_settings_clear(link);
 
         link->routes = set_free(link->routes);
-        link->nexthops = set_free(link->nexthops);
         link->neighbors = set_free(link->neighbors);
         link->addresses = set_free(link->addresses);
         link->qdiscs = set_free(link->qdiscs);
@@ -252,7 +251,9 @@ int link_get_by_index(Manager *m, int ifindex, Link **ret) {
         Link *link;
 
         assert(m);
-        assert(ifindex > 0);
+
+        if (ifindex <= 0)
+                return -EINVAL;
 
         link = hashmap_get(m->links_by_index, INT_TO_PTR(ifindex));
         if (!link)
@@ -1049,6 +1050,10 @@ static int link_configure(Link *link) {
         if (r < 0)
                 return r;
 
+        r = link_configure_mtu(link);
+        if (r < 0)
+                return r;
+
         if (link->iftype == ARPHRD_CAN) {
                 /* let's shortcut things for CAN which doesn't need most of what's done below. */
                 r = link_request_to_set_can(link);
@@ -1082,10 +1087,6 @@ static int link_configure(Link *link) {
         if (r < 0)
                 return r;
 
-        r = link_configure_mtu(link);
-        if (r < 0)
-                return r;
-
         r = link_request_to_set_addrgen_mode(link);
         if (r < 0)
                 return r;
@@ -1240,10 +1241,20 @@ int link_reconfigure_impl(Link *link, bool force) {
                 return 0;
 
         if (network) {
+                _cleanup_free_ char *joined = strv_join(network->dropins, ", ");
+
                 if (link->state == LINK_STATE_INITIALIZED)
-                        log_link_info(link, "Configuring with %s.", network->filename);
+                        log_link_info(link, "Configuring with %s%s%s%s.",
+                                      network->filename,
+                                      isempty(joined) ? "" : " (dropins: ",
+                                      joined,
+                                      isempty(joined) ? "" : ")");
                 else
-                        log_link_info(link, "Reconfiguring with %s.", network->filename);
+                        log_link_info(link, "Reconfiguring with %s%s%s%s.",
+                                      network->filename,
+                                      isempty(joined) ? "" : " (dropins: ",
+                                      joined,
+                                      isempty(joined) ? "" : ")");
         } else
                 log_link_full(link, link->state == LINK_STATE_INITIALIZED ? LOG_DEBUG : LOG_INFO,
                               "Unmanaging interface.");
index 7458ea93bd0824d7b8cb651f237f7e75676f4f59..cad9fa8410685fa20b968c66ef34d7623945012a 100644 (file)
@@ -125,7 +125,6 @@ typedef struct Link {
         Set *addresses;
         Set *neighbors;
         Set *routes;
-        Set *nexthops;
         Set *qdiscs;
         Set *tclasses;
 
index aecbc1d67c69ef378e25ea108689819126fffd2d..a8906f81c1ba606f0326a73979b31e6a5438d8cd 100644 (file)
@@ -201,10 +201,12 @@ static int bus_method_reload(sd_bus_message *message, void *userdata, sd_bus_err
         Manager *manager = userdata;
         int r;
 
-        r = bus_verify_polkit_async(message, CAP_NET_ADMIN,
-                                    "org.freedesktop.network1.reload",
-                                    NULL, true, UID_INVALID,
-                                    &manager->polkit_registry, error);
+        r = bus_verify_polkit_async(
+                        message,
+                        "org.freedesktop.network1.reload",
+                        /* details= */ NULL,
+                        &manager->polkit_registry,
+                        error);
         if (r < 0)
                 return r;
         if (r == 0)
index fca5d76618438aed22fe9f446191c48b9b3d2edd..b162d21aa0f9f91720f2cdc819f6775e3b6f82b9 100644 (file)
@@ -591,6 +591,7 @@ int manager_new(Manager **ret, bool test_mode) {
                 .online_state = _LINK_ONLINE_STATE_INVALID,
                 .manage_foreign_routes = true,
                 .manage_foreign_rules = true,
+                .manage_foreign_nexthops = true,
                 .ethtool_fd = -EBADF,
                 .dhcp_duid.type = DUID_TYPE_EN,
                 .dhcp6_duid.type = DUID_TYPE_EN,
@@ -648,7 +649,6 @@ Manager* manager_free(Manager *m) {
          * set_free() must be called after the above sd_netlink_unref(). */
         m->routes = set_free(m->routes);
 
-        m->nexthops = set_free(m->nexthops);
         m->nexthops_by_id = hashmap_free(m->nexthops_by_id);
 
         sd_event_source_unref(m->speed_meter_event_source);
@@ -867,6 +867,9 @@ static int manager_enumerate_nexthop(Manager *m) {
         assert(m);
         assert(m->rtnl);
 
+        if (!m->manage_foreign_nexthops)
+                return 0;
+
         r = sd_rtnl_message_new_nexthop(m->rtnl, &req, RTM_GETNEXTHOP, 0, 0);
         if (r < 0)
                 return r;
index fbef5289d2865095cbc27889c30294c2923df5c1..a9827d8a4b4fffea9b61280829b5abc6e40691b6 100644 (file)
@@ -38,6 +38,7 @@ struct Manager {
         bool restarting;
         bool manage_foreign_routes;
         bool manage_foreign_rules;
+        bool manage_foreign_nexthops;
 
         Set *dirty_links;
         Set *new_wlan_ifindices;
@@ -73,9 +74,6 @@ struct Manager {
         /* Manage nexthops by id. */
         Hashmap *nexthops_by_id;
 
-        /* Manager stores nexthops without RTA_OIF attribute. */
-        Set *nexthops;
-
         /* Manager stores routes without RTA_OIF attribute. */
         unsigned route_remove_messages;
         Set *routes;
index eb208c183cda23114e59add1f78debb68c9d1bd8..b9c7875bc4dc94dc0981dcb43936bcdba7d1517d 100644 (file)
@@ -648,12 +648,12 @@ int network_drop_invalid_neighbors(Network *network) {
                 dup = set_remove(neighbors, neighbor);
                 if (dup) {
                         log_warning("%s: Duplicated neighbor settings for %s is specified at line %u and %u, "
-                                    "dropping the address setting specified at line %u.",
+                                    "dropping the neighbor setting specified at line %u.",
                                     dup->section->filename,
                                     IN_ADDR_TO_STRING(neighbor->family, &neighbor->in_addr),
                                     neighbor->section->line,
                                     dup->section->line, dup->section->line);
-                        /* neighbor_free() will drop the address from neighbors_by_section. */
+                        /* neighbor_free() will drop the neighbor from neighbors_by_section. */
                         neighbor_free(dup);
                 }
 
index e2ded28197bc81b2fabe75b69efda24e04781942..aeb33ae8d603a6d37ccd3f586c649146c33508fb 100644 (file)
@@ -29,18 +29,9 @@ NextHop *nexthop_free(NextHop *nexthop) {
 
         config_section_free(nexthop->section);
 
-        if (nexthop->link) {
-                set_remove(nexthop->link->nexthops, nexthop);
-
-                if (nexthop->link->manager && nexthop->id > 0)
-                        hashmap_remove(nexthop->link->manager->nexthops_by_id, UINT32_TO_PTR(nexthop->id));
-        }
-
         if (nexthop->manager) {
-                set_remove(nexthop->manager->nexthops, nexthop);
-
-                if (nexthop->id > 0)
-                        hashmap_remove(nexthop->manager->nexthops_by_id, UINT32_TO_PTR(nexthop->id));
+                assert(nexthop->id > 0);
+                hashmap_remove(nexthop->manager->nexthops_by_id, UINT32_TO_PTR(nexthop->id));
         }
 
         hashmap_free_free(nexthop->group);
@@ -50,6 +41,14 @@ NextHop *nexthop_free(NextHop *nexthop) {
 
 DEFINE_SECTION_CLEANUP_FUNCTIONS(NextHop, nexthop_free);
 
+DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(
+                nexthop_hash_ops,
+                void,
+                trivial_hash_func,
+                trivial_compare_func,
+                NextHop,
+                nexthop_free);
+
 static int nexthop_new(NextHop **ret) {
         _cleanup_(nexthop_freep) NextHop *nexthop = NULL;
 
@@ -106,35 +105,54 @@ static int nexthop_new_static(Network *network, const char *filename, unsigned s
 
 static void nexthop_hash_func(const NextHop *nexthop, struct siphash *state) {
         assert(nexthop);
+        assert(state);
 
-        siphash24_compress(&nexthop->protocol, sizeof(nexthop->protocol), state);
         siphash24_compress(&nexthop->id, sizeof(nexthop->id), state);
-        siphash24_compress(&nexthop->blackhole, sizeof(nexthop->blackhole), state);
-        siphash24_compress(&nexthop->family, sizeof(nexthop->family), state);
+}
 
-        switch (nexthop->family) {
-        case AF_INET:
-        case AF_INET6:
-                siphash24_compress(&nexthop->gw, FAMILY_ADDRESS_SIZE(nexthop->family), state);
+static int nexthop_compare_func(const NextHop *a, const NextHop *b) {
+        assert(a);
+        assert(b);
 
-                break;
-        default:
-                /* treat any other address family as AF_UNSPEC */
-                break;
-        }
+        return CMP(a->id, b->id);
 }
 
-static int nexthop_compare_func(const NextHop *a, const NextHop *b) {
+static int nexthop_compare_full(const NextHop *a, const NextHop *b) {
         int r;
 
+        assert(a);
+        assert(b);
+
+        /* This compares detailed configs, except for ID and ifindex. */
+
         r = CMP(a->protocol, b->protocol);
         if (r != 0)
                 return r;
 
-        r = CMP(a->id, b->id);
+        r = CMP(a->flags, b->flags);
+        if (r != 0)
+                return r;
+
+        r = CMP(hashmap_size(a->group), hashmap_size(b->group));
         if (r != 0)
                 return r;
 
+        if (!hashmap_isempty(a->group)) {
+                struct nexthop_grp *ga;
+
+                HASHMAP_FOREACH(ga, a->group) {
+                        struct nexthop_grp *gb;
+
+                        gb = hashmap_get(b->group, UINT32_TO_PTR(ga->id));
+                        if (!gb)
+                                return CMP(ga, gb);
+
+                        r = CMP(ga->weight, gb->weight);
+                        if (r != 0)
+                                return r;
+                }
+        }
+
         r = CMP(a->blackhole, b->blackhole);
         if (r != 0)
                 return r;
@@ -143,29 +161,15 @@ static int nexthop_compare_func(const NextHop *a, const NextHop *b) {
         if (r != 0)
                 return r;
 
-        if (IN_SET(a->family, AF_INET, AF_INET6))
-                return memcmp(&a->gw, &b->gw, FAMILY_ADDRESS_SIZE(a->family));
+        if (IN_SET(a->family, AF_INET, AF_INET6)) {
+                r = memcmp(&a->gw, &b->gw, FAMILY_ADDRESS_SIZE(a->family));
+                if (r != 0)
+                        return r;
+        }
 
         return 0;
 }
 
-DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(
-                nexthop_hash_ops,
-                NextHop,
-                nexthop_hash_func,
-                nexthop_compare_func,
-                nexthop_free);
-
-static bool nexthop_equal(const NextHop *a, const NextHop *b) {
-        if (a == b)
-                return true;
-
-        if (!a || !b)
-                return false;
-
-        return nexthop_compare_func(a, b) == 0;
-}
-
 static int nexthop_dup(const NextHop *src, NextHop **ret) {
         _cleanup_(nexthop_freep) NextHop *dest = NULL;
         struct nexthop_grp *nhg;
@@ -180,7 +184,6 @@ static int nexthop_dup(const NextHop *src, NextHop **ret) {
 
         /* unset all pointers */
         dest->manager = NULL;
-        dest->link = NULL;
         dest->network = NULL;
         dest->section = NULL;
         dest->group = NULL;
@@ -203,7 +206,12 @@ static int nexthop_dup(const NextHop *src, NextHop **ret) {
         return 0;
 }
 
-int manager_get_nexthop_by_id(Manager *manager, uint32_t id, NextHop **ret) {
+static bool nexthop_bound_to_link(const NextHop *nexthop) {
+        assert(nexthop);
+        return !nexthop->blackhole && hashmap_isempty(nexthop->group);
+}
+
+int nexthop_get_by_id(Manager *manager, uint32_t id, NextHop **ret) {
         NextHop *nh;
 
         assert(manager);
@@ -220,96 +228,116 @@ int manager_get_nexthop_by_id(Manager *manager, uint32_t id, NextHop **ret) {
         return 0;
 }
 
-static bool nexthop_owned_by_link(const NextHop *nexthop) {
-        return !nexthop->blackhole && hashmap_isempty(nexthop->group);
-}
-
-static int nexthop_get(Manager *manager, Link *link, NextHop *in, NextHop **ret) {
+static int nexthop_get(Link *link, const NextHop *in, NextHop **ret) {
         NextHop *nexthop;
-        Set *nexthops;
+        int ifindex;
 
+        assert(link);
+        assert(link->manager);
         assert(in);
 
-        if (nexthop_owned_by_link(in)) {
-                if (!link)
-                        return -ENOENT;
+        if (in->id > 0)
+                return nexthop_get_by_id(link->manager, in->id, ret);
 
-                nexthops = link->nexthops;
-        } else {
-                if (!manager)
-                        return -ENOENT;
+        ifindex = nexthop_bound_to_link(in) ? link->ifindex : 0;
 
-                nexthops = manager->nexthops;
-        }
+        HASHMAP_FOREACH(nexthop, link->manager->nexthops_by_id) {
+                if (nexthop->ifindex != ifindex)
+                        continue;
+                if (nexthop_compare_full(nexthop, in) != 0)
+                        continue;
 
-        nexthop = set_get(nexthops, in);
-        if (nexthop) {
                 if (ret)
                         *ret = nexthop;
                 return 0;
         }
 
-        if (in->id > 0)
+        return -ENOENT;
+}
+
+static int nexthop_get_request_by_id(Manager *manager, uint32_t id, Request **ret) {
+        Request *req;
+
+        assert(manager);
+
+        req = ordered_set_get(
+                        manager->request_queue,
+                        &(Request) {
+                                .type = REQUEST_TYPE_NEXTHOP,
+                                .userdata = (void*) &(const NextHop) { .id = id },
+                                .hash_func = (hash_func_t) nexthop_hash_func,
+                                .compare_func = (compare_func_t) nexthop_compare_func,
+                        });
+        if (!req)
                 return -ENOENT;
 
-        /* Also find nexthop configured without ID. */
-        SET_FOREACH(nexthop, nexthops) {
-                uint32_t id;
-                bool found;
+        if (ret)
+                *ret = req;
+        return 0;
+}
 
-                id = nexthop->id;
-                nexthop->id = 0;
-                found = nexthop_equal(nexthop, in);
-                nexthop->id = id;
+static int nexthop_get_request(Link *link, const NextHop *in, Request **ret) {
+        Request *req;
+        int ifindex;
+
+        assert(link);
+        assert(link->manager);
+        assert(in);
+
+        if (in->id > 0)
+                return nexthop_get_request_by_id(link->manager, in->id, ret);
+
+        ifindex = nexthop_bound_to_link(in) ? link->ifindex : 0;
 
-                if (!found)
+        ORDERED_SET_FOREACH(req, link->manager->request_queue) {
+                if (req->type != REQUEST_TYPE_NEXTHOP)
+                        continue;
+
+                NextHop *nexthop = ASSERT_PTR(req->userdata);
+                if (nexthop->ifindex != ifindex)
+                        continue;
+                if (nexthop_compare_full(nexthop, in) != 0)
                         continue;
 
                 if (ret)
-                        *ret = nexthop;
+                        *ret = req;
                 return 0;
         }
 
         return -ENOENT;
 }
 
-static int nexthop_add(Manager *manager, Link *link, NextHop *nexthop) {
+static int nexthop_add_new(Manager *manager, uint32_t id, NextHop **ret) {
+        _cleanup_(nexthop_freep) NextHop *nexthop = NULL;
         int r;
 
-        assert(nexthop);
-        assert(nexthop->id > 0);
-
-        if (nexthop_owned_by_link(nexthop)) {
-                assert(link);
+        assert(manager);
+        assert(id > 0);
 
-                r = set_ensure_put(&link->nexthops, &nexthop_hash_ops, nexthop);
-                if (r < 0)
-                        return r;
-                if (r == 0)
-                        return -EEXIST;
+        r = nexthop_new(&nexthop);
+        if (r < 0)
+                return r;
 
-                nexthop->link = link;
+        nexthop->id = id;
 
-                manager = link->manager;
-        } else {
-                assert(manager);
+        r = hashmap_ensure_put(&manager->nexthops_by_id, &nexthop_hash_ops, UINT32_TO_PTR(nexthop->id), nexthop);
+        if (r < 0)
+                return r;
+        if (r == 0)
+                return -EEXIST;
 
-                r = set_ensure_put(&manager->nexthops, &nexthop_hash_ops, nexthop);
-                if (r < 0)
-                        return r;
-                if (r == 0)
-                        return -EEXIST;
+        nexthop->manager = manager;
 
-                nexthop->manager = manager;
-        }
+        if (ret)
+                *ret = nexthop;
 
-        return hashmap_ensure_put(&manager->nexthops_by_id, NULL, UINT32_TO_PTR(nexthop->id), nexthop);
+        TAKE_PTR(nexthop);
+        return 0;
 }
 
 static int nexthop_acquire_id(Manager *manager, NextHop *nexthop) {
         _cleanup_set_free_ Set *ids = NULL;
         Network *network;
-        uint32_t id;
         int r;
 
         assert(manager);
@@ -318,6 +346,10 @@ static int nexthop_acquire_id(Manager *manager, NextHop *nexthop) {
         if (nexthop->id > 0)
                 return 0;
 
+        /* If ManageForeignNextHops=no, nexthop with id == 0 should be already filtered by
+         * nexthop_section_verify(). */
+        assert(manager->manage_foreign_nexthops);
+
         /* Find the lowest unused ID. */
 
         ORDERED_HASHMAP_FOREACH(network, manager->networks) {
@@ -333,30 +365,34 @@ static int nexthop_acquire_id(Manager *manager, NextHop *nexthop) {
                 }
         }
 
-        for (id = 1; id < UINT32_MAX; id++) {
-                if (manager_get_nexthop_by_id(manager, id, NULL) >= 0)
+        for (uint32_t id = 1; id < UINT32_MAX; id++) {
+                if (nexthop_get_by_id(manager, id, NULL) >= 0)
+                        continue;
+                if (nexthop_get_request_by_id(manager, id, NULL) >= 0)
                         continue;
                 if (set_contains(ids, UINT32_TO_PTR(id)))
                         continue;
-                break;
+
+                nexthop->id = id;
+                return 0;
         }
 
-        nexthop->id = id;
-        return 0;
+        return -EBUSY;
 }
 
-static void log_nexthop_debug(const NextHop *nexthop, const char *str, const Link *link) {
+static void log_nexthop_debug(const NextHop *nexthop, const char *str, Manager *manager) {
         _cleanup_free_ char *state = NULL, *group = NULL, *flags = NULL;
         struct nexthop_grp *nhg;
+        Link *link = NULL;
 
         assert(nexthop);
         assert(str);
-
-        /* link may be NULL. */
+        assert(manager);
 
         if (!DEBUG_LOGGING)
                 return;
 
+        (void) link_get_by_index(manager, nexthop->ifindex, &link);
         (void) network_config_state_to_string_alloc(nexthop->state, &state);
         (void) route_flags_to_string_alloc(nexthop->flags, &flags);
 
@@ -390,22 +426,21 @@ static int nexthop_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, Link
 static int nexthop_remove(NextHop *nexthop) {
         _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
         Manager *manager;
-        Link *link;
+        Link *link = NULL;
+        Request *req;
         int r;
 
-        assert(nexthop);
-        assert(nexthop->manager || (nexthop->link && nexthop->link->manager));
+        manager = ASSERT_PTR(ASSERT_PTR(nexthop)->manager);
 
         /* link may be NULL. */
-        link = nexthop->link;
-        manager = nexthop->manager ?: nexthop->link->manager;
+        (void) link_get_by_index(manager, nexthop->ifindex, &link);
 
         if (nexthop->id == 0) {
                 log_link_debug(link, "Cannot remove nexthop without valid ID, ignoring.");
                 return 0;
         }
 
-        log_nexthop_debug(nexthop, "Removing", link);
+        log_nexthop_debug(nexthop, "Removing", manager);
 
         r = sd_rtnl_message_new_nexthop(manager->rtnl, &m, RTM_DELNEXTHOP, AF_UNSPEC, RTPROT_UNSPEC);
         if (r < 0)
@@ -423,6 +458,9 @@ static int nexthop_remove(NextHop *nexthop) {
         link_ref(link); /* link may be NULL, link_ref() is OK with that */
 
         nexthop_enter_removing(nexthop);
+        if (nexthop_get_request_by_id(manager, nexthop->id, &req) >= 0)
+                nexthop_enter_removing(req->userdata);
+
         return 0;
 }
 
@@ -431,6 +469,7 @@ static int nexthop_configure(NextHop *nexthop, Link *link, Request *req) {
         int r;
 
         assert(nexthop);
+        assert(nexthop->id > 0);
         assert(IN_SET(nexthop->family, AF_UNSPEC, AF_INET, AF_INET6));
         assert(link);
         assert(link->manager);
@@ -438,17 +477,15 @@ static int nexthop_configure(NextHop *nexthop, Link *link, Request *req) {
         assert(link->ifindex > 0);
         assert(req);
 
-        log_nexthop_debug(nexthop, "Configuring", link);
+        log_nexthop_debug(nexthop, "Configuring", link->manager);
 
         r = sd_rtnl_message_new_nexthop(link->manager->rtnl, &m, RTM_NEWNEXTHOP, nexthop->family, nexthop->protocol);
         if (r < 0)
                 return r;
 
-        if (nexthop->id > 0) {
-                r = sd_netlink_message_append_u32(m, NHA_ID, nexthop->id);
-                if (r < 0)
-                        return r;
-        }
+        r = sd_netlink_message_append_u32(m, NHA_ID, nexthop->id);
+        if (r < 0)
+                return r;
 
         if (!hashmap_isempty(nexthop->group)) {
                 _cleanup_free_ struct nexthop_grp *group = NULL;
@@ -471,7 +508,9 @@ static int nexthop_configure(NextHop *nexthop, Link *link, Request *req) {
                 if (r < 0)
                         return r;
         } else {
-                r = sd_netlink_message_append_u32(m, NHA_OIF, link->ifindex);
+                assert(nexthop->ifindex == link->ifindex);
+
+                r = sd_netlink_message_append_u32(m, NHA_OIF, nexthop->ifindex);
                 if (r < 0)
                         return r;
 
@@ -520,7 +559,9 @@ static bool nexthop_is_ready_to_configure(Link *link, const NextHop *nexthop) {
         if (!link_is_ready_to_configure(link, false))
                 return false;
 
-        if (nexthop_owned_by_link(nexthop)) {
+        if (nexthop_bound_to_link(nexthop)) {
+                assert(nexthop->ifindex == link->ifindex);
+
                 /* TODO: fdb nexthop does not require IFF_UP. The conditions below needs to be updated
                  * when fdb nexthop support is added. See rtm_to_nh_config() in net/ipv4/nexthop.c of
                  * kernel. */
@@ -534,7 +575,7 @@ static bool nexthop_is_ready_to_configure(Link *link, const NextHop *nexthop) {
         HASHMAP_FOREACH(nhg, nexthop->group) {
                 NextHop *g;
 
-                if (manager_get_nexthop_by_id(link->manager, nhg->id, &g) < 0)
+                if (nexthop_get_by_id(link->manager, nhg->id, &g) < 0)
                         return false;
 
                 if (!nexthop_exists(g))
@@ -556,10 +597,12 @@ static bool nexthop_is_ready_to_configure(Link *link, const NextHop *nexthop) {
 }
 
 static int nexthop_process_request(Request *req, Link *link, NextHop *nexthop) {
+        NextHop *existing;
         int r;
 
         assert(req);
         assert(link);
+        assert(link->manager);
         assert(nexthop);
 
         if (!nexthop_is_ready_to_configure(link, nexthop))
@@ -570,39 +613,49 @@ static int nexthop_process_request(Request *req, Link *link, NextHop *nexthop) {
                 return log_link_warning_errno(link, r, "Failed to configure nexthop");
 
         nexthop_enter_configuring(nexthop);
+        if (nexthop_get_by_id(link->manager, nexthop->id, &existing) >= 0)
+                nexthop_enter_configuring(existing);
+
         return 1;
 }
 
-static int link_request_nexthop(Link *link, NextHop *nexthop) {
-        NextHop *existing;
+static int link_request_nexthop(Link *link, const NextHop *nexthop) {
+        _cleanup_(nexthop_freep) NextHop *tmp = NULL;
+        NextHop *existing = NULL;
         int r;
 
         assert(link);
+        assert(link->manager);
         assert(nexthop);
         assert(nexthop->source != NETWORK_CONFIG_SOURCE_FOREIGN);
 
-        if (nexthop_get(link->manager, link, nexthop, &existing) < 0) {
-                _cleanup_(nexthop_freep) NextHop *tmp = NULL;
+        if (nexthop_get_request(link, nexthop, NULL) >= 0)
+                return 0; /* already requested, skipping. */
 
-                r = nexthop_dup(nexthop, &tmp);
-                if (r < 0)
-                        return r;
+        r = nexthop_dup(nexthop, &tmp);
+        if (r < 0)
+                return r;
 
+        if (nexthop_get(link, nexthop, &existing) < 0) {
                 r = nexthop_acquire_id(link->manager, tmp);
                 if (r < 0)
                         return r;
+        } else {
+                /* Copy ID */
+                assert(tmp->id == 0 || tmp->id == existing->id);
+                tmp->id = existing->id;
 
-                r = nexthop_add(link->manager, link, tmp);
-                if (r < 0)
-                        return r;
+                /* Copy state for logging below. */
+                tmp->state = existing->state;
+        }
 
-                existing = TAKE_PTR(tmp);
-        } else
-                existing->source = nexthop->source;
+        if (nexthop_bound_to_link(tmp))
+                tmp->ifindex = link->ifindex;
 
-        log_nexthop_debug(existing, "Requesting", link);
+        log_nexthop_debug(tmp, "Requesting", link->manager);
         r = link_queue_request_safe(link, REQUEST_TYPE_NEXTHOP,
-                                    existing, NULL,
+                                    tmp,
+                                    nexthop_free,
                                     nexthop_hash_func,
                                     nexthop_compare_func,
                                     nexthop_process_request,
@@ -612,7 +665,11 @@ static int link_request_nexthop(Link *link, NextHop *nexthop) {
         if (r <= 0)
                 return r;
 
-        nexthop_enter_requesting(existing);
+        nexthop_enter_requesting(tmp);
+        if (existing)
+                nexthop_enter_requesting(existing);
+
+        TAKE_PTR(tmp);
         return 1;
 }
 
@@ -645,14 +702,40 @@ int link_request_static_nexthops(Link *link, bool only_ipv4) {
         return 0;
 }
 
-static void manager_mark_nexthops(Manager *manager, bool foreign, const Link *except) {
+static bool nexthop_can_update(const NextHop *assigned_nexthop, const NextHop *requested_nexthop) {
+        assert(assigned_nexthop);
+        assert(assigned_nexthop->manager);
+        assert(requested_nexthop);
+        assert(requested_nexthop->network);
+
+        /* A group nexthop cannot be replaced with a non-group nexthop, and vice versa.
+         * See replace_nexthop_grp() and replace_nexthop_single() in net/ipv4/nexthop.c of the kernel. */
+        if (hashmap_isempty(assigned_nexthop->group) != hashmap_isempty(requested_nexthop->group))
+                return false;
+
+        /* There are several more conditions if we can replace a group nexthop, e.g. hash threshold and
+         * resilience. But, currently we do not support to modify that. Let's add checks for them in the
+         * future when we support to configure them.*/
+
+        /* When a nexthop is replaced with a blackhole nexthop, and a group nexthop has multiple nexthops
+         * including this nexthop, then the kernel refuses to replace the existing nexthop.
+         * So, here, for simplicity, let's unconditionally refuse to replace a non-blackhole nexthop with
+         * a blackhole nexthop. See replace_nexthop() in net/ipv4/nexthop.c of the kernel. */
+        if (!assigned_nexthop->blackhole && requested_nexthop->blackhole)
+                return false;
+
+        return true;
+}
+
+static void link_mark_nexthops(Link *link, bool foreign) {
         NextHop *nexthop;
-        Link *link;
+        Link *other;
 
-        assert(manager);
+        assert(link);
+        assert(link->manager);
 
         /* First, mark all nexthops. */
-        SET_FOREACH(nexthop, manager->nexthops) {
+        HASHMAP_FOREACH(nexthop, link->manager->nexthops_by_id) {
                 /* do not touch nexthop created by the kernel */
                 if (nexthop->protocol == RTPROT_KERNEL)
                         continue;
@@ -665,33 +748,46 @@ static void manager_mark_nexthops(Manager *manager, bool foreign, const Link *ex
                 if (!nexthop_exists(nexthop))
                         continue;
 
+                /* Ignore nexthops bound to other links. */
+                if (nexthop->ifindex > 0 && nexthop->ifindex != link->ifindex)
+                        continue;
+
                 nexthop_mark(nexthop);
         }
 
         /* Then, unmark all nexthops requested by active links. */
-        HASHMAP_FOREACH(link, manager->links_by_index) {
-                if (link == except)
+        HASHMAP_FOREACH(other, link->manager->links_by_index) {
+                if (!foreign && other == link)
                         continue;
 
-                if (!IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED))
+                if (!IN_SET(other->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED))
                         continue;
 
-                HASHMAP_FOREACH(nexthop, link->network->nexthops_by_section) {
+                HASHMAP_FOREACH(nexthop, other->network->nexthops_by_section) {
                         NextHop *existing;
 
-                        if (nexthop_get(manager, NULL, nexthop, &existing) >= 0)
-                                nexthop_unmark(existing);
+                        if (nexthop_get(other, nexthop, &existing) < 0)
+                                continue;
+
+                        if (!nexthop_can_update(existing, nexthop))
+                                continue;
+
+                        /* Found matching static configuration. Keep the existing nexthop. */
+                        nexthop_unmark(existing);
                 }
         }
 }
 
-static int manager_drop_marked_nexthops(Manager *manager) {
+int link_drop_nexthops(Link *link, bool foreign) {
         NextHop *nexthop;
         int r = 0;
 
-        assert(manager);
+        assert(link);
+        assert(link->manager);
+
+        link_mark_nexthops(link, foreign);
 
-        SET_FOREACH(nexthop, manager->nexthops) {
+        HASHMAP_FOREACH(nexthop, link->manager->nexthops_by_id) {
                 if (!nexthop_is_marked(nexthop))
                         continue;
 
@@ -701,106 +797,80 @@ static int manager_drop_marked_nexthops(Manager *manager) {
         return r;
 }
 
-int link_drop_foreign_nexthops(Link *link) {
+void link_foreignize_nexthops(Link *link) {
         NextHop *nexthop;
-        int r = 0;
 
         assert(link);
         assert(link->manager);
-        assert(link->network);
-
-        /* First, mark all nexthops. */
-        SET_FOREACH(nexthop, link->nexthops) {
-                /* do not touch nexthop created by the kernel */
-                if (nexthop->protocol == RTPROT_KERNEL)
-                        continue;
-
-                /* Do not remove nexthops we configured. */
-                if (nexthop->source != NETWORK_CONFIG_SOURCE_FOREIGN)
-                        continue;
-
-                /* Ignore nexthops not assigned yet or already removed. */
-                if (!nexthop_exists(nexthop))
-                        continue;
-
-                nexthop_mark(nexthop);
-        }
-
-        /* Then, unmark all nexthops requested by active links. */
-        HASHMAP_FOREACH(nexthop, link->network->nexthops_by_section) {
-                NextHop *existing;
 
-                if (nexthop_get(NULL, link, nexthop, &existing) >= 0)
-                        nexthop_unmark(existing);
-        }
+        link_mark_nexthops(link, /* foreign = */ false);
 
-        /* Finally, remove all marked rules. */
-        SET_FOREACH(nexthop, link->nexthops) {
+        HASHMAP_FOREACH(nexthop, link->manager->nexthops_by_id) {
                 if (!nexthop_is_marked(nexthop))
                         continue;
 
-                RET_GATHER(r, nexthop_remove(nexthop));
+                nexthop->source = NETWORK_CONFIG_SOURCE_FOREIGN;
         }
+}
 
-        manager_mark_nexthops(link->manager, /* foreign = */ true, NULL);
+static int nexthop_update_group(NextHop *nexthop, const struct nexthop_grp *group, size_t size) {
+        _cleanup_hashmap_free_free_ Hashmap *h = NULL;
+        size_t n_group;
+        int r;
 
-        return RET_GATHER(r, manager_drop_marked_nexthops(link->manager));
-}
+        assert(nexthop);
+        assert(group || size == 0);
 
-int link_drop_managed_nexthops(Link *link) {
-        NextHop *nexthop;
-        int r = 0;
+        if (size == 0 || size % sizeof(struct nexthop_grp) != 0)
+                return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
+                                       "rtnl: received nexthop message with invalid nexthop group size, ignoring.");
 
-        assert(link);
-        assert(link->manager);
+        if ((uintptr_t) group % alignof(struct nexthop_grp) != 0)
+                return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
+                                       "rtnl: received nexthop message with invalid alignment, ignoring.");
 
-        SET_FOREACH(nexthop, link->nexthops) {
-                /* do not touch nexthop created by the kernel */
-                if (nexthop->protocol == RTPROT_KERNEL)
-                        continue;
+        n_group = size / sizeof(struct nexthop_grp);
+        for (size_t i = 0; i < n_group; i++) {
+                _cleanup_free_ struct nexthop_grp *nhg = NULL;
 
-                /* Do not touch addresses managed by kernel or other tools. */
-                if (nexthop->source == NETWORK_CONFIG_SOURCE_FOREIGN)
+                if (group[i].id == 0) {
+                        log_debug("rtnl: received nexthop message with invalid ID in group, ignoring.");
                         continue;
+                }
 
-                /* Ignore nexthops not assigned yet or already removing. */
-                if (!nexthop_exists(nexthop))
+                if (group[i].weight > 254) {
+                        log_debug("rtnl: received nexthop message with invalid weight in group, ignoring.");
                         continue;
+                }
 
-                RET_GATHER(r, nexthop_remove(nexthop));
-        }
-
-        manager_mark_nexthops(link->manager, /* foreign = */ false, link);
-
-        return RET_GATHER(r, manager_drop_marked_nexthops(link->manager));
-}
-
-void link_foreignize_nexthops(Link *link) {
-        NextHop *nexthop;
-
-        assert(link);
-
-        SET_FOREACH(nexthop, link->nexthops)
-                nexthop->source = NETWORK_CONFIG_SOURCE_FOREIGN;
-
-        manager_mark_nexthops(link->manager, /* foreign = */ false, link);
+                nhg = newdup(struct nexthop_grp, group + i, 1);
+                if (!nhg)
+                        return log_oom();
 
-        SET_FOREACH(nexthop, link->manager->nexthops) {
-                if (!nexthop_is_marked(nexthop))
+                r = hashmap_ensure_put(&h, NULL, UINT32_TO_PTR(nhg->id), nhg);
+                if (r == -ENOMEM)
+                        return log_oom();
+                if (r < 0) {
+                        log_debug_errno(r, "Failed to store nexthop group, ignoring: %m");
                         continue;
-
-                nexthop->source = NETWORK_CONFIG_SOURCE_FOREIGN;
+                }
+                if (r > 0)
+                        TAKE_PTR(nhg);
         }
+
+        hashmap_free_free(nexthop->group);
+        nexthop->group = TAKE_PTR(h);
+        return 0;
 }
 
 int manager_rtnl_process_nexthop(sd_netlink *rtnl, sd_netlink_message *message, Manager *m) {
-        _cleanup_(nexthop_freep) NextHop *tmp = NULL;
         _cleanup_free_ void *raw_group = NULL;
-        NextHop *nexthop = NULL;
         size_t raw_group_size;
-        uint32_t ifindex;
         uint16_t type;
-        Link *link = NULL;
+        uint32_t id, ifindex;
+        NextHop *nexthop = NULL;
+        Request *req = NULL;
+        bool is_new = false;
         int r;
 
         assert(rtnl);
@@ -824,163 +894,107 @@ int manager_rtnl_process_nexthop(sd_netlink *rtnl, sd_netlink_message *message,
                 return 0;
         }
 
-        r = sd_netlink_message_read_u32(message, NHA_OIF, &ifindex);
-        if (r < 0 && r != -ENODATA) {
-                log_warning_errno(r, "rtnl: could not get NHA_OIF attribute, ignoring: %m");
+        r = sd_netlink_message_read_u32(message, NHA_ID, &id);
+        if (r == -ENODATA) {
+                log_warning_errno(r, "rtnl: received nexthop message without NHA_ID attribute, ignoring: %m");
                 return 0;
-        } else if (r >= 0) {
-                if (ifindex <= 0) {
-                        log_warning("rtnl: received nexthop message with invalid ifindex %"PRIu32", ignoring.", ifindex);
-                        return 0;
-                }
-
-                r = link_get_by_index(m, ifindex, &link);
-                if (r < 0) {
-                        if (!m->enumerating)
-                                log_warning("rtnl: received nexthop message for link (%"PRIu32") we do not know about, ignoring", ifindex);
-                        return 0;
-                }
-        }
-
-        r = nexthop_new(&tmp);
-        if (r < 0)
-                return log_oom();
-
-        r = sd_rtnl_message_get_family(message, &tmp->family);
-        if (r < 0) {
-                log_link_warning_errno(link, r, "rtnl: could not get nexthop family, ignoring: %m");
+        } else if (r < 0) {
+                log_warning_errno(r, "rtnl: could not get NHA_ID attribute, ignoring: %m");
                 return 0;
-        } else if (!IN_SET(tmp->family, AF_UNSPEC, AF_INET, AF_INET6)) {
-                log_link_debug(link, "rtnl: received nexthop message with invalid family %d, ignoring.", tmp->family);
+        } else if (id == 0) {
+                log_warning("rtnl: received nexthop message with invalid nexthop ID, ignoring: %m");
                 return 0;
         }
 
-        r = sd_rtnl_message_nexthop_get_protocol(message, &tmp->protocol);
-        if (r < 0) {
-                log_link_warning_errno(link, r, "rtnl: could not get nexthop protocol, ignoring: %m");
-                return 0;
-        }
+        (void) nexthop_get_by_id(m, id, &nexthop);
+        (void) nexthop_get_request_by_id(m, id, &req);
 
-        r = sd_rtnl_message_nexthop_get_flags(message, &tmp->flags);
-        if (r < 0) {
-                log_link_warning_errno(link, r, "rtnl: could not get nexthop flags, ignoring: %m");
-                return 0;
-        }
+        if (type == RTM_DELNEXTHOP) {
+                if (nexthop) {
+                        nexthop_enter_removed(nexthop);
+                        log_nexthop_debug(nexthop, "Forgetting removed", m);
+                        nexthop_free(nexthop);
+                } else
+                        log_nexthop_debug(&(const NextHop) { .id = id }, "Kernel removed unknown", m);
+
+                if (req)
+                        nexthop_enter_removed(req->userdata);
 
-        r = sd_netlink_message_read_data(message, NHA_GROUP, &raw_group_size, &raw_group);
-        if (r < 0 && r != -ENODATA) {
-                log_link_warning_errno(link, r, "rtnl: could not get NHA_GROUP attribute, ignoring: %m");
                 return 0;
-        } else if (r >= 0) {
-                struct nexthop_grp *group = raw_group;
-                size_t n_group;
+        }
 
-                if (raw_group_size == 0 || raw_group_size % sizeof(struct nexthop_grp) != 0) {
-                        log_link_warning(link, "rtnl: received nexthop message with invalid nexthop group size, ignoring.");
+        /* If we did not know the nexthop, then save it. */
+        if (!nexthop) {
+                r = nexthop_add_new(m, id, &nexthop);
+                if (r < 0) {
+                        log_warning_errno(r, "Failed to add received nexthop, ignoring: %m");
                         return 0;
                 }
 
-                assert((uintptr_t) group % alignof(struct nexthop_grp) == 0);
+                is_new = true;
+        }
 
-                n_group = raw_group_size / sizeof(struct nexthop_grp);
-                for (size_t i = 0; i < n_group; i++) {
-                        _cleanup_free_ struct nexthop_grp *nhg = NULL;
+        /* Also update information that cannot be obtained through netlink notification. */
+        if (req && req->waiting_reply) {
+                NextHop *n = ASSERT_PTR(req->userdata);
 
-                        if (group[i].id == 0) {
-                                log_link_warning(link, "rtnl: received nexthop message with invalid ID in group, ignoring.");
-                                return 0;
-                        }
-                        if (group[i].weight > 254) {
-                                log_link_warning(link, "rtnl: received nexthop message with invalid weight in group, ignoring.");
-                                return 0;
-                        }
+                nexthop->source = n->source;
+        }
 
-                        nhg = newdup(struct nexthop_grp, group + i, 1);
-                        if (!nhg)
-                                return log_oom();
+        r = sd_rtnl_message_get_family(message, &nexthop->family);
+        if (r < 0)
+                log_debug_errno(r, "rtnl: could not get nexthop family, ignoring: %m");
 
-                        r = hashmap_ensure_put(&tmp->group, NULL, UINT32_TO_PTR(nhg->id), nhg);
-                        if (r == -ENOMEM)
-                                return log_oom();
-                        if (r < 0) {
-                                log_link_warning_errno(link, r, "Failed to store nexthop group, ignoring: %m");
-                                return 0;
-                        }
-                        if (r > 0)
-                                TAKE_PTR(nhg);
-                }
-        }
+        r = sd_rtnl_message_nexthop_get_protocol(message, &nexthop->protocol);
+        if (r < 0)
+                log_debug_errno(r, "rtnl: could not get nexthop protocol, ignoring: %m");
 
-        if (tmp->family != AF_UNSPEC) {
-                r = netlink_message_read_in_addr_union(message, NHA_GATEWAY, tmp->family, &tmp->gw);
-                if (r < 0 && r != -ENODATA) {
-                        log_link_warning_errno(link, r, "rtnl: could not get NHA_GATEWAY attribute, ignoring: %m");
-                        return 0;
-                }
+        r = sd_rtnl_message_nexthop_get_flags(message, &nexthop->flags);
+        if (r < 0)
+                log_debug_errno(r, "rtnl: could not get nexthop flags, ignoring: %m");
+
+        r = sd_netlink_message_read_data(message, NHA_GROUP, &raw_group_size, &raw_group);
+        if (r == -ENODATA)
+                nexthop->group = hashmap_free_free(nexthop->group);
+        else if (r < 0)
+                log_debug_errno(r, "rtnl: could not get NHA_GROUP attribute, ignoring: %m");
+        else
+                (void) nexthop_update_group(nexthop, raw_group, raw_group_size);
+
+        if (nexthop->family != AF_UNSPEC) {
+                r = netlink_message_read_in_addr_union(message, NHA_GATEWAY, nexthop->family, &nexthop->gw);
+                if (r == -ENODATA)
+                        nexthop->gw = IN_ADDR_NULL;
+                else if (r < 0)
+                        log_debug_errno(r, "rtnl: could not get NHA_GATEWAY attribute, ignoring: %m");
         }
 
         r = sd_netlink_message_has_flag(message, NHA_BLACKHOLE);
-        if (r < 0) {
-                log_link_warning_errno(link, r, "rtnl: could not get NHA_BLACKHOLE attribute, ignoring: %m");
-                return 0;
-        }
-        tmp->blackhole = r;
+        if (r < 0)
+                log_debug_errno(r, "rtnl: could not get NHA_BLACKHOLE attribute, ignoring: %m");
+        else
+                nexthop->blackhole = r;
 
-        r = sd_netlink_message_read_u32(message, NHA_ID, &tmp->id);
-        if (r == -ENODATA) {
-                log_link_warning_errno(link, r, "rtnl: received nexthop message without NHA_ID attribute, ignoring: %m");
-                return 0;
-        } else if (r < 0) {
-                log_link_warning_errno(link, r, "rtnl: could not get NHA_ID attribute, ignoring: %m");
-                return 0;
-        } else if (tmp->id == 0) {
-                log_link_warning(link, "rtnl: received nexthop message with invalid nexthop ID, ignoring: %m");
-                return 0;
-        }
+        r = sd_netlink_message_read_u32(message, NHA_OIF, &ifindex);
+        if (r == -ENODATA)
+                nexthop->ifindex = 0;
+        else if (r < 0)
+                log_debug_errno(r, "rtnl: could not get NHA_OIF attribute, ignoring: %m");
+        else if (ifindex > INT32_MAX)
+                log_debug_errno(r, "rtnl: received invalid NHA_OIF attribute, ignoring: %m");
+        else
+                nexthop->ifindex = (int) ifindex;
 
         /* All blackhole or group nexthops are managed by Manager. Note that the linux kernel does not
          * set NHA_OID attribute when NHA_BLACKHOLE or NHA_GROUP is set. Just for safety. */
-        if (!nexthop_owned_by_link(tmp))
-                link = NULL;
+        if (!nexthop_bound_to_link(nexthop))
+                nexthop->ifindex = 0;
 
-        (void) nexthop_get(m, link, tmp, &nexthop);
-
-        switch (type) {
-        case RTM_NEWNEXTHOP:
-                if (nexthop) {
-                        nexthop->flags = tmp->flags;
-                        nexthop_enter_configured(nexthop);
-                        log_nexthop_debug(tmp, "Received remembered", link);
-                } else {
-                        nexthop_enter_configured(tmp);
-                        log_nexthop_debug(tmp, "Remembering", link);
-
-                        r = nexthop_add(m, link, tmp);
-                        if (r < 0) {
-                                log_link_warning_errno(link, r, "Could not remember foreign nexthop, ignoring: %m");
-                                return 0;
-                        }
-
-                        TAKE_PTR(tmp);
-                }
-
-                break;
-        case RTM_DELNEXTHOP:
-                if (nexthop) {
-                        nexthop_enter_removed(nexthop);
-                        if (nexthop->state == 0) {
-                                log_nexthop_debug(nexthop, "Forgetting", link);
-                                nexthop_free(nexthop);
-                        } else
-                                log_nexthop_debug(nexthop, "Removed", link);
-                } else
-                        log_nexthop_debug(tmp, "Kernel removed unknown", link);
-                break;
-
-        default:
-                assert_not_reached();
-        }
+        nexthop_enter_configured(nexthop);
+        if (req)
+                nexthop_enter_configured(req->userdata);
 
+        log_nexthop_debug(nexthop, is_new ? "Remembering" : "Received remembered", m);
         return 1;
 }
 
@@ -988,6 +1002,13 @@ static int nexthop_section_verify(NextHop *nh) {
         if (section_is_invalid(nh->section))
                 return -EINVAL;
 
+        if (!nh->network->manager->manage_foreign_nexthops && nh->id == 0)
+                return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
+                                         "%s: [NextHop] section without specifying Id= is not supported "
+                                         "if ManageForeignNextHops=no is set in networkd.conf. "
+                                         "Ignoring [NextHop] section from line %u.",
+                                         nh->section->filename, nh->section->line);
+
         if (!hashmap_isempty(nh->group)) {
                 if (in_addr_is_set(nh->family, &nh->gw))
                         return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
index 6f2aa6f6dc3f44a8e7b44f478d623649ebbba601..c1d39e6e8ea9b5115d45ea203491a952969aeaae 100644 (file)
@@ -20,13 +20,12 @@ typedef struct Network Network;
 typedef struct NextHop {
         Network *network;
         Manager *manager;
-        Link *link;
         ConfigSection *section;
         NetworkConfigSource source;
         NetworkConfigState state;
 
         uint8_t protocol;
-
+        int ifindex;
         uint32_t id;
         bool blackhole;
         int family;
@@ -40,13 +39,18 @@ NextHop *nexthop_free(NextHop *nexthop);
 
 void network_drop_invalid_nexthops(Network *network);
 
-int link_drop_managed_nexthops(Link *link);
-int link_drop_foreign_nexthops(Link *link);
+int link_drop_nexthops(Link *link, bool foreign);
+static inline int link_drop_foreign_nexthops(Link *link) {
+        return link_drop_nexthops(link, /* foreign = */ true);
+}
+static inline int link_drop_managed_nexthops(Link *link) {
+        return link_drop_nexthops(link, /* foreign = */ false);
+}
 void link_foreignize_nexthops(Link *link);
 
 int link_request_static_nexthops(Link *link, bool only_ipv4);
 
-int manager_get_nexthop_by_id(Manager *manager, uint32_t id, NextHop **ret);
+int nexthop_get_by_id(Manager *manager, uint32_t id, NextHop **ret);
 int manager_rtnl_process_nexthop(sd_netlink *rtnl, sd_netlink_message *message, Manager *m);
 
 DEFINE_NETWORK_CONFIG_STATE_FUNCTIONS(NextHop, nexthop);
index 0d782dc9a7a98ec78e2eb8d5cb1b3a9af7097077..a88418e28e17a8f43c9d98484e82307570466f57 100644 (file)
@@ -58,12 +58,14 @@ static void request_hash_func(const Request *req, struct siphash *state) {
         assert(req);
         assert(state);
 
-        siphash24_compress_boolean(req->link, state);
-        if (req->link)
-                siphash24_compress(&req->link->ifindex, sizeof(req->link->ifindex), state);
-
         siphash24_compress(&req->type, sizeof(req->type), state);
 
+        if (req->type != REQUEST_TYPE_NEXTHOP) {
+                siphash24_compress_boolean(req->link, state);
+                if (req->link)
+                        siphash24_compress(&req->link->ifindex, sizeof(req->link->ifindex), state);
+        }
+
         siphash24_compress(&req->hash_func, sizeof(req->hash_func), state);
         siphash24_compress(&req->compare_func, sizeof(req->compare_func), state);
 
@@ -77,19 +79,21 @@ static int request_compare_func(const struct Request *a, const struct Request *b
         assert(a);
         assert(b);
 
-        r = CMP(!!a->link, !!b->link);
+        r = CMP(a->type, b->type);
         if (r != 0)
                 return r;
 
-        if (a->link) {
-                r = CMP(a->link->ifindex, b->link->ifindex);
+        if (a->type != REQUEST_TYPE_NEXTHOP) {
+                r = CMP(!!a->link, !!b->link);
                 if (r != 0)
                         return r;
-        }
 
-        r = CMP(a->type, b->type);
-        if (r != 0)
-                return r;
+                if (a->link) {
+                        r = CMP(a->link->ifindex, b->link->ifindex);
+                        if (r != 0)
+                                return r;
+                }
+        }
 
         r = CMP(PTR_TO_UINT64(a->hash_func), PTR_TO_UINT64(b->hash_func));
         if (r != 0)
index 7218d799fc842098e9b19499a3ce7e4344e02676..c0189ae899a85abab2bd4bec9c8c68fcfa36d458 100644 (file)
@@ -461,7 +461,7 @@ static int route_convert(Manager *manager, const Route *route, ConvertedRoutes *
                 struct nexthop_grp *nhg;
                 NextHop *nh;
 
-                r = manager_get_nexthop_by_id(manager, route->nexthop_id, &nh);
+                r = nexthop_get_by_id(manager, route->nexthop_id, &nh);
                 if (r < 0)
                         return r;
 
@@ -475,7 +475,7 @@ static int route_convert(Manager *manager, const Route *route, ConvertedRoutes *
                                 return r;
 
                         route_apply_nexthop(c->routes[0], nh, UINT8_MAX);
-                        c->links[0] = nh->link;
+                        (void) link_get_by_index(manager, nh->ifindex, c->links);
 
                         *ret = TAKE_PTR(c);
                         return 1;
@@ -489,7 +489,7 @@ static int route_convert(Manager *manager, const Route *route, ConvertedRoutes *
                 HASHMAP_FOREACH(nhg, nh->group) {
                         NextHop *h;
 
-                        r = manager_get_nexthop_by_id(manager, nhg->id, &h);
+                        r = nexthop_get_by_id(manager, nhg->id, &h);
                         if (r < 0)
                                 return r;
 
@@ -498,7 +498,7 @@ static int route_convert(Manager *manager, const Route *route, ConvertedRoutes *
                                 return r;
 
                         route_apply_nexthop(c->routes[i], h, nhg->weight);
-                        c->links[i] = h->link;
+                        (void) link_get_by_index(manager, h->ifindex, c->links + i);
 
                         i++;
                 }
@@ -1284,7 +1284,7 @@ static int route_is_ready_to_configure(const Route *route, Link *link) {
                 struct nexthop_grp *nhg;
                 NextHop *nh;
 
-                if (manager_get_nexthop_by_id(link->manager, route->nexthop_id, &nh) < 0)
+                if (nexthop_get_by_id(link->manager, route->nexthop_id, &nh) < 0)
                         return false;
 
                 if (!nexthop_exists(nh))
@@ -1293,7 +1293,7 @@ static int route_is_ready_to_configure(const Route *route, Link *link) {
                 HASHMAP_FOREACH(nhg, nh->group) {
                         NextHop *g;
 
-                        if (manager_get_nexthop_by_id(link->manager, nhg->id, &g) < 0)
+                        if (nexthop_get_by_id(link->manager, nhg->id, &g) < 0)
                                 return false;
 
                         if (!nexthop_exists(g))
index 2b37c86d2351a791aca2cf26d12a01f2579c725f..38f90969ec498dffcf9a0c6d337efb25d7594f6b 100644 (file)
@@ -563,12 +563,20 @@ static int link_is_ready_to_set_link(Link *link, Request *req) {
                 break;
         }
         case REQUEST_TYPE_SET_LINK_MTU: {
-                Request req_ipoib = {
-                        .link = link,
-                        .type = REQUEST_TYPE_SET_LINK_IPOIB,
-                };
+                if (ordered_set_contains(link->manager->request_queue,
+                                         &(const Request) {
+                                                 .link = link,
+                                                 .type = REQUEST_TYPE_SET_LINK_IPOIB,
+                                         }))
+                        return false;
 
-                return !ordered_set_contains(link->manager->request_queue, &req_ipoib);
+                /* Changing FD mode may affect MTU. */
+                if (ordered_set_contains(link->manager->request_queue,
+                                         &(const Request) {
+                                                 .link = link,
+                                                 .type = REQUEST_TYPE_SET_LINK_CAN,
+                                         }))
+                        return false;
         }
         default:
                 break;
@@ -858,7 +866,7 @@ int link_request_to_set_master(Link *link) {
 
 int link_request_to_set_mtu(Link *link, uint32_t mtu) {
         const char *origin;
-        uint32_t min_mtu;
+        uint32_t min_mtu, max_mtu;
         Request *req;
         int r;
 
@@ -886,10 +894,19 @@ int link_request_to_set_mtu(Link *link, uint32_t mtu) {
                 mtu = min_mtu;
         }
 
-        if (mtu > link->max_mtu) {
+        max_mtu = link->max_mtu;
+        if (link->iftype == ARPHRD_CAN)
+                /* The maximum MTU may be changed when FD mode is changed.
+                 * See https://docs.kernel.org/networking/can.html#can-fd-flexible-data-rate-driver-support
+                 *   MTU = 16 (CAN_MTU)   => Classical CAN device
+                 *   MTU = 72 (CANFD_MTU) => CAN FD capable device
+                 * So, even if the current maximum is 16, we should not reduce the requested value now. */
+                max_mtu = MAX(max_mtu, 72u);
+
+        if (mtu > max_mtu) {
                 log_link_warning(link, "Reducing the requested MTU %"PRIu32" to the interface's maximum MTU %"PRIu32".",
-                                 mtu, link->max_mtu);
-                mtu = link->max_mtu;
+                                 mtu, max_mtu);
+                mtu = max_mtu;
         }
 
         if (link->mtu == mtu)
index 2ac6c3527bc288f882a81e167e95a405d0da729e..2b226b2e2a1836cedd969917de85973c4832f272 100644 (file)
@@ -2,6 +2,7 @@
 
 #include <netinet/in.h>
 #include <linux/if.h>
+#include <linux/if_arp.h>
 
 #include "missing_network.h"
 #include "networkd-link.h"
 #include "string-table.h"
 #include "sysctl-util.h"
 
-static int link_update_ipv6_sysctl(Link *link) {
+static bool link_is_configured_for_family(Link *link, int family) {
         assert(link);
 
+        if (!link->network)
+                return false;
+
         if (link->flags & IFF_LOOPBACK)
+                return false;
+
+        /* CAN devices do not support IP layer. Most of the functions below are never called for CAN devices,
+         * but link_set_ipv6_mtu() may be called after setting interface MTU, and warn about the failure. For
+         * safety, let's unconditionally check if the interface is not a CAN device. */
+        if (IN_SET(family, AF_INET, AF_INET6) && link->iftype == ARPHRD_CAN)
+                return false;
+
+        if (family == AF_INET6 && !socket_ipv6_is_supported())
+                return false;
+
+        return true;
+}
+
+static int link_update_ipv6_sysctl(Link *link) {
+        assert(link);
+
+        if (!link_is_configured_for_family(link, AF_INET6))
                 return 0;
 
         if (!link_ipv6_enabled(link))
@@ -27,10 +49,7 @@ static int link_update_ipv6_sysctl(Link *link) {
 static int link_set_proxy_arp(Link *link) {
         assert(link);
 
-        if (link->flags & IFF_LOOPBACK)
-                return 0;
-
-        if (!link->network)
+        if (!link_is_configured_for_family(link, AF_INET))
                 return 0;
 
         if (link->network->proxy_arp < 0)
@@ -43,13 +62,7 @@ static bool link_ip_forward_enabled(Link *link, int family) {
         assert(link);
         assert(IN_SET(family, AF_INET, AF_INET6));
 
-        if (family == AF_INET6 && !socket_ipv6_is_supported())
-                return false;
-
-        if (link->flags & IFF_LOOPBACK)
-                return false;
-
-        if (!link->network)
+        if (!link_is_configured_for_family(link, family))
                 return false;
 
         return link->network->ip_forward & (family == AF_INET ? ADDRESS_FAMILY_IPV4 : ADDRESS_FAMILY_IPV6);
@@ -92,10 +105,7 @@ static int link_set_ipv6_forward(Link *link) {
 static int link_set_ipv4_rp_filter(Link *link) {
         assert(link);
 
-        if (link->flags & IFF_LOOPBACK)
-                return 0;
-
-        if (!link->network)
+        if (!link_is_configured_for_family(link, AF_INET))
                 return 0;
 
         if (link->network->ipv4_rp_filter < 0)
@@ -110,13 +120,7 @@ static int link_set_ipv6_privacy_extensions(Link *link) {
         assert(link);
         assert(link->manager);
 
-        if (!socket_ipv6_is_supported())
-                return 0;
-
-        if (link->flags & IFF_LOOPBACK)
-                return 0;
-
-        if (!link->network)
+        if (!link_is_configured_for_family(link, AF_INET6))
                 return 0;
 
         val = link->network->ipv6_privacy_extensions;
@@ -133,14 +137,7 @@ static int link_set_ipv6_privacy_extensions(Link *link) {
 static int link_set_ipv6_accept_ra(Link *link) {
         assert(link);
 
-        /* Make this a NOP if IPv6 is not available */
-        if (!socket_ipv6_is_supported())
-                return 0;
-
-        if (link->flags & IFF_LOOPBACK)
-                return 0;
-
-        if (!link->network)
+        if (!link_is_configured_for_family(link, AF_INET6))
                 return 0;
 
         return sysctl_write_ip_property(AF_INET6, link->ifname, "accept_ra", "0");
@@ -149,14 +146,7 @@ static int link_set_ipv6_accept_ra(Link *link) {
 static int link_set_ipv6_dad_transmits(Link *link) {
         assert(link);
 
-        /* Make this a NOP if IPv6 is not available */
-        if (!socket_ipv6_is_supported())
-                return 0;
-
-        if (link->flags & IFF_LOOPBACK)
-                return 0;
-
-        if (!link->network)
+        if (!link_is_configured_for_family(link, AF_INET6))
                 return 0;
 
         if (link->network->ipv6_dad_transmits < 0)
@@ -168,14 +158,7 @@ static int link_set_ipv6_dad_transmits(Link *link) {
 static int link_set_ipv6_hop_limit(Link *link) {
         assert(link);
 
-        /* Make this a NOP if IPv6 is not available */
-        if (!socket_ipv6_is_supported())
-                return 0;
-
-        if (link->flags & IFF_LOOPBACK)
-                return 0;
-
-        if (!link->network)
+        if (!link_is_configured_for_family(link, AF_INET6))
                 return 0;
 
         if (link->network->ipv6_hop_limit <= 0)
@@ -189,13 +172,7 @@ static int link_set_ipv6_proxy_ndp(Link *link) {
 
         assert(link);
 
-        if (!socket_ipv6_is_supported())
-                return 0;
-
-        if (link->flags & IFF_LOOPBACK)
-                return 0;
-
-        if (!link->network)
+        if (!link_is_configured_for_family(link, AF_INET6))
                 return 0;
 
         if (link->network->ipv6_proxy_ndp >= 0)
@@ -211,14 +188,7 @@ int link_set_ipv6_mtu(Link *link) {
 
         assert(link);
 
-        /* Make this a NOP if IPv6 is not available */
-        if (!socket_ipv6_is_supported())
-                return 0;
-
-        if (link->flags & IFF_LOOPBACK)
-                return 0;
-
-        if (!link->network)
+        if (!link_is_configured_for_family(link, AF_INET6))
                 return 0;
 
         if (link->network->ipv6_mtu == 0)
@@ -237,7 +207,7 @@ int link_set_ipv6_mtu(Link *link) {
 static int link_set_ipv4_accept_local(Link *link) {
         assert(link);
 
-        if (link->flags & IFF_LOOPBACK)
+        if (!link_is_configured_for_family(link, AF_INET))
                 return 0;
 
         if (link->network->ipv4_accept_local < 0)
@@ -249,7 +219,7 @@ static int link_set_ipv4_accept_local(Link *link) {
 static int link_set_ipv4_route_localnet(Link *link) {
         assert(link);
 
-        if (link->flags & IFF_LOOPBACK)
+        if (!link_is_configured_for_family(link, AF_INET))
                 return 0;
 
         if (link->network->ipv4_route_localnet < 0)
@@ -258,6 +228,20 @@ static int link_set_ipv4_route_localnet(Link *link) {
         return sysctl_write_ip_property_boolean(AF_INET, link->ifname, "route_localnet", link->network->ipv4_route_localnet > 0);
 }
 
+static int link_set_ipv4_promote_secondaries(Link *link) {
+        assert(link);
+
+        if (!link_is_configured_for_family(link, AF_INET))
+                return 0;
+
+        /* If promote_secondaries is not set, DHCP will work only as long as the IP address does not
+         * changes between leases. The kernel will remove all secondary IP addresses of an interface
+         * otherwise. The way systemd-networkd works is that the new IP of a lease is added as a
+         * secondary IP and when the primary one expires it relies on the kernel to promote the
+         * secondary IP. See also https://github.com/systemd/systemd/issues/7163 */
+        return sysctl_write_ip_property_boolean(AF_INET, link->ifname, "promote_secondaries", true);
+}
+
 int link_set_sysctl(Link *link) {
         int r;
 
@@ -321,12 +305,7 @@ int link_set_sysctl(Link *link) {
         if (r < 0)
                 log_link_warning_errno(link, r, "Cannot set IPv4 reverse path filtering for interface, ignoring: %m");
 
-        /* If promote_secondaries is not set, DHCP will work only as long as the IP address does not
-         * changes between leases. The kernel will remove all secondary IP addresses of an interface
-         * otherwise. The way systemd-networkd works is that the new IP of a lease is added as a
-         * secondary IP and when the primary one expires it relies on the kernel to promote the
-         * secondary IP. See also https://github.com/systemd/systemd/issues/7163 */
-        r = sysctl_write_ip_property_boolean(AF_INET, link->ifname, "promote_secondaries", true);
+        r = link_set_ipv4_promote_secondaries(link);
         if (r < 0)
                 log_link_warning_errno(link, r, "Cannot enable promote_secondaries for interface, ignoring: %m");
 
index e5a5e8892621a11b66a6ab18504992511db6bf58..2994b8b70c1c14e8341810484cda81c9ad4711fc 100644 (file)
@@ -21,6 +21,7 @@
 #SpeedMeterIntervalSec=10sec
 #ManageForeignRoutingPolicyRules=yes
 #ManageForeignRoutes=yes
+#ManageForeignNextHops=yes
 #RouteTable=
 #IPv6PrivacyExtensions=no
 
index a9d36627a86bed8eee47bf2e081425dc35c44c39..a5002437c6f7914d5b892e70afa0d2c5b06bc9d1 100644 (file)
@@ -35,6 +35,8 @@ static int chown_cgroup_path(const char *path, uid_t uid_shift) {
                        "cgroup.stat",
                        "cgroup.subtree_control",
                        "cgroup.threads",
+                       "memory.oom.group",
+                       "memory.reclaim",
                        "notify_on_release",
                        "tasks")
                 if (fchownat(fd, fn, uid_shift, uid_shift, 0) < 0)
index c661f1d9e0b271268a2c1be4e4ce3aa648b4a9ad..369d8742b60f0be1da0a9041d2ea29b4171ed0ea 100644 (file)
@@ -95,9 +95,7 @@ static int generate_mac(
         assert_cc(ETH_ALEN <= sizeof(result));
         memcpy(mac->ether_addr_octet, &result, ETH_ALEN);
 
-        /* see eth_random_addr in the kernel */
-        mac->ether_addr_octet[0] &= 0xfe;        /* clear multicast bit */
-        mac->ether_addr_octet[0] |= 0x02;        /* set local assignment bit (IEEE802) */
+        ether_addr_mark_random(mac);
 
         return 0;
 }
index 66962d7ba958f37edc5646bec95543c711fff10e..27400aa4a21fba72faaea081b34b279d4b75c069 100644 (file)
@@ -368,7 +368,7 @@ int allocate_scope(
         if (r < 0)
                 return bus_log_parse_error(r);
 
-        r = bus_wait_for_jobs_one(w, object, false, NULL);
+        r = bus_wait_for_jobs_one(w, object, BUS_WAIT_JOBS_LOG_ERROR, NULL);
         if (r < 0)
                 return r;
 
index 6ab604d3dcca5357524f694a93816ce2c65429db..b4a74a1e4375052f8f8e9ec59583ec001315a4b6 100644 (file)
@@ -229,8 +229,7 @@ static DeviceNode* arg_extra_nodes = NULL;
 static size_t arg_n_extra_nodes = 0;
 static char **arg_sysctl = NULL;
 static ConsoleMode arg_console_mode = _CONSOLE_MODE_INVALID;
-static MachineCredential *arg_credentials = NULL;
-static size_t arg_n_credentials = 0;
+static MachineCredentialContext arg_credentials = {};
 static char **arg_bind_user = NULL;
 static bool arg_suppress_sync = false;
 static char *arg_settings_filename = NULL;
@@ -266,6 +265,7 @@ STATIC_DESTRUCTOR_REGISTER(arg_syscall_deny_list, strv_freep);
 #if HAVE_SECCOMP
 STATIC_DESTRUCTOR_REGISTER(arg_seccomp, seccomp_releasep);
 #endif
+STATIC_DESTRUCTOR_REGISTER(arg_credentials, machine_credential_context_done);
 STATIC_DESTRUCTOR_REGISTER(arg_cpu_set, cpu_set_reset);
 STATIC_DESTRUCTOR_REGISTER(arg_sysctl, strv_freep);
 STATIC_DESTRUCTOR_REGISTER(arg_bind_user, strv_freep);
@@ -1568,7 +1568,7 @@ static int parse_argv(int argc, char *argv[]) {
                         break;
 
                 case ARG_SET_CREDENTIAL:
-                        r = machine_credential_set(&arg_credentials, &arg_n_credentials, optarg);
+                        r = machine_credential_set(&arg_credentials, optarg);
                         if (r < 0)
                                 return r;
 
@@ -1576,7 +1576,7 @@ static int parse_argv(int argc, char *argv[]) {
                         break;
 
                 case ARG_LOAD_CREDENTIAL:
-                        r = machine_credential_load(&arg_credentials, &arg_n_credentials, optarg);
+                        r = machine_credential_load(&arg_credentials, optarg);
                         if (r < 0)
                                 return r;
 
@@ -2182,7 +2182,7 @@ static int copy_devnodes(const char *dest) {
                         if (mknod(to, st.st_mode, st.st_rdev) < 0) {
                                 /* Explicitly warn the user when /dev is already populated. */
                                 if (errno == EEXIST)
-                                        log_notice("%s/dev is pre-mounted and pre-populated. If a pre-mounted /dev is provided it needs to be an unpopulated file system.", dest);
+                                        log_notice("%s/dev/ is pre-mounted and pre-populated. If a pre-mounted /dev/ is provided it needs to be an unpopulated file system.", dest);
                                 if (errno != EPERM)
                                         return log_error_errno(errno, "mknod(%s) failed: %m", to);
 
@@ -2367,7 +2367,7 @@ static int setup_credentials(const char *root) {
         const char *q;
         int r;
 
-        if (arg_n_credentials <= 0)
+        if (arg_credentials.n_credentials == 0)
                 return 0;
 
         r = userns_mkdir(root, "/run/host", 0755, 0, 0);
@@ -2383,11 +2383,11 @@ static int setup_credentials(const char *root) {
         if (r < 0)
                 return r;
 
-        for (size_t i = 0; i < arg_n_credentials; i++) {
+        FOREACH_ARRAY(cred, arg_credentials.credentials, arg_credentials.n_credentials) {
                 _cleanup_free_ char *j = NULL;
                 _cleanup_close_ int fd = -EBADF;
 
-                j = path_join(q, arg_credentials[i].id);
+                j = path_join(q, cred->id);
                 if (!j)
                         return log_oom();
 
@@ -2395,7 +2395,7 @@ static int setup_credentials(const char *root) {
                 if (fd < 0)
                         return log_error_errno(errno, "Failed to create credential file %s: %m", j);
 
-                r = loop_write(fd, arg_credentials[i].data, arg_credentials[i].size);
+                r = loop_write(fd, cred->data, cred->size);
                 if (r < 0)
                         return log_error_errno(r, "Failed to write credential to file %s: %m", j);
 
@@ -3394,7 +3394,7 @@ static int inner_child(
         if (asprintf(envp + n_env++, "container_uuid=%s", SD_ID128_TO_UUID_STRING(arg_uuid)) < 0)
                 return log_oom();
 
-        if (fdset_size(fds) > 0) {
+        if (!fdset_isempty(fds)) {
                 r = fdset_cloexec(fds, false);
                 if (r < 0)
                         return log_error_errno(r, "Failed to unset O_CLOEXEC for file descriptors.");
@@ -3406,7 +3406,7 @@ static int inner_child(
         if (asprintf(envp + n_env++, "NOTIFY_SOCKET=%s", NSPAWN_NOTIFY_SOCKET_PATH) < 0)
                 return log_oom();
 
-        if (arg_n_credentials > 0) {
+        if (arg_credentials.n_credentials > 0) {
                 envp[n_env] = strdup("CREDENTIALS_DIRECTORY=/run/host/credentials");
                 if (!envp[n_env])
                         return log_oom();
@@ -5286,7 +5286,7 @@ static int initialize_rlimits(void) {
                  * don't read the other limits from PID 1 but prefer the static table above. */
         };
 
-        int rl;
+        int rl, r;
 
         for (rl = 0; rl < _RLIMIT_MAX; rl++) {
                 /* Let's only fill in what the user hasn't explicitly configured anyway */
@@ -5297,8 +5297,9 @@ static int initialize_rlimits(void) {
                         if (IN_SET(rl, RLIMIT_NPROC, RLIMIT_SIGPENDING)) {
                                 /* For these two let's read the limits off PID 1. See above for an explanation. */
 
-                                if (prlimit(1, rl, NULL, &buffer) < 0)
-                                        return log_error_errno(errno, "Failed to read resource limit RLIMIT_%s of PID 1: %m", rlimit_to_string(rl));
+                                r = pid_getrlimit(1, rl, &buffer);
+                                if (r < 0)
+                                        return log_error_errno(r, "Failed to read resource limit RLIMIT_%s of PID 1: %m", rlimit_to_string(rl));
 
                                 v = &buffer;
                         } else if (rl == RLIMIT_NOFILE) {
@@ -5643,7 +5644,7 @@ static int run(int argc, char *argv[]) {
                         /* Always take an exclusive lock on our own ephemeral copy. */
                         r = image_path_lock(np, LOCK_EX|LOCK_NB, &tree_global_lock, &tree_local_lock);
                         if (r < 0) {
-                                r = log_error_errno(r, "Failed to create image lock: %m");
+                                log_error_errno(r, "Failed to create image lock: %m");
                                 goto finish;
                         }
 
@@ -5668,11 +5669,11 @@ static int run(int argc, char *argv[]) {
                 } else {
                         r = image_path_lock(arg_image, (arg_read_only ? LOCK_SH : LOCK_EX) | LOCK_NB, &tree_global_lock, &tree_local_lock);
                         if (r == -EBUSY) {
-                                r = log_error_errno(r, "Disk image %s is currently busy.", arg_image);
+                                log_error_errno(r, "Disk image %s is currently busy.", arg_image);
                                 goto finish;
                         }
                         if (r < 0) {
-                                r = log_error_errno(r, "Failed to create image lock: %m");
+                                log_error_errno(r, "Failed to create image lock: %m");
                                 goto finish;
                         }
 
@@ -5857,7 +5858,6 @@ finish:
         expose_port_free_all(arg_expose_ports);
         rlimit_free_all(arg_rlimit);
         device_node_array_free(arg_extra_nodes, arg_n_extra_nodes);
-        machine_credential_free_all(arg_credentials, arg_n_credentials);
 
         if (r < 0)
                 return r;
index f9f0af2d0499b790fd1bc04d3803cfe98087991b..6e6678c33d990b8113bda61299848eb8436ec8cd 100644 (file)
@@ -276,7 +276,7 @@ int oomd_cgroup_kill(const char *path, bool recurse, bool dry_run) {
         if (r < 0)
                 log_debug_errno(r, "Failed to set user.oomd_kill on kill: %m");
 
-        return set_size(pids_killed) != 0;
+        return !set_isempty(pids_killed);
 }
 
 typedef void (*dump_candidate_func)(const OomdCGroupContext *ctx, FILE *f, const char *prefix);
index 62f3ee6744a8f4e4a92e77c00779ccbe3f85c736..491040ffb086889a403c98e129cc70691fac9780 100644 (file)
@@ -55,8 +55,9 @@ static int resize_crypt_luks_device(dev_t devno, const char *fstype, dev_t main_
                 return log_error_errno(r, "Failed to open main block device " DEVNUM_FORMAT_STR ": %m",
                                        DEVNUM_FORMAT_VAL(main_devno));
 
-        if (ioctl(main_devfd, BLKGETSIZE64, &size) != 0)
-                return log_error_errno(errno, "Failed to query size of \"%s\" (before resize): %m",
+        r = blockdev_get_device_size(main_devfd, &size);
+        if (r < 0)
+                return log_error_errno(r, "Failed to query size of \"%s\" (before resize): %m",
                                        main_devpath);
 
         log_debug("%s is %"PRIu64" bytes", main_devpath, size);
@@ -83,9 +84,9 @@ static int resize_crypt_luks_device(dev_t devno, const char *fstype, dev_t main_
         if (r < 0)
                 return log_error_errno(r, "crypt_resize() of %s failed: %m", devpath);
 
-        if (ioctl(main_devfd, BLKGETSIZE64, &size) != 0)
-                log_warning_errno(errno, "Failed to query size of \"%s\" (after resize): %m",
-                                  devpath);
+        r = blockdev_get_device_size(main_devfd, &size);
+        if (r < 0)
+                log_warning_errno(r, "Failed to query size of \"%s\" (after resize): %m", devpath);
         else
                 log_debug("%s is now %"PRIu64" bytes", main_devpath, size);
 
@@ -250,8 +251,9 @@ static int run(int argc, char *argv[]) {
                 return log_error_errno(r, "Failed to open block device " DEVNUM_FORMAT_STR ": %m",
                                        DEVNUM_FORMAT_VAL(devno));
 
-        if (ioctl(devfd, BLKGETSIZE64, &size) != 0)
-                return log_error_errno(errno, "Failed to query size of \"%s\": %m", devpath);
+        r = blockdev_get_device_size(devfd, &size);
+        if (r < 0)
+                return log_error_errno(r, "Failed to query size of \"%s\": %m", devpath);
 
         log_debug("Resizing \"%s\" to %"PRIu64" bytes...", arg_target, size);
 
index 3a92d1be1c1d5ad4f7a78a6d5b604a51d4337bce..1e9284e2e2e855ad71c59e91b7192e6dfae24c9b 100644 (file)
@@ -6042,8 +6042,9 @@ static int context_open_copy_block_paths(
                 if (S_ISREG(st.st_mode))
                         size = st.st_size;
                 else if (S_ISBLK(st.st_mode)) {
-                        if (ioctl(source_fd, BLKGETSIZE64, &size) != 0)
-                                return log_error_errno(errno, "Failed to determine size of block device to copy from: %m");
+                        r = blockdev_get_device_size(source_fd, &size);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to determine size of block device to copy from: %m");
                 } else
                         return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Specified path to copy blocks from '%s' is not a regular file, block device or directory, refusing: %m", opened);
 
@@ -7365,8 +7366,9 @@ static int resize_backing_fd(
 
                 assert(loop_device);
 
-                if (ioctl(*fd, BLKGETSIZE64, &current_size) < 0)
-                        return log_error_errno(errno, "Failed to determine size of block device %s: %m", node);
+                r = blockdev_get_device_size(*fd, &current_size);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to determine size of block device %s: %m", node);
         } else {
                 r = stat_verify_regular(&st);
                 if (r < 0)
index 12959496ded7d690c367339fdc7070ccf44d6684..ead353f5a69704e22f0d312574480fe566991816 100644 (file)
@@ -276,18 +276,18 @@ static int vl_method_extend(Varlink *link, JsonVariant *parameters, VarlinkMetho
                 return r;
 
         if (!TPM2_PCR_INDEX_VALID(p.pcr))
-                return varlink_errorb(link, VARLINK_ERROR_INVALID_PARAMETER, JSON_BUILD_OBJECT(JSON_BUILD_PAIR_STRING("parameter", "pcr")));
+                return varlink_error_invalid_parameter_name(link, "pcr");
 
         if (p.text) {
                 /* Specifying both the text string and the binary data is not allowed */
                 if (p.data.iov_base)
-                        return varlink_errorb(link, VARLINK_ERROR_INVALID_PARAMETER, JSON_BUILD_OBJECT(JSON_BUILD_PAIR_STRING("parameter", "data")));
+                        return varlink_error_invalid_parameter_name(link, "data");
 
                 r = extend_now(p.pcr, p.text, strlen(p.text), _TPM2_USERSPACE_EVENT_TYPE_INVALID);
         } else if (p.data.iov_base)
                 r = extend_now(p.pcr, p.data.iov_base, p.data.iov_len, _TPM2_USERSPACE_EVENT_TYPE_INVALID);
         else
-                return varlink_errorb(link, VARLINK_ERROR_INVALID_PARAMETER, JSON_BUILD_OBJECT(JSON_BUILD_PAIR_STRING("parameter", "text")));
+                return varlink_error_invalid_parameter_name(link, "text");
         if (r < 0)
                 return r;
 
index dc48bc57e5628eb2566b7011f4d3e183e15010e9..23bdab0befe66e331e857a0a7424d4c999452b8e 100644 (file)
@@ -11,6 +11,7 @@
 #include "blockdev-util.h"
 #include "build.h"
 #include "chase.h"
+#include "color-util.h"
 #include "conf-files.h"
 #include "efi-api.h"
 #include "env-util.h"
@@ -1932,40 +1933,6 @@ static int event_log_map_components(EventLog *el) {
         return event_log_validate_fully_recognized(el);
 }
 
-static void hsv_to_rgb(
-                double h, double s, double v,
-                uint8_t* ret_r, uint8_t *ret_g, uint8_t *ret_b) {
-
-        double c, x, m, r, g, b;
-
-        assert(s >= 0 && s <= 100);
-        assert(v >= 0 && v <= 100);
-        assert(ret_r);
-        assert(ret_g);
-        assert(ret_b);
-
-        c = (s / 100.0) * (v / 100.0);
-        x = c * (1 - fabs(fmod(h / 60.0, 2) - 1));
-        m = (v / 100) - c;
-
-        if (h >= 0 && h < 60)
-                r = c, g = x, b = 0.0;
-        else if (h >= 60 && h < 120)
-                r = x, g = c, b = 0.0;
-        else if (h >= 120 && h < 180)
-                r = 0.0, g = c, b = x;
-        else if (h >= 180 && h < 240)
-                r = 0.0, g = x, b = c;
-        else if (h >= 240 && h < 300)
-                r = x, g = 0.0, b = c;
-        else
-                r = c, g = 0.0, b = x;
-
-        *ret_r = (uint8_t) ((r + m) * 255);
-        *ret_g = (uint8_t) ((g + m) * 255);
-        *ret_b = (uint8_t) ((b + m) * 255);
-}
-
 #define ANSI_TRUE_COLOR_MAX (7U + 3U + 1U + 3U + 1U + 3U + 2U)
 
 static const char *ansi_true_color(uint8_t r, uint8_t g, uint8_t b, char ret[static ANSI_TRUE_COLOR_MAX]) {
@@ -4906,7 +4873,7 @@ static int parse_argv(int argc, char *argv[]) {
                         break;
 
                 case ARG_PCRLOCK:
-                        if (isempty(optarg) || streq(optarg, "-"))
+                        if (empty_or_dash(optarg))
                                 arg_pcrlock_path = mfree(arg_pcrlock_path);
                         else {
                                 r = parse_path_argument(optarg, /* suppress_root= */ false, &arg_pcrlock_path);
@@ -4918,7 +4885,7 @@ static int parse_argv(int argc, char *argv[]) {
                         break;
 
                 case ARG_POLICY:
-                        if (isempty(optarg) || streq(optarg, "-"))
+                        if (empty_or_dash(optarg))
                                 arg_policy_path = mfree(arg_policy_path);
                         else {
                                 r = parse_path_argument(optarg, /* suppress_root= */ false, &arg_policy_path);
index 0d5518060eb9a9276ac2243b247d06cf63bc9216..4f239e2b125eb85bfd8c84f67c2bd5b9bdcda009 100644 (file)
@@ -320,11 +320,8 @@ static int method_detach_image(sd_bus_message *message, void *userdata, sd_bus_e
 
         r = bus_verify_polkit_async(
                         message,
-                        CAP_SYS_ADMIN,
                         "org.freedesktop.portable1.attach-images",
-                        NULL,
-                        false,
-                        UID_INVALID,
+                        /* details= */ NULL,
                         &m->polkit_registry,
                         error);
         if (r < 0)
@@ -377,11 +374,8 @@ static int method_set_pool_limit(sd_bus_message *message, void *userdata, sd_bus
 
         r = bus_verify_polkit_async(
                         message,
-                        CAP_SYS_ADMIN,
                         "org.freedesktop.portable1.manage-images",
-                        NULL,
-                        false,
-                        UID_INVALID,
+                        /* details= */ NULL,
                         &m->polkit_registry,
                         error);
         if (r < 0)
index 1f61c3b8c42c6c654dc1738c6667d8a074c40a28..63f177eb74b28f58685a6c7103c75e0e81a0f616 100644 (file)
@@ -451,11 +451,8 @@ static int bus_image_method_detach(
 
         r = bus_verify_polkit_async(
                         message,
-                        CAP_SYS_ADMIN,
                         "org.freedesktop.portable1.attach-images",
-                        NULL,
-                        false,
-                        UID_INVALID,
+                        /* details= */ NULL,
                         &m->polkit_registry,
                         error);
         if (r < 0)
@@ -1010,11 +1007,8 @@ int bus_image_acquire(
         if (mode == BUS_IMAGE_AUTHENTICATE_ALL) {
                 r = bus_verify_polkit_async(
                                 message,
-                                CAP_SYS_ADMIN,
                                 polkit_action,
-                                NULL,
-                                false,
-                                UID_INVALID,
+                                /* details= */ NULL,
                                 &m->polkit_registry,
                                 error);
                 if (r < 0)
@@ -1064,11 +1058,8 @@ int bus_image_acquire(
                 if (mode == BUS_IMAGE_AUTHENTICATE_BY_PATH) {
                         r = bus_verify_polkit_async(
                                         message,
-                                        CAP_SYS_ADMIN,
                                         polkit_action,
-                                        NULL,
-                                        false,
-                                        UID_INVALID,
+                                        /* details= */ NULL,
                                         &m->polkit_registry,
                                         error);
                         if (r < 0)
index da68b41a371fb4fc128a6b7b9c7e5d9a49b0d65c..1af370d23936c2aa72c79f2ab16bdd798bb3a789 100644 (file)
@@ -80,7 +80,7 @@ bool dns_type_is_valid_query(uint16_t type) {
                        DNS_TYPE_RRSIG);
 }
 
-bool dns_type_is_zone_transer(uint16_t type) {
+bool dns_type_is_zone_transfer(uint16_t type) {
 
         /* Zone transfers, either normal or incremental */
 
index f0bb3be7beadc3fbc824605d6efbbab385d2f69f..9c294f4e5fd8ab6e39da1868e7ac5c331257630d 100644 (file)
@@ -118,7 +118,7 @@ bool dns_type_is_obsolete(uint16_t type);
 bool dns_type_may_wildcard(uint16_t type);
 bool dns_type_apex_only(uint16_t type);
 bool dns_type_needs_authentication(uint16_t type);
-bool dns_type_is_zone_transer(uint16_t type);
+bool dns_type_is_zone_transfer(uint16_t type);
 int dns_type_to_af(uint16_t type);
 
 bool dns_class_is_pseudo(uint16_t class);
index 1ef25acdad446ac99aad24c9e4c96f779bdf0132..ef3f5237a9ea131aa2a2aed458db83611048458b 100644 (file)
@@ -806,7 +806,7 @@ static int bus_method_resolve_record(sd_bus_message *message, void *userdata, sd
 
         if (!dns_type_is_valid_query(type))
                 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Specified resource record type %" PRIu16 " may not be used in a query.", type);
-        if (dns_type_is_zone_transer(type))
+        if (dns_type_is_zone_transfer(type))
                 return sd_bus_error_set(error, SD_BUS_ERROR_NOT_SUPPORTED, "Zone transfers not permitted via this programming interface.");
         if (dns_type_is_obsolete(type))
                 return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Specified DNS resource record type %" PRIu16 " is obsolete.", type);
@@ -1988,10 +1988,12 @@ static int bus_method_register_service(sd_bus_message *message, void *userdata,
         if (r < 0)
                 return r;
 
-        r = bus_verify_polkit_async(message, CAP_SYS_ADMIN,
-                                    "org.freedesktop.resolve1.register-service",
-                                    NULL, false, UID_INVALID,
-                                    &m->polkit_registry, error);
+        r = bus_verify_polkit_async(
+                        message,
+                        "org.freedesktop.resolve1.register-service",
+                        /* details= */ NULL,
+                        &m->polkit_registry,
+                        error);
         if (r < 0)
                 return r;
         if (r == 0)
index 78665bc93be6ad50651184e77d2e761b3f3d56f4..c5c5037cec6193296ad53c9bab568cf81c16125d 100644 (file)
@@ -164,8 +164,8 @@ void dns_cache_flush(DnsCache *c) {
         while ((key = hashmap_first_key(c->by_key)))
                 dns_cache_remove_by_key(c, key);
 
-        assert(hashmap_size(c->by_key) == 0);
-        assert(prioq_size(c->by_expiry) == 0);
+        assert(hashmap_isempty(c->by_key));
+        assert(prioq_isempty(c->by_expiry));
 
         c->by_key = hashmap_free(c->by_key);
         c->by_expiry = prioq_free(c->by_expiry);
@@ -186,7 +186,7 @@ static void dns_cache_make_space(DnsCache *c, unsigned add) {
                 _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
                 DnsCacheItem *i;
 
-                if (prioq_size(c->by_expiry) <= 0)
+                if (prioq_isempty(c->by_expiry))
                         break;
 
                 if (prioq_size(c->by_expiry) + add < CACHE_MAX)
index 2580c2333c366bc02c2037555a3ed25e779add23..3c9b90c89b229f6d62423859f13d8b983665d7ce 100644 (file)
@@ -1951,7 +1951,7 @@ found_closest_encloser:
 }
 
 static int dnssec_nsec_wildcard_equal(DnsResourceRecord *rr, const char *name) {
-        char label[DNS_LABEL_MAX];
+        char label[DNS_LABEL_MAX+1];
         const char *n;
         int r;
 
index ca1eea4346309be1233ce76ee97b0f7866a35a28..991a1be458453766aa55fbfa10e914dc0d522a80 100644 (file)
@@ -552,7 +552,7 @@ int dns_packet_append_name(
 
         while (!dns_name_is_root(name)) {
                 const char *z = name;
-                char label[DNS_LABEL_MAX];
+                char label[DNS_LABEL_MAX+1];
                 size_t n = 0;
 
                 if (allow_compression)
index 2e8b3e55801723ecce469c435965674d005c5d4a..40615ff605139f349ba3f1275f353ee60017c056 100644 (file)
@@ -1557,7 +1557,7 @@ int dns_scope_add_dnssd_services(DnsScope *scope) {
 
         assert(scope);
 
-        if (hashmap_size(scope->manager->dnssd_services) == 0)
+        if (hashmap_isempty(scope->manager->dnssd_services))
                 return 0;
 
         scope->announced = false;
index c59e3b7f495f78d8fc98c6717eb8cdc7d77a9bfc..f43e1f8b617196e2609137d5eca37462449d9eb6 100644 (file)
@@ -929,7 +929,7 @@ static void dns_stub_process_query(Manager *m, DnsStubListenerExtra *l, DnsStrea
                 return;
         }
 
-        if (dns_type_is_zone_transer(dns_question_first_key(p->question)->type)) {
+        if (dns_type_is_zone_transfer(dns_question_first_key(p->question)->type)) {
                 log_debug("Got request for zone transfer, refusing.");
                 dns_stub_send_failure(m, l, s, p, DNS_RCODE_REFUSED, false);
                 return;
index 696fce532a41f98fb36c679b62f5576b58fe626f..fe88e502e7c11c3ad24264ff47ace13279113a36 100644 (file)
@@ -2808,7 +2808,7 @@ static int dns_transaction_requires_rrsig(DnsTransaction *t, DnsResourceRecord *
                         if (r == 0)
                                 continue;
 
-                        return FLAGS_SET(t->answer_query_flags, SD_RESOLVED_AUTHENTICATED);
+                        return FLAGS_SET(dt->answer_query_flags, SD_RESOLVED_AUTHENTICATED);
                 }
 
                 return true;
@@ -2835,7 +2835,7 @@ static int dns_transaction_requires_rrsig(DnsTransaction *t, DnsResourceRecord *
                         /* We found the transaction that was supposed to find the SOA RR for us. It was
                          * successful, but found no RR for us. This means we are not at a zone cut. In this
                          * case, we require authentication if the SOA lookup was authenticated too. */
-                        return FLAGS_SET(t->answer_query_flags, SD_RESOLVED_AUTHENTICATED);
+                        return FLAGS_SET(dt->answer_query_flags, SD_RESOLVED_AUTHENTICATED);
                 }
 
                 return true;
index 1703c43d4b0ab0d906b0a4011108886fbdbfa9ad..2156f4f685d27955cb3d24ffd7d1463d48a084f5 100644 (file)
@@ -181,7 +181,7 @@ static int dns_trust_anchor_add_builtin_negative(DnsTrustAnchor *d) {
          * trust anchor defined at all. This enables easy overriding
          * of negative trust anchors. */
 
-        if (set_size(d->negative_by_name) > 0)
+        if (!set_isempty(d->negative_by_name))
                 return 0;
 
         r = set_ensure_allocated(&d->negative_by_name, &dns_name_hash_ops);
index f533f972fc955418177718f2c01264bd62638b7e..d4ede46459c6eb6c0b61c10f22cee8568244f479 100644 (file)
@@ -70,8 +70,8 @@ void dns_zone_flush(DnsZone *z) {
         while ((i = hashmap_first(z->by_key)))
                 dns_zone_item_remove_and_free(z, i);
 
-        assert(hashmap_size(z->by_key) == 0);
-        assert(hashmap_size(z->by_name) == 0);
+        assert(hashmap_isempty(z->by_key));
+        assert(hashmap_isempty(z->by_name));
 
         z->by_key = hashmap_free(z->by_key);
         z->by_name = hashmap_free(z->by_name);
index 0f0d4786ef12b89946ce80581532cf361c6d6a7e..0ae24fbf02827e7c912f865a2b5e22a5d9195504 100644 (file)
@@ -20,10 +20,14 @@ int bus_dnssd_method_unregister(sd_bus_message *message, void *userdata, sd_bus_
 
         m = s->manager;
 
-        r = bus_verify_polkit_async(message, CAP_SYS_ADMIN,
-                                    "org.freedesktop.resolve1.unregister-service",
-                                    NULL, false, s->originator,
-                                    &m->polkit_registry, error);
+        r = bus_verify_polkit_async_full(
+                        message,
+                        "org.freedesktop.resolve1.unregister-service",
+                        /* details= */ NULL,
+                        /* interactive= */ false,
+                        /* good_user= */ s->originator,
+                        &m->polkit_registry,
+                        error);
         if (r < 0)
                 return r;
         if (r == 0)
index 4f8f591306cbefeceba4bd1851030a7786d3a443..7ca3214b0649db304f9fe7547a65e4608c3faee2 100644 (file)
@@ -236,10 +236,11 @@ static int bus_link_method_set_dns_servers_internal(sd_bus_message *message, voi
         if (r < 0)
                 return r;
 
-        r = bus_verify_polkit_async(message, CAP_NET_ADMIN,
-                                    "org.freedesktop.resolve1.set-dns-servers",
-                                    NULL, true, UID_INVALID,
-                                    &l->manager->polkit_registry, error);
+        r = bus_verify_polkit_async(
+                        message,
+                        "org.freedesktop.resolve1.set-dns-servers",
+                        /* details= */ NULL,
+                        &l->manager->polkit_registry, error);
         if (r < 0)
                 goto finalize;
         if (r == 0) {
@@ -368,10 +369,12 @@ int bus_link_method_set_domains(sd_bus_message *message, void *userdata, sd_bus_
         if (r < 0)
                 return r;
 
-        r = bus_verify_polkit_async(message, CAP_NET_ADMIN,
-                                    "org.freedesktop.resolve1.set-domains",
-                                    NULL, true, UID_INVALID,
-                                    &l->manager->polkit_registry, error);
+        r = bus_verify_polkit_async(
+                        message,
+                        "org.freedesktop.resolve1.set-domains",
+                        /* details= */ NULL,
+                        &l->manager->polkit_registry,
+                        error);
         if (r < 0)
                 return r;
         if (r == 0)
@@ -446,10 +449,12 @@ int bus_link_method_set_default_route(sd_bus_message *message, void *userdata, s
         if (r < 0)
                 return r;
 
-        r = bus_verify_polkit_async(message, CAP_NET_ADMIN,
-                                    "org.freedesktop.resolve1.set-default-route",
-                                    NULL, true, UID_INVALID,
-                                    &l->manager->polkit_registry, error);
+        r = bus_verify_polkit_async(
+                        message,
+                        "org.freedesktop.resolve1.set-default-route",
+                        /* details= */ NULL,
+                        &l->manager->polkit_registry,
+                        error);
         if (r < 0)
                 return r;
         if (r == 0)
@@ -493,10 +498,12 @@ int bus_link_method_set_llmnr(sd_bus_message *message, void *userdata, sd_bus_er
                         return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid LLMNR setting: %s", llmnr);
         }
 
-        r = bus_verify_polkit_async(message, CAP_NET_ADMIN,
-                                    "org.freedesktop.resolve1.set-llmnr",
-                                    NULL, true, UID_INVALID,
-                                    &l->manager->polkit_registry, error);
+        r = bus_verify_polkit_async(
+                        message,
+                        "org.freedesktop.resolve1.set-llmnr",
+                        /* details= */ NULL,
+                        &l->manager->polkit_registry,
+                        error);
         if (r < 0)
                 return r;
         if (r == 0)
@@ -541,10 +548,12 @@ int bus_link_method_set_mdns(sd_bus_message *message, void *userdata, sd_bus_err
                         return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid MulticastDNS setting: %s", mdns);
         }
 
-        r = bus_verify_polkit_async(message, CAP_NET_ADMIN,
-                                    "org.freedesktop.resolve1.set-mdns",
-                                    NULL, true, UID_INVALID,
-                                    &l->manager->polkit_registry, error);
+        r = bus_verify_polkit_async(
+                        message,
+                        "org.freedesktop.resolve1.set-mdns",
+                        /* details= */ NULL,
+                        &l->manager->polkit_registry,
+                        error);
         if (r < 0)
                 return r;
         if (r == 0)
@@ -589,10 +598,12 @@ int bus_link_method_set_dns_over_tls(sd_bus_message *message, void *userdata, sd
                         return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid DNSOverTLS setting: %s", dns_over_tls);
         }
 
-        r = bus_verify_polkit_async(message, CAP_NET_ADMIN,
-                                    "org.freedesktop.resolve1.set-dns-over-tls",
-                                    NULL, true, UID_INVALID,
-                                    &l->manager->polkit_registry, error);
+        r = bus_verify_polkit_async(
+                        message,
+                        "org.freedesktop.resolve1.set-dns-over-tls",
+                        /* details= */ NULL,
+                        &l->manager->polkit_registry,
+                        error);
         if (r < 0)
                 return r;
         if (r == 0)
@@ -637,10 +648,12 @@ int bus_link_method_set_dnssec(sd_bus_message *message, void *userdata, sd_bus_e
                         return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid DNSSEC setting: %s", dnssec);
         }
 
-        r = bus_verify_polkit_async(message, CAP_NET_ADMIN,
-                                    "org.freedesktop.resolve1.set-dnssec",
-                                    NULL, true, UID_INVALID,
-                                    &l->manager->polkit_registry, error);
+        r = bus_verify_polkit_async(
+                        message,
+                        "org.freedesktop.resolve1.set-dnssec",
+                        /* details= */ NULL,
+                        &l->manager->polkit_registry,
+                        error);
         if (r < 0)
                 return r;
         if (r == 0)
@@ -698,10 +711,12 @@ int bus_link_method_set_dnssec_negative_trust_anchors(sd_bus_message *message, v
                         return -ENOMEM;
         }
 
-        r = bus_verify_polkit_async(message, CAP_NET_ADMIN,
-                                    "org.freedesktop.resolve1.set-dnssec-negative-trust-anchors",
-                                    NULL, true, UID_INVALID,
-                                    &l->manager->polkit_registry, error);
+        r = bus_verify_polkit_async(
+                        message,
+                        "org.freedesktop.resolve1.set-dnssec-negative-trust-anchors",
+                        /* details= */ NULL,
+                        &l->manager->polkit_registry,
+                        error);
         if (r < 0)
                 return r;
         if (r == 0)
@@ -734,10 +749,12 @@ int bus_link_method_revert(sd_bus_message *message, void *userdata, sd_bus_error
         if (r < 0)
                 return r;
 
-        r = bus_verify_polkit_async(message, CAP_NET_ADMIN,
-                                    "org.freedesktop.resolve1.revert",
-                                    NULL, true, UID_INVALID,
-                                    &l->manager->polkit_registry, error);
+        r = bus_verify_polkit_async(
+                        message,
+                        "org.freedesktop.resolve1.revert",
+                        /* details= */ NULL,
+                        &l->manager->polkit_registry,
+                        error);
         if (r < 0)
                 return r;
         if (r == 0)
index b52619e287b433c490f9a2234ad1b45796a77449..0295662b5b5eae4f886adc940e4c7ce6acaa2f5a 100644 (file)
@@ -388,7 +388,7 @@ static char* fallback_hostname(void) {
 
 static int make_fallback_hostnames(char **full_hostname, char **llmnr_hostname, char **mdns_hostname) {
         _cleanup_free_ char *h = NULL, *n = NULL, *m = NULL;
-        char label[DNS_LABEL_MAX];
+        char label[DNS_LABEL_MAX+1];
         const char *p;
         int r;
 
index 00abada426ccebfc1c26bd1c3af4054035dce029..adcd35d6bee8483b781fbef9fe83fc834de574f1 100644 (file)
@@ -14,7 +14,7 @@ int resolve_system_hostname(char **full_hostname, char **first_label) {
 #elif HAVE_LIBIDN
         int k;
 #endif
-        char label[DNS_LABEL_MAX];
+        char label[DNS_LABEL_MAX+1];
         const char *p, *decoded;
         int r;
 
index 597a25abeb634a9cae463fbb9cdc6633c10e9d1e..95336b86fe5a7143ec1ead09b56317f853e313a9 100644 (file)
@@ -7,3 +7,17 @@ executables += [
                 'sources' : files('run.c'),
         },
 ]
+
+install_emptydir(bindir)
+
+meson.add_install_script(sh, '-c',
+                             ln_s.format(bindir / 'systemd-run',
+                                         bindir / 'uid0'))
+
+custom_target(
+        'systemd-uid0',
+        input : 'systemd-uid0.in',
+        output : 'systemd-uid0',
+        command : [jinja2_cmdline, '@INPUT@', '@OUTPUT@'],
+        install : pamconfdir != 'no',
+        install_dir : pamconfdir)
index 88eca0fd6d172c1d68bc0473b65c92326ee735b5..45da75f8897bb55bf540156ce7132a74ec621f6e 100644 (file)
@@ -17,6 +17,7 @@
 #include "bus-unit-util.h"
 #include "bus-wait-for-jobs.h"
 #include "calendarspec.h"
+#include "color-util.h"
 #include "env-util.h"
 #include "escape.h"
 #include "exit-status.h"
@@ -73,6 +74,9 @@ static bool arg_aggressive_gc = false;
 static char *arg_working_directory = NULL;
 static bool arg_shell = false;
 static char **arg_cmdline = NULL;
+static char *arg_exec_path = NULL;
+static bool arg_ignore_failure = false;
+static char *arg_background = NULL;
 
 STATIC_DESTRUCTOR_REGISTER(arg_description, freep);
 STATIC_DESTRUCTOR_REGISTER(arg_environment, strv_freep);
@@ -82,6 +86,8 @@ STATIC_DESTRUCTOR_REGISTER(arg_socket_property, strv_freep);
 STATIC_DESTRUCTOR_REGISTER(arg_timer_property, strv_freep);
 STATIC_DESTRUCTOR_REGISTER(arg_working_directory, freep);
 STATIC_DESTRUCTOR_REGISTER(arg_cmdline, strv_freep);
+STATIC_DESTRUCTOR_REGISTER(arg_exec_path, freep);
+STATIC_DESTRUCTOR_REGISTER(arg_background, freep);
 
 static int help(void) {
         _cleanup_free_ char *link = NULL;
@@ -91,8 +97,8 @@ static int help(void) {
         if (r < 0)
                 return log_oom();
 
-        printf("%s [OPTIONS...] COMMAND [ARGUMENTS...]\n"
-               "\n%sRun the specified command in a transient scope or service.%s\n\n"
+        printf("%1$s [OPTIONS...] COMMAND [ARGUMENTS...]\n"
+               "\n%5$sRun the specified command in a transient scope or service.%6$s\n\n"
                "  -h --help                       Show this help\n"
                "     --version                    Show package version\n"
                "     --no-ask-password            Do not prompt for password\n"
@@ -104,7 +110,7 @@ static int help(void) {
                "  -p --property=NAME=VALUE        Set service or scope unit property\n"
                "     --description=TEXT           Description for unit\n"
                "     --slice=SLICE                Run in the specified slice\n"
-               "     --slice-inherit              Inherit the slice\n"
+               "     --slice-inherit              Inherit the slice from the caller\n"
                "     --expand-environment=BOOL    Control expansion of environment variables\n"
                "     --no-block                   Do not wait until operation finished\n"
                "  -r --remain-after-exit          Leave service around until explicitly stopped\n"
@@ -122,12 +128,14 @@ static int help(void) {
                "  -P --pipe                       Pass STDIN/STDOUT/STDERR directly to service\n"
                "  -q --quiet                      Suppress information messages during runtime\n"
                "  -G --collect                    Unload unit after it ran, even when failed\n"
-               "  -S --shell                      Invoke a $SHELL interactively\n\n"
-               "Path options:\n"
-               "     --path-property=NAME=VALUE   Set path unit property\n\n"
-               "Socket options:\n"
-               "     --socket-property=NAME=VALUE Set socket unit property\n\n"
-               "Timer options:\n"
+               "  -S --shell                      Invoke a $SHELL interactively\n"
+               "     --ignore-failure             Ignore the exit status of the invoked process\n"
+               "     --background=COLOR           Set ANSI color for background\n"
+               "\n%3$sPath options:%4$s\n"
+               "     --path-property=NAME=VALUE   Set path unit property\n"
+               "\n%3$sSocket options:%4$s\n"
+               "     --socket-property=NAME=VALUE Set socket unit property\n"
+               "\n%3$sTimer options:%4$s\n"
                "     --on-active=SECONDS          Run after SECONDS delay\n"
                "     --on-boot=SECONDS            Run SECONDS after machine was booted up\n"
                "     --on-startup=SECONDS         Run SECONDS after systemd activation\n"
@@ -137,6 +145,40 @@ static int help(void) {
                "     --on-timezone-change         Run when the timezone changes\n"
                "     --on-clock-change            Run when the realtime clock jumps\n"
                "     --timer-property=NAME=VALUE  Set timer unit property\n"
+               "\nSee the %2$s for details.\n",
+               program_invocation_short_name,
+               link,
+               ansi_underline(), ansi_normal(),
+               ansi_highlight(), ansi_normal());
+
+        return 0;
+}
+
+static int help_sudo_mode(void) {
+        _cleanup_free_ char *link = NULL;
+        int r;
+
+        r = terminal_urlify_man("uid0", "1", &link);
+        if (r < 0)
+                return log_oom();
+
+        printf("%s [OPTIONS...] COMMAND [ARGUMENTS...]\n"
+               "\n%sElevate privileges interactively.%s\n\n"
+               "  -h --help                       Show this help\n"
+               "  -V --version                    Show package version\n"
+               "     --no-ask-password            Do not prompt for password\n"
+               "     --machine=CONTAINER          Operate on local container\n"
+               "     --unit=UNIT                  Run under the specified unit name\n"
+               "     --property=NAME=VALUE        Set service or scope unit property\n"
+               "     --description=TEXT           Description for unit\n"
+               "     --slice=SLICE                Run in the specified slice\n"
+               "     --slice-inherit              Inherit the slice\n"
+               "  -u --user=USER                  Run as system user\n"
+               "  -g --group=GROUP                Run as system group\n"
+               "     --nice=NICE                  Nice level\n"
+               "  -D --chdir=PATH                 Set working directory\n"
+               "     --setenv=NAME[=VALUE]        Set environment variable\n"
+               "     --background=COLOR           Set ANSI color for background\n"
                "\nSee the %s for details.\n",
                program_invocation_short_name,
                ansi_highlight(),
@@ -162,6 +204,18 @@ static int add_timer_property(const char *name, const char *val) {
         return 0;
 }
 
+static char **make_login_shell_cmdline(const char *shell) {
+        _cleanup_free_ char *argv0 = NULL;
+
+        assert(shell);
+
+        argv0 = strjoin("-", shell); /* The - is how shells determine if they shall be consider login shells */
+        if (!argv0)
+                return NULL;
+
+        return strv_new(argv0);
+}
+
 static int parse_argv(int argc, char *argv[]) {
 
         enum {
@@ -194,6 +248,8 @@ static int parse_argv(int argc, char *argv[]) {
                 ARG_WAIT,
                 ARG_WORKING_DIRECTORY,
                 ARG_SHELL,
+                ARG_IGNORE_FAILURE,
+                ARG_BACKGROUND,
         };
 
         static const struct option options[] = {
@@ -239,6 +295,8 @@ static int parse_argv(int argc, char *argv[]) {
                 { "working-directory",  required_argument, NULL, ARG_WORKING_DIRECTORY  },
                 { "same-dir",           no_argument,       NULL, 'd'                    },
                 { "shell",              no_argument,       NULL, 'S'                    },
+                { "ignore-failure",     no_argument,       NULL, ARG_IGNORE_FAILURE     },
+                { "background",         no_argument,       NULL, ARG_BACKGROUND         },
                 {},
         };
 
@@ -282,7 +340,7 @@ static int parse_argv(int argc, char *argv[]) {
                         break;
 
                 case ARG_DESCRIPTION:
-                        r = free_and_strdup(&arg_description, optarg);
+                        r = free_and_strdup_warn(&arg_description, optarg);
                         if (r < 0)
                                 return r;
                         break;
@@ -524,6 +582,16 @@ static int parse_argv(int argc, char *argv[]) {
                         arg_shell = true;
                         break;
 
+                case ARG_IGNORE_FAILURE:
+                        arg_ignore_failure = true;
+                        break;
+
+                case ARG_BACKGROUND:
+                        r = free_and_strdup_warn(&arg_background, optarg);
+                        if (r < 0)
+                                return r;
+                        break;
+
                 case '?':
                         return -EINVAL;
 
@@ -556,11 +624,8 @@ static int parse_argv(int argc, char *argv[]) {
                                         return log_error_errno(r, "Failed to get current working directory: %m");
                         }
 
-                        if (!arg_service_type) {
-                                arg_service_type = strdup("exec");
-                                if (!arg_service_type)
-                                        return log_oom();
-                        }
+                        if (!arg_service_type)
+                                arg_service_type = "exec";
 
                         arg_wait = true;
                 }
@@ -654,6 +719,259 @@ static int parse_argv(int argc, char *argv[]) {
         return 1;
 }
 
+static int parse_argv_sudo_mode(int argc, char *argv[]) {
+
+        enum {
+                ARG_NO_ASK_PASSWORD = 0x100,
+                ARG_HOST,
+                ARG_MACHINE,
+                ARG_UNIT,
+                ARG_PROPERTY,
+                ARG_DESCRIPTION,
+                ARG_SLICE,
+                ARG_SLICE_INHERIT,
+                ARG_NICE,
+                ARG_SETENV,
+                ARG_BACKGROUND,
+        };
+
+        /* If invoked as "uid0" binary, let's expose a more sudo-like interface. We add various extensions
+         * though (but limit the extension to long options). */
+
+        static const struct option options[] = {
+                { "help",               no_argument,       NULL, 'h'                    },
+                { "version",            no_argument,       NULL, 'V'                    },
+                { "no-ask-password",    no_argument,       NULL, ARG_NO_ASK_PASSWORD    },
+                { "machine",            required_argument, NULL, ARG_MACHINE            },
+                { "unit",               required_argument, NULL, ARG_UNIT               },
+                { "property",           required_argument, NULL, ARG_PROPERTY           },
+                { "description",        required_argument, NULL, ARG_DESCRIPTION        },
+                { "slice",              required_argument, NULL, ARG_SLICE              },
+                { "slice-inherit",      no_argument,       NULL, ARG_SLICE_INHERIT      },
+                { "user",               required_argument, NULL, 'u'                    },
+                { "group",              required_argument, NULL, 'g'                    },
+                { "nice",               required_argument, NULL, ARG_NICE               },
+                { "chdir",              required_argument, NULL, 'D'                    },
+                { "setenv",             required_argument, NULL, ARG_SETENV             },
+                { "background",         required_argument, NULL, ARG_BACKGROUND         },
+                {},
+        };
+
+        int r, c;
+
+        assert(argc >= 0);
+        assert(argv);
+
+        /* Resetting to 0 forces the invocation of an internal initialization routine of getopt_long()
+         * that checks for GNU extensions in optstring ('-' or '+' at the beginning). */
+        optind = 0;
+        while ((c = getopt_long(argc, argv, "+hVu:g:D:", options, NULL)) >= 0)
+
+                switch (c) {
+
+                case 'h':
+                        return help_sudo_mode();
+
+                case 'V':
+                        return version();
+
+                case ARG_NO_ASK_PASSWORD:
+                        arg_ask_password = false;
+                        break;
+
+                case ARG_MACHINE:
+                        arg_transport = BUS_TRANSPORT_MACHINE;
+                        arg_host = optarg;
+                        break;
+
+                case ARG_UNIT:
+                        arg_unit = optarg;
+                        break;
+
+                case ARG_PROPERTY:
+                        if (strv_extend(&arg_property, optarg) < 0)
+                                return log_oom();
+
+                        break;
+
+                case ARG_DESCRIPTION:
+                        r = free_and_strdup_warn(&arg_description, optarg);
+                        if (r < 0)
+                                return r;
+                        break;
+
+                case ARG_SLICE:
+                        arg_slice = optarg;
+                        break;
+
+                case ARG_SLICE_INHERIT:
+                        arg_slice_inherit = true;
+                        break;
+
+                case 'u':
+                        arg_exec_user = optarg;
+                        break;
+
+                case 'g':
+                        arg_exec_group = optarg;
+                        break;
+
+                case ARG_NICE:
+                        r = parse_nice(optarg, &arg_nice);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to parse nice value: %s", optarg);
+
+                        arg_nice_set = true;
+                        break;
+
+                case 'D':
+                        r = parse_path_argument(optarg, true, &arg_working_directory);
+                        if (r < 0)
+                                return r;
+
+                        break;
+
+                case ARG_SETENV:
+                        r = strv_env_replace_strdup_passthrough(&arg_environment, optarg);
+                        if (r < 0)
+                                return log_error_errno(r, "Cannot assign environment variable %s: %m", optarg);
+
+                        break;
+
+                case ARG_BACKGROUND:
+                        r = free_and_strdup_warn(&arg_background, optarg);
+                        if (r < 0)
+                                return r;
+
+                        break;
+
+                case '?':
+                        return -EINVAL;
+
+                default:
+                        assert_not_reached();
+                }
+
+        if (!arg_working_directory) {
+                if (arg_exec_user) {
+                        /* When switching to a specific user, also switch to its home directory. */
+                        arg_working_directory = strdup("~");
+                        if (!arg_working_directory)
+                                return log_oom();
+                } else {
+                        /* When switching to root without this being specified, then stay in the current directory */
+                        r = safe_getcwd(&arg_working_directory);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to get current working directory: %m");
+                }
+        }
+
+        arg_service_type = "exec";
+        arg_quiet = true;
+        arg_wait = true;
+        arg_aggressive_gc = true;
+
+        arg_stdio = isatty(STDIN_FILENO) && isatty(STDOUT_FILENO) && isatty(STDERR_FILENO) ? ARG_STDIO_PTY : ARG_STDIO_DIRECT;
+        arg_expand_environment = false;
+        arg_send_sighup = true;
+
+        _cleanup_strv_free_ char **l = NULL;
+        if (argc > optind)
+                l = strv_copy(argv + optind);
+        else {
+                const char *e;
+
+                e = strv_env_get(arg_environment, "SHELL");
+                if (e)
+                        arg_exec_path = strdup(e);
+                else {
+                        if (arg_transport == BUS_TRANSPORT_LOCAL) {
+                                r = get_shell(&arg_exec_path);
+                                if (r < 0)
+                                        return log_error_errno(r, "Failed to determine shell: %m");
+                        } else
+                                arg_exec_path = strdup("/bin/sh");
+                }
+                if (!arg_exec_path)
+                        return log_oom();
+
+                l = make_login_shell_cmdline(arg_exec_path);
+        }
+        if (!l)
+                return log_oom();
+
+        strv_free_and_replace(arg_cmdline, l);
+
+        if (!arg_slice) {
+                arg_slice = strdup("user.slice");
+                if (!arg_slice)
+                        return log_oom();
+        }
+
+        _cleanup_free_ char *un = NULL;
+        un = getusername_malloc();
+        if (!un)
+                return log_oom();
+
+        /* Set a bunch of environment variables in a roughly sudo-compatible way */
+        r = strv_env_assign(&arg_environment, "SUDO_USER", un);
+        if (r < 0)
+                return log_error_errno(r, "Failed to set $SUDO_USER environment variable: %m");
+
+        r = strv_env_assignf(&arg_environment, "SUDO_UID", UID_FMT, getuid());
+        if (r < 0)
+                return log_error_errno(r, "Failed to set $SUDO_UID environment variable: %m");
+
+        r = strv_env_assignf(&arg_environment, "SUDO_GID", GID_FMT, getgid());
+        if (r < 0)
+                return log_error_errno(r, "Failed to set $SUDO_GID environment variable: %m");
+
+        if (strv_extendf(&arg_property, "LogExtraFields=ELEVATED_UID=" UID_FMT, getuid()) < 0)
+                return log_oom();
+
+        if (strv_extendf(&arg_property, "LogExtraFields=ELEVATED_GID=" GID_FMT, getgid()) < 0)
+                return log_oom();
+
+        if (strv_extendf(&arg_property, "LogExtraFields=ELEVATED_USER=%s", un) < 0)
+                return log_oom();
+
+        if (strv_extend(&arg_property, "PAMName=systemd-uid0") < 0)
+                return log_oom();
+
+        if (!arg_background && arg_stdio == ARG_STDIO_PTY) {
+                double red, green, blue;
+
+                r = get_default_background_color(&red, &green, &blue);
+                if (r < 0)
+                        log_debug_errno(r, "Unable to get terminal background color, not tinting background: %m");
+                else {
+                        double h, s, v;
+
+                        rgb_to_hsv(red, green, blue, &h, &s, &v);
+
+                        if (!arg_exec_user || STR_IN_SET(arg_exec_user, "root", "0"))
+                                h = 0; /* red */
+                        else
+                                h = 60 /* yellow */;
+
+                        if (v > 50) /* If the background is bright, then pull down saturation */
+                                s = 25;
+                        else        /* otherwise pump it up */
+                                s = 75;
+
+                        v = MAX(30, v); /* Make sure we don't hide the color in black */
+
+                        uint8_t r8, g8, b8;
+                        hsv_to_rgb(h, s, v, &r8, &g8, &b8);
+
+                        if (asprintf(&arg_background, "48;2;%u;%u;%u", r8, g8, b8) < 0)
+                                return log_oom();
+                }
+        }
+
+        return 1;
+}
+
 static int transient_unit_set_properties(sd_bus_message *m, UnitType t, char **properties) {
         int r;
 
@@ -902,7 +1220,7 @@ static int transient_service_set_properties(sd_bus_message *m, const char *pty_p
                 if (r < 0)
                         return bus_log_create_error(r);
 
-                r = sd_bus_message_append(m, "s", arg_cmdline[0]);
+                r = sd_bus_message_append(m, "s", arg_exec_path ?: arg_cmdline[0]);
                 if (r < 0)
                         return bus_log_create_error(r);
 
@@ -913,9 +1231,10 @@ static int transient_service_set_properties(sd_bus_message *m, const char *pty_p
                 if (use_ex_prop)
                         r = sd_bus_message_append_strv(
                                         m,
-                                        STRV_MAKE(arg_expand_environment > 0 ? NULL : "no-env-expand"));
+                                        STRV_MAKE(arg_expand_environment > 0 ? NULL : "no-env-expand",
+                                                  arg_ignore_failure ? "ignore-failure" : NULL));
                 else
-                        r = sd_bus_message_append(m, "b", false);
+                        r = sd_bus_message_append(m, "b", arg_ignore_failure);
                 if (r < 0)
                         return bus_log_create_error(r);
 
@@ -1381,7 +1700,7 @@ static int start_transient_service(sd_bus *bus) {
 
                 r = bus_wait_for_jobs_one(w,
                                           object,
-                                          arg_quiet,
+                                          arg_quiet ? 0 : BUS_WAIT_JOBS_LOG_ERROR,
                                           arg_runtime_scope == RUNTIME_SCOPE_USER ? STRV_MAKE_CONST("--user") : NULL);
                 if (r < 0)
                         return r;
@@ -1435,6 +1754,9 @@ static int start_transient_service(sd_bus *bus) {
 
                         /* Make sure to process any TTY events before we process bus events */
                         (void) pty_forward_set_priority(c.forward, SD_EVENT_PRIORITY_IMPORTANT);
+
+                        if (!isempty(arg_background))
+                                (void) pty_forward_set_background_color(c.forward, arg_background);
                 }
 
                 path = unit_dbus_path_from_name(service);
@@ -1616,7 +1938,8 @@ static int start_transient_scope(sd_bus *bus) {
         if (r < 0)
                 return bus_log_parse_error(r);
 
-        r = bus_wait_for_jobs_one(w, object, arg_quiet, arg_runtime_scope == RUNTIME_SCOPE_USER ? STRV_MAKE_CONST("--user") : NULL);
+        r = bus_wait_for_jobs_one(w, object, arg_quiet ? 0 : BUS_WAIT_JOBS_LOG_ERROR,
+                                  arg_runtime_scope == RUNTIME_SCOPE_USER ? STRV_MAKE_CONST("--user") : NULL);
         if (r < 0)
                 return r;
 
@@ -1886,7 +2209,8 @@ static int start_transient_trigger(sd_bus *bus, const char *suffix) {
         if (r < 0)
                 return bus_log_parse_error(r);
 
-        r = bus_wait_for_jobs_one(w, object, arg_quiet, arg_runtime_scope == RUNTIME_SCOPE_USER ? STRV_MAKE_CONST("--user") : NULL);
+        r = bus_wait_for_jobs_one(w, object, arg_quiet ? 0 : BUS_WAIT_JOBS_LOG_ERROR,
+                                  arg_runtime_scope == RUNTIME_SCOPE_USER ? STRV_MAKE_CONST("--user") : NULL);
         if (r < 0)
                 return r;
 
@@ -1900,6 +2224,8 @@ static int start_transient_trigger(sd_bus *bus, const char *suffix) {
 }
 
 static bool shall_make_executable_absolute(void) {
+        if (arg_exec_path)
+                return false;
         if (strv_isempty(arg_cmdline))
                 return false;
         if (arg_transport != BUS_TRANSPORT_LOCAL)
@@ -1920,7 +2246,10 @@ static int run(int argc, char* argv[]) {
         log_parse_environment();
         log_open();
 
-        r = parse_argv(argc, argv);
+        if (invoked_as(argv, "uid0"))
+                r = parse_argv_sudo_mode(argc, argv);
+        else
+                r = parse_argv(argc, argv);
         if (r <= 0)
                 return r;
 
diff --git a/src/run/systemd-uid0.in b/src/run/systemd-uid0.in
new file mode 100644 (file)
index 0000000..57bd5e3
--- /dev/null
@@ -0,0 +1,23 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+# This file is part of systemd.
+#
+# Used by uid0 sessions
+
+{% if ENABLE_HOMED %}
+-account sufficient pam_systemd_home.so
+{% endif %}
+account  required   pam_unix.so
+
+{% if HAVE_SELINUX %}
+session  required   pam_selinux.so close
+session  required   pam_selinux.so open
+{% endif %}
+session  required   pam_loginuid.so
+session  optional   pam_keyinit.so force revoke
+session  required   pam_namespace.so
+{% if ENABLE_HOMED %}
+-session optional   pam_systemd_home.so
+{% endif %}
+session  optional   pam_umask.so silent
+session  optional   pam_systemd.so
+session  required   pam_unix.so
index 0e323f4644e576274a1840896264147b5a81df23..6d71245549d66f3ee84969918d2fa2161061b7b2 100644 (file)
@@ -443,9 +443,7 @@ int ask_password_tty(
                         (void) loop_write(ttyfd, ANSI_NORMAL, SIZE_MAX);
 
                 new_termios = old_termios;
-                new_termios.c_lflag &= ~(ICANON|ECHO);
-                new_termios.c_cc[VMIN] = 1;
-                new_termios.c_cc[VTIME] = 0;
+                termios_disable_echo(&new_termios);
 
                 r = RET_NERRNO(tcsetattr(ttyfd, TCSADRAIN, &new_termios));
                 if (r < 0)
index c906aec109c0efef51a0459cf1d4be3847f2ba27..347cd787c3a21173114b1cc5f7a95c3652f646c5 100644 (file)
@@ -777,6 +777,21 @@ int blockdev_get_sector_size(int fd, uint32_t *ret) {
         return 0;
 }
 
+int blockdev_get_device_size(int fd, uint64_t *ret) {
+        uint64_t sz = 0;
+
+        assert(fd >= 0);
+        assert(ret);
+
+        /* This is just a type-safe wrapper around BLKGETSIZE64 that gets us around having to include messy linux/fs.h in various clients */
+
+        if (ioctl(fd, BLKGETSIZE64, &sz) < 0)
+                return -errno;
+
+        *ret = sz;
+        return 0;
+}
+
 int blockdev_get_root(int level, dev_t *ret) {
         _cleanup_free_ char *p = NULL;
         dev_t devno;
index 954a23df3e9116078abab3ee902c50732569a35e..28aede289a0590f44cb408174153f8450a80284e 100644 (file)
@@ -57,5 +57,6 @@ int block_device_has_partitions(sd_device *dev);
 int blockdev_reread_partition_table(sd_device *dev);
 
 int blockdev_get_sector_size(int fd, uint32_t *ret);
+int blockdev_get_device_size(int fd, uint64_t *ret);
 
 int blockdev_get_root(int level, dev_t *ret);
index 904b897984a52d7aa449bbec3ff7456c96c75d79..9f923372a4b3e3ecdddf523a1c8c57cb5ae89821 100644 (file)
@@ -102,7 +102,6 @@ static int bus_message_new_polkit_auth_call(
 
 int bus_test_polkit(
                 sd_bus_message *call,
-                int capability,
                 const char *action,
                 const char **details,
                 uid_t good_user,
@@ -120,7 +119,7 @@ int bus_test_polkit(
         if (r != 0)
                 return r;
 
-        r = sd_bus_query_sender_privilege(call, capability);
+        r = sd_bus_query_sender_privilege(call, -1);
         if (r < 0)
                 return r;
         if (r > 0)
@@ -465,12 +464,11 @@ static int async_polkit_query_check_action(
  * <- async_polkit_defer(q)
  */
 
-int bus_verify_polkit_async(
+int bus_verify_polkit_async_full(
                 sd_bus_message *call,
-                int capability,
                 const char *action,
                 const char **details,
-                bool interactive,
+                bool interactive, /* Use only for legacy method calls that have a separate "allow_interactive_authentication" field */
                 uid_t good_user,
                 Hashmap **registry,
                 sd_bus_error *ret_error) {
@@ -499,7 +497,7 @@ int bus_verify_polkit_async(
         }
 #endif
 
-        r = sd_bus_query_sender_privilege(call, capability);
+        r = sd_bus_query_sender_privilege(call, -1);
         if (r < 0)
                 return r;
         if (r > 0)
index e2a3b7eef6659aa0bd1beb131649a432e462e63e..d82ac4679c617ee70cb024aadb9b0c1d77b74871 100644 (file)
@@ -4,8 +4,13 @@
 #include "sd-bus.h"
 
 #include "hashmap.h"
+#include "user-util.h"
 
-int bus_test_polkit(sd_bus_message *call, int capability, const char *action, const char **details, uid_t good_user, bool *_challenge, sd_bus_error *e);
+int bus_test_polkit(sd_bus_message *call, const char *action, const char **details, uid_t good_user, bool *_challenge, sd_bus_error *e);
+
+int bus_verify_polkit_async_full(sd_bus_message *call, const char *action, const char **details, bool interactive, uid_t good_user, Hashmap **registry, sd_bus_error *error);
+static inline int bus_verify_polkit_async(sd_bus_message *call, const char *action, const char **details, Hashmap **registry, sd_bus_error *ret_error) {
+        return bus_verify_polkit_async_full(call, action, details, false, UID_INVALID, registry, ret_error);
+}
 
-int bus_verify_polkit_async(sd_bus_message *call, int capability, const char *action, const char **details, bool interactive, uid_t good_user, Hashmap **registry, sd_bus_error *error);
 Hashmap *bus_verify_polkit_async_registry_free(Hashmap *registry);
index 969c62979f046b32eae51b3408384f3822917733..b3280c15dfede0e5a974c56e6565375d9bb2fdae 100644 (file)
@@ -227,12 +227,12 @@ finish:
                          service_shell_quoted ?: "<service>");
 }
 
-static int check_wait_response(BusWaitForJobs *d, bool quiet, const char* const* extra_args) {
+static int check_wait_response(BusWaitForJobs *d, WaitJobsFlags flags, const char* const* extra_args) {
         assert(d);
         assert(d->name);
         assert(d->result);
 
-        if (!quiet) {
+        if (FLAGS_SET(flags, BUS_WAIT_JOBS_LOG_ERROR)) {
                 if (streq(d->result, "canceled"))
                         log_error("Job for %s canceled.", strna(d->name));
                 else if (streq(d->result, "timeout"))
@@ -279,14 +279,21 @@ static int check_wait_response(BusWaitForJobs *d, bool quiet, const char* const*
                 return -EOPNOTSUPP;
         else if (streq(d->result, "once"))
                 return -ESTALE;
-        else if (STR_IN_SET(d->result, "done", "skipped"))
+        else if (streq(d->result, "done")) {
+                if (FLAGS_SET(flags, BUS_WAIT_JOBS_LOG_SUCCESS))
+                        log_info("Job for %s finished.", strna(d->name));
                 return 0;
+        } else if (streq(d->result, "skipped")) {
+                if (FLAGS_SET(flags, BUS_WAIT_JOBS_LOG_SUCCESS))
+                        log_info("Job for %s was skipped.", strna(d->name));
+                return 0;
+        }
 
         return log_debug_errno(SYNTHETIC_ERRNO(EIO),
                                "Unexpected job result, assuming server side newer than us: %s", d->result);
 }
 
-int bus_wait_for_jobs(BusWaitForJobs *d, bool quiet, const char* const* extra_args) {
+int bus_wait_for_jobs(BusWaitForJobs *d, WaitJobsFlags flags, const char* const* extra_args) {
         int r = 0;
 
         assert(d);
@@ -299,7 +306,7 @@ int bus_wait_for_jobs(BusWaitForJobs *d, bool quiet, const char* const* extra_ar
                         return log_error_errno(q, "Failed to wait for response: %m");
 
                 if (d->name && d->result) {
-                        q = check_wait_response(d, quiet, extra_args);
+                        q = check_wait_response(d, flags, extra_args);
                         /* Return the first error as it is most likely to be
                          * meaningful. */
                         if (q < 0 && r == 0)
@@ -322,12 +329,12 @@ int bus_wait_for_jobs_add(BusWaitForJobs *d, const char *path) {
         return set_put_strdup(&d->jobs, path);
 }
 
-int bus_wait_for_jobs_one(BusWaitForJobs *d, const char *path, bool quiet, const char* const* extra_args) {
+int bus_wait_for_jobs_one(BusWaitForJobs *d, const char *path, WaitJobsFlags flags, const char* const* extra_args) {
         int r;
 
         r = bus_wait_for_jobs_add(d, path);
         if (r < 0)
                 return log_oom();
 
-        return bus_wait_for_jobs(d, quiet, extra_args);
+        return bus_wait_for_jobs(d, flags, extra_args);
 }
index 5acf8b9241d0b82ab97f93b55031310feb8ba03c..4d56c079ab354fd7959f9136f2fe04dd49da5a6f 100644 (file)
@@ -5,12 +5,17 @@
 
 #include "macro.h"
 
+typedef enum WaitJobsFlags {
+        BUS_WAIT_JOBS_LOG_ERROR   = 1 << 0,
+        BUS_WAIT_JOBS_LOG_SUCCESS = 1 << 1,
+} WaitJobsFlags;
+
 typedef struct BusWaitForJobs BusWaitForJobs;
 
 int bus_wait_for_jobs_new(sd_bus *bus, BusWaitForJobs **ret);
 BusWaitForJobs* bus_wait_for_jobs_free(BusWaitForJobs *d);
 int bus_wait_for_jobs_add(BusWaitForJobs *d, const char *path);
-int bus_wait_for_jobs(BusWaitForJobs *d, bool quiet, const char* const* extra_args);
-int bus_wait_for_jobs_one(BusWaitForJobs *d, const char *path, bool quiet, const char* const* extra_args);
+int bus_wait_for_jobs(BusWaitForJobs *d, WaitJobsFlags flags, const char* const* extra_args);
+int bus_wait_for_jobs_one(BusWaitForJobs *d, const char *path, WaitJobsFlags flags, const char* const* extra_args);
 
 DEFINE_TRIVIAL_CLEANUP_FUNC(BusWaitForJobs*, bus_wait_for_jobs_free);
index 811f129f6cd9bc4747af683b0a792fa7c0286a3f..934a16eaf389e249d21918ba4a32192949d90fce 100644 (file)
@@ -421,6 +421,8 @@ int cg_set_access(
                 { "cgroup.procs",           true  },
                 { "cgroup.subtree_control", true  },
                 { "cgroup.threads",         false },
+                { "memory.oom.group",       false },
+                { "memory.reclaim",         false },
                 {},
         };
 
diff --git a/src/shared/color-util.c b/src/shared/color-util.c
new file mode 100644 (file)
index 0000000..776445e
--- /dev/null
@@ -0,0 +1,77 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <math.h>
+
+#include "color-util.h"
+#include "macro.h"
+
+void rgb_to_hsv(double r, double g, double b,
+                double *ret_h, double *ret_s, double *ret_v) {
+
+        assert(r >= 0 && r <= 1);
+        assert(g >= 0 && g <= 1);
+        assert(b >= 0 && b <= 1);
+        assert(ret_h);
+        assert(ret_s);
+        assert(ret_v);
+
+        double max_color = fmax(r, fmax(g, b));
+        double min_color = fmin(r, fmin(g, b));
+        double delta = max_color - min_color;
+
+        *ret_v = max_color * 100.0;
+
+        if (max_color > 0)
+                *ret_s = delta / max_color * 100.0;
+        else {
+                *ret_s = 0;
+                *ret_h = NAN;
+                return;
+        }
+
+        if (delta > 0) {
+                if (r >= max_color)
+                        *ret_h = 60 * fmod((g - b) / delta, 6);
+                else if (g >= max_color)
+                        *ret_h = 60 * (((b - r) / delta) + 2);
+                else if (b >= max_color)
+                        *ret_h = 60 * (((r - g) / delta) + 4);
+
+                *ret_h = fmod(*ret_h, 360);
+        } else
+                *ret_h = NAN;
+}
+
+void hsv_to_rgb(double h, double s, double v,
+                uint8_t* ret_r, uint8_t *ret_g, uint8_t *ret_b) {
+
+        double c, x, m, r, g, b;
+
+        assert(s >= 0 && s <= 100);
+        assert(v >= 0 && v <= 100);
+        assert(ret_r);
+        assert(ret_g);
+        assert(ret_b);
+
+        h = fmod(h, 360);
+        c = (s / 100.0) * (v / 100.0);
+        x = c * (1 - fabs(fmod(h / 60.0, 2) - 1));
+        m = (v / 100) - c;
+
+        if (h >= 0 && h < 60)
+                r = c, g = x, b = 0.0;
+        else if (h >= 60 && h < 120)
+                r = x, g = c, b = 0.0;
+        else if (h >= 120 && h < 180)
+                r = 0.0, g = c, b = x;
+        else if (h >= 180 && h < 240)
+                r = 0.0, g = x, b = c;
+        else if (h >= 240 && h < 300)
+                r = x, g = 0.0, b = c;
+        else
+                r = c, g = 0.0, b = x;
+
+        *ret_r = (uint8_t) ((r + m) * 255);
+        *ret_g = (uint8_t) ((g + m) * 255);
+        *ret_b = (uint8_t) ((b + m) * 255);
+}
diff --git a/src/shared/color-util.h b/src/shared/color-util.h
new file mode 100644 (file)
index 0000000..a4ae9eb
--- /dev/null
@@ -0,0 +1,11 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include <inttypes.h>
+
+void rgb_to_hsv(double r, double g, double b,
+                double *ret_h, double *ret_s, double *ret_v);
+
+void hsv_to_rgb(
+                double h, double s, double v,
+                uint8_t* ret_r, uint8_t *ret_g, uint8_t *ret_b);
index 7cc8889595318beb414d0b10095102c5bd952ee9..0026da5b48352fcabc7ebd73caeb0245989fad5a 100644 (file)
@@ -100,6 +100,17 @@ int get_encrypted_credentials_dir(const char **ret) {
         return get_credentials_dir_internal("ENCRYPTED_CREDENTIALS_DIRECTORY", ret);
 }
 
+int open_credentials_dir(void) {
+        const char *d;
+        int r;
+
+        r = get_credentials_dir(&d);
+        if (r < 0)
+                return r;
+
+        return RET_NERRNO(open(d, O_CLOEXEC|O_DIRECTORY));
+}
+
 int read_credential(const char *name, void **ret, size_t *ret_size) {
         _cleanup_free_ char *fn = NULL;
         const char *d;
index 5e39a6a022f6dc159e2b50627dbf9ec8b8412229..36ca0fb61009a8b18e559267d12a900c5b228c72 100644 (file)
@@ -31,6 +31,8 @@ bool credential_glob_valid(const char *s);
 int get_credentials_dir(const char **ret);
 int get_encrypted_credentials_dir(const char **ret);
 
+int open_credentials_dir(void);
+
 /* Where creds have been passed to the system */
 #define SYSTEM_CREDENTIALS_DIRECTORY "/run/credentials/@system"
 #define ENCRYPTED_SYSTEM_CREDENTIALS_DIRECTORY "/run/credentials/@encrypted"
index 348880cac882e7f458e3a3187d5daa11a3350f3a..b3f4c9ab766cb0f61664b58c030de13f637fa218 100644 (file)
@@ -13,6 +13,7 @@
 #include <unistd.h>
 
 #include "alloc-util.h"
+#include "blockdev-util.h"
 #include "btrfs-util.h"
 #include "chase.h"
 #include "chattr-util.h"
@@ -446,8 +447,9 @@ static int image_make(
                                         read_only = true;
                         }
 
-                        if (ioctl(block_fd, BLKGETSIZE64, &size) < 0)
-                                log_debug_errno(errno, "Failed to issue BLKGETSIZE64 on device %s/%s, ignoring: %m", path ?: strnull(parent), filename);
+                        r = blockdev_get_device_size(block_fd, &size);
+                        if (r < 0)
+                                log_debug_errno(r, "Failed to issue BLKGETSIZE64 on device %s/%s, ignoring: %m", path ?: strnull(parent), filename);
 
                         block_fd = safe_close(block_fd);
                 }
index 2687eafaf6b70fe9edc217f98458eb74a7ad3058..98690367a15386d47af1924bd3aed06b789d04ed 100644 (file)
@@ -1547,6 +1547,7 @@ int dissect_image_file(
 #if HAVE_BLKID
         _cleanup_(dissected_image_unrefp) DissectedImage *m = NULL;
         _cleanup_close_ int fd = -EBADF;
+        struct stat st;
         int r;
 
         assert(path);
@@ -1555,7 +1556,10 @@ int dissect_image_file(
         if (fd < 0)
                 return -errno;
 
-        r = fd_verify_regular(fd);
+        if (fstat(fd, &st) < 0)
+                return -errno;
+
+        r = stat_verify_regular(&st);
         if (r < 0)
                 return r;
 
@@ -1563,6 +1567,8 @@ int dissect_image_file(
         if (r < 0)
                 return r;
 
+        m->image_size = st.st_size;
+
         r = probe_sector_size(fd, &m->sector_size);
         if (r < 0)
                 return r;
@@ -1759,8 +1765,9 @@ static int fs_grow(const char *node_path, int mount_fd, const char *mount_path)
         if (node_fd < 0)
                 return log_debug_errno(errno, "Failed to open node device %s: %m", node_path);
 
-        if (ioctl(node_fd, BLKGETSIZE64, &size) != 0)
-                return log_debug_errno(errno, "Failed to get block device size of %s: %m", node_path);
+        r = blockdev_get_device_size(node_fd, &size);
+        if (r < 0)
+                return log_debug_errno(r, "Failed to get block device size of %s: %m", node_path);
 
         if (mount_fd < 0) {
                 assert(mount_path);
@@ -2179,7 +2186,7 @@ int dissected_image_mount(
                         if (r > 0)
                                 ok = true;
                 }
-                if (!ok && FLAGS_SET(flags, DISSECT_IMAGE_VALIDATE_OS_EXT)) {
+                if (!ok && FLAGS_SET(flags, DISSECT_IMAGE_VALIDATE_OS_EXT) && m->image_name) {
                         r = extension_has_forbidden_content(where);
                         if (r < 0)
                                 return r;
@@ -3390,10 +3397,7 @@ int dissected_image_acquire_metadata(DissectedImage *m, DissectImageFlags extra_
         assert(m);
 
         for (; n_meta_initialized < _META_MAX; n_meta_initialized ++) {
-                if (!paths[n_meta_initialized]) {
-                        fds[2*n_meta_initialized] = fds[2*n_meta_initialized+1] = -EBADF;
-                        continue;
-                }
+                assert(paths[n_meta_initialized]);
 
                 if (pipe2(fds + 2*n_meta_initialized, O_CLOEXEC) < 0) {
                         r = -errno;
@@ -3435,14 +3439,16 @@ int dissected_image_acquire_metadata(DissectedImage *m, DissectImageFlags extra_
                 for (unsigned k = 0; k < _META_MAX; k++) {
                         _cleanup_close_ int fd = -ENOENT;
 
-                        if (!paths[k])
-                                continue;
+                        assert(paths[k]);
 
                         fds[2*k] = safe_close(fds[2*k]);
 
                         switch (k) {
 
                         case META_SYSEXT_RELEASE:
+                                if (!m->image_name)
+                                        goto next;
+
                                 /* As per the os-release spec, if the image is an extension it will have a
                                  * file named after the image name in extension-release.d/ - we use the image
                                  * name and try to resolve it with the extension-release helpers, as
@@ -3463,6 +3469,9 @@ int dissected_image_acquire_metadata(DissectedImage *m, DissectImageFlags extra_
                                 break;
 
                         case META_CONFEXT_RELEASE:
+                                if (!m->image_name)
+                                        goto next;
+
                                 /* As above */
                                 r = open_extension_release(
                                                 t,
@@ -3498,7 +3507,7 @@ int dissected_image_acquire_metadata(DissectedImage *m, DissectImageFlags extra_
                                 if (r < 0)
                                         goto inner_fail;
 
-                                continue;
+                                goto next;
                         }
 
                         default:
@@ -3511,14 +3520,14 @@ int dissected_image_acquire_metadata(DissectedImage *m, DissectImageFlags extra_
 
                         if (fd < 0) {
                                 log_debug_errno(fd, "Failed to read %s file of image, ignoring: %m", paths[k]);
-                                fds[2*k+1] = safe_close(fds[2*k+1]);
-                                continue;
+                                goto next;
                         }
 
                         r = copy_bytes(fd, fds[2*k+1], UINT64_MAX, 0);
                         if (r < 0)
                                 goto inner_fail;
 
+                next:
                         fds[2*k+1] = safe_close(fds[2*k+1]);
                 }
 
@@ -3535,8 +3544,7 @@ int dissected_image_acquire_metadata(DissectedImage *m, DissectImageFlags extra_
         for (unsigned k = 0; k < _META_MAX; k++) {
                 _cleanup_fclose_ FILE *f = NULL;
 
-                if (!paths[k])
-                        continue;
+                assert(paths[k]);
 
                 fds[2*k+1] = safe_close(fds[2*k+1]);
 
@@ -3628,18 +3636,25 @@ int dissected_image_acquire_metadata(DissectedImage *m, DissectImageFlags extra_
         r = wait_for_terminate_and_check("(sd-dissect)", child, 0);
         child = 0;
         if (r < 0)
-                return r;
+                goto finish;
 
         n = read(error_pipe[0], &v, sizeof(v));
-        if (n < 0)
-                return -errno;
-        if (n == sizeof(v))
-                return v; /* propagate error sent to us from child */
-        if (n != 0)
-                return -EIO;
-
-        if (r != EXIT_SUCCESS)
-                return -EPROTO;
+        if (n < 0) {
+                r = -errno;
+                goto finish;
+        }
+        if (n == sizeof(v)) {
+                r = v; /* propagate error sent to us from child */
+                goto finish;
+        }
+        if (n != 0) {
+                r = -EIO;
+                goto finish;
+        }
+        if (r != EXIT_SUCCESS) {
+                r = -EPROTO;
+                goto finish;
+        }
 
         free_and_replace(m->hostname, hostname);
         m->machine_id = machine_id;
@@ -3690,6 +3705,7 @@ int dissect_loop_device(
                 return r;
 
         m->loop = loop_device_ref(loop);
+        m->image_size = m->loop->device_size;
         m->sector_size = m->loop->sector_size;
 
         r = dissect_image(m, loop->fd, loop->node, verity, mount_options, image_policy, flags);
index 979fd384fe665d2b0fe30673365d5de5bc2e39b6..ed02049ed0c7190768a22c4e1a2cc3340f880658 100644 (file)
@@ -102,10 +102,12 @@ struct DissectedImage {
         DecryptedImage *decrypted_image;
 
         uint32_t sector_size;
+        uint64_t image_size;
 
-        /* Meta information extracted from /etc/os-release and similar */
         char *image_name;
         sd_id128_t image_uuid;
+
+        /* Meta information extracted from /etc/os-release and similar */
         char *hostname;
         sd_id128_t machine_id;
         char **machine_info;
index b41c9b06ca3bfa471eab87839b0bffc7435bab0c..909b4cdcc93aeedce4d5e43deedea64b26ad8c3b 100644 (file)
@@ -410,7 +410,7 @@ int dns_name_concat(const char *a, const char *b, DNSLabelFlags flags, char **_r
                 goto finish;
 
         for (;;) {
-                char label[DNS_LABEL_MAX];
+                char label[DNS_LABEL_MAX+1];
 
                 r = dns_label_unescape(&p, label, sizeof label, flags);
                 if (r < 0)
@@ -507,7 +507,7 @@ int dns_name_compare_func(const char *a, const char *b) {
         y = b + strlen(b);
 
         for (;;) {
-                char la[DNS_LABEL_MAX], lb[DNS_LABEL_MAX];
+                char la[DNS_LABEL_MAX+1], lb[DNS_LABEL_MAX+1];
 
                 if (x == NULL && y == NULL)
                         return 0;
@@ -543,7 +543,7 @@ int dns_name_equal(const char *x, const char *y) {
         assert(y);
 
         for (;;) {
-                char la[DNS_LABEL_MAX], lb[DNS_LABEL_MAX];
+                char la[DNS_LABEL_MAX+1], lb[DNS_LABEL_MAX+1];
 
                 r = dns_label_unescape(&x, la, sizeof la, 0);
                 if (r < 0)
@@ -574,7 +574,7 @@ int dns_name_endswith(const char *name, const char *suffix) {
         s = suffix;
 
         for (;;) {
-                char ln[DNS_LABEL_MAX], ls[DNS_LABEL_MAX];
+                char ln[DNS_LABEL_MAX+1], ls[DNS_LABEL_MAX+1];
 
                 r = dns_label_unescape(&n, ln, sizeof ln, 0);
                 if (r < 0)
@@ -612,7 +612,7 @@ int dns_name_startswith(const char *name, const char *prefix) {
         p = prefix;
 
         for (;;) {
-                char ln[DNS_LABEL_MAX], lp[DNS_LABEL_MAX];
+                char ln[DNS_LABEL_MAX+1], lp[DNS_LABEL_MAX+1];
 
                 r = dns_label_unescape(&p, lp, sizeof lp, 0);
                 if (r < 0)
@@ -644,7 +644,7 @@ int dns_name_change_suffix(const char *name, const char *old_suffix, const char
         s = old_suffix;
 
         for (;;) {
-                char ln[DNS_LABEL_MAX], ls[DNS_LABEL_MAX];
+                char ln[DNS_LABEL_MAX+1], ls[DNS_LABEL_MAX+1];
 
                 if (!saved_before)
                         saved_before = n;
@@ -929,7 +929,7 @@ bool dns_srv_type_is_valid(const char *name) {
                 return false;
 
         for (;;) {
-                char label[DNS_LABEL_MAX];
+                char label[DNS_LABEL_MAX+1];
 
                 /* This more or less implements RFC 6335, Section 5.1 */
 
@@ -1227,7 +1227,7 @@ int dns_name_common_suffix(const char *a, const char *b, const char **ret) {
                 return m;
 
         for (;;) {
-                char la[DNS_LABEL_MAX], lb[DNS_LABEL_MAX];
+                char la[DNS_LABEL_MAX+1], lb[DNS_LABEL_MAX+1];
                 const char *x, *y;
 
                 if (k >= n || k >= m) {
@@ -1328,7 +1328,7 @@ int dns_name_apply_idna(const char *name, char **ret) {
         assert(ret);
 
         for (;;) {
-                char label[DNS_LABEL_MAX];
+                char label[DNS_LABEL_MAX+1];
 
                 r = dns_label_unescape(&name, label, sizeof label, 0);
                 if (r < 0)
index 9b8c7f73bcd03a6ef44dbbf833c0063a53b15f0c..db87084a4da83861a897e99777c4ea8f4e89d1b4 100644 (file)
 typedef enum VerifyESPFlags {
         VERIFY_ESP_SEARCHING         = 1 << 0, /* Downgrade various "not found" logs to debug level */
         VERIFY_ESP_UNPRIVILEGED_MODE = 1 << 1, /* Call into udev rather than blkid */
-        VERIFY_ESP_RELAX_CHECKS      = 1 << 2, /* Do not validate ESP partition */
+        VERIFY_ESP_SKIP_FSTYPE_CHECK = 1 << 2, /* Skip filesystem check */
+        VERIFY_ESP_SKIP_DEVICE_CHECK = 1 << 3, /* Skip device node check  */
 } VerifyESPFlags;
 
+static VerifyESPFlags verify_esp_flags_init(int unprivileged_mode, const char *env_name_for_relaxing) {
+        VerifyESPFlags flags = 0;
+        int r;
+
+        assert(env_name_for_relaxing);
+
+        if (unprivileged_mode < 0)
+                unprivileged_mode = geteuid() != 0;
+        if (unprivileged_mode)
+                flags |= VERIFY_ESP_UNPRIVILEGED_MODE;
+
+        r = getenv_bool(env_name_for_relaxing);
+        if (r < 0 && r != -ENXIO)
+                log_debug_errno(r, "Failed to parse $%s environment variable, assuming false.", env_name_for_relaxing);
+        else if (r > 0)
+                flags |= VERIFY_ESP_SKIP_FSTYPE_CHECK | VERIFY_ESP_SKIP_DEVICE_CHECK;
+
+        if (detect_container() > 0)
+                flags |= VERIFY_ESP_SKIP_DEVICE_CHECK;
+
+        return flags;
+}
+
 static int verify_esp_blkid(
                 dev_t devid,
                 VerifyESPFlags flags,
@@ -326,8 +350,8 @@ static int verify_esp(
                 dev_t *ret_devid,
                 VerifyESPFlags flags) {
 
-        bool relax_checks, searching = FLAGS_SET(flags, VERIFY_ESP_SEARCHING),
-             unprivileged_mode = FLAGS_SET(flags, VERIFY_ESP_UNPRIVILEGED_MODE);
+        bool searching = FLAGS_SET(flags, VERIFY_ESP_SEARCHING),
+                unprivileged_mode = FLAGS_SET(flags, VERIFY_ESP_UNPRIVILEGED_MODE);
         _cleanup_free_ char *p = NULL;
         _cleanup_close_ int pfd = -EBADF;
         dev_t devid = 0;
@@ -343,10 +367,6 @@ static int verify_esp(
          *  -EACESS        → if 'unprivileged_mode' is set, and we have trouble accessing the thing
          */
 
-        relax_checks =
-                getenv_bool("SYSTEMD_RELAX_ESP_CHECKS") > 0 ||
-                FLAGS_SET(flags, VERIFY_ESP_RELAX_CHECKS);
-
         /* Non-root user can only check the status, so if an error occurred in the following, it does not cause any
          * issues. Let's also, silence the error messages. */
 
@@ -356,7 +376,7 @@ static int verify_esp(
                                       (unprivileged_mode && ERRNO_IS_PRIVILEGE(r)) ? LOG_DEBUG : LOG_ERR,
                                       r, "Failed to open parent directory of \"%s\": %m", path);
 
-        if (!relax_checks) {
+        if (!FLAGS_SET(flags, VERIFY_ESP_SKIP_FSTYPE_CHECK)) {
                 _cleanup_free_ char *f = NULL;
                 struct statfs sfs;
 
@@ -383,19 +403,20 @@ static int verify_esp(
                                               "File system \"%s\" is not a FAT EFI System Partition (ESP) file system.", p);
         }
 
-        relax_checks =
-                relax_checks ||
-                detect_container() > 0;
-
-        r = verify_fsroot_dir(pfd, p, flags, relax_checks ? NULL : &devid);
+        r = verify_fsroot_dir(pfd, p, flags, FLAGS_SET(flags, VERIFY_ESP_SKIP_DEVICE_CHECK) ? NULL : &devid);
         if (r < 0)
                 return r;
 
         /* In a container we don't have access to block devices, skip this part of the verification, we trust
          * the container manager set everything up correctly on its own. */
-        if (relax_checks)
+        if (FLAGS_SET(flags, VERIFY_ESP_SKIP_DEVICE_CHECK))
                 goto finish;
 
+        if (devnum_is_zero(devid))
+                return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
+                                      SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
+                                      "Could not determine backing block device of directory \"%s\" (btrfs RAID?).", p);
+
         /* If we are unprivileged we ask udev for the metadata about the partition. If we are privileged we
          * use blkid instead. Why? Because this code is called from 'bootctl' which is pretty much an
          * emergency recovery tool that should also work when udev isn't up (i.e. from the emergency shell),
@@ -454,15 +475,7 @@ int find_esp_and_warn_at(
 
         assert(rfd >= 0 || rfd == AT_FDCWD);
 
-        if (unprivileged_mode < 0)
-                unprivileged_mode = geteuid() != 0;
-        flags = unprivileged_mode > 0 ? VERIFY_ESP_UNPRIVILEGED_MODE : 0;
-
-        r = dir_fd_is_root_or_cwd(rfd);
-        if (r < 0)
-                return log_error_errno(r, "Failed to check if directory file descriptor is root: %m");
-        if (r == 0)
-                flags |= VERIFY_ESP_RELAX_CHECKS;
+        flags = verify_esp_flags_init(unprivileged_mode, "SYSTEMD_RELAX_ESP_CHECKS");
 
         if (path)
                 return verify_esp(rfd, path, ret_path, ret_part, ret_pstart, ret_psize, ret_uuid, ret_devid, flags);
@@ -742,8 +755,7 @@ static int verify_xbootldr(
         _cleanup_free_ char *p = NULL;
         _cleanup_close_ int pfd = -EBADF;
         bool searching = FLAGS_SET(flags, VERIFY_ESP_SEARCHING),
-                unprivileged_mode = FLAGS_SET(flags, VERIFY_ESP_UNPRIVILEGED_MODE),
-                relax_checks;
+                unprivileged_mode = FLAGS_SET(flags, VERIFY_ESP_UNPRIVILEGED_MODE);
         dev_t devid = 0;
         int r;
 
@@ -756,17 +768,22 @@ static int verify_xbootldr(
                                       (unprivileged_mode && ERRNO_IS_PRIVILEGE(r)) ? LOG_DEBUG : LOG_ERR,
                                       r, "Failed to open parent directory of \"%s\": %m", path);
 
-        relax_checks =
-                getenv_bool("SYSTEMD_RELAX_XBOOTLDR_CHECKS") > 0 ||
-                detect_container() > 0;
-
-        r = verify_fsroot_dir(pfd, p, flags, relax_checks ? NULL : &devid);
+        r = verify_fsroot_dir(pfd, p, flags, FLAGS_SET(flags, VERIFY_ESP_SKIP_DEVICE_CHECK) ? NULL : &devid);
         if (r < 0)
                 return r;
 
-        if (relax_checks)
+        if (FLAGS_SET(flags, VERIFY_ESP_SKIP_DEVICE_CHECK))
                 goto finish;
 
+        if (devnum_is_zero(devid))
+                return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
+                                      SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
+                                      "Could not determine backing block device of directory \"%s\" (btrfs RAID?).%s",
+                                      p,
+                                      searching ? "" :
+                                      "\nHint: set $SYSTEMD_RELAX_XBOOTLDR_CHECKS=yes environment variable "
+                                      "to bypass this and further verifications for the directory.");
+
         if (unprivileged_mode)
                 r = verify_xbootldr_udev(devid, flags, ret_uuid);
         else
@@ -800,17 +817,14 @@ int find_xbootldr_and_warn_at(
                 sd_id128_t *ret_uuid,
                 dev_t *ret_devid) {
 
-        VerifyESPFlags flags = 0;
+        VerifyESPFlags flags;
         int r;
 
         /* Similar to find_esp_and_warn(), but finds the XBOOTLDR partition. Returns the same errors. */
 
         assert(rfd >= 0 || rfd == AT_FDCWD);
 
-        if (unprivileged_mode < 0)
-                unprivileged_mode = geteuid() != 0;
-        if (unprivileged_mode)
-                flags |= VERIFY_ESP_UNPRIVILEGED_MODE;
+        flags = verify_esp_flags_init(unprivileged_mode, "SYSTEMD_RELAX_XBOOTLDR_CHECKS");
 
         if (path)
                 return verify_xbootldr(rfd, path, flags, ret_path, ret_uuid, ret_devid);
index f67e917b533c36316a4bb8b7aea6ca7a3774e053..648bfc502655ee8df566de33ccc52edb3dea1627 100644 (file)
@@ -6,6 +6,7 @@
 
 #include "alloc-util.h"
 #include "conf-files.h"
+#include "env-util.h"
 #include "fd-util.h"
 #include "fileio.h"
 #include "fs-util.h"
@@ -710,3 +711,16 @@ bool hwdb_should_reload(sd_hwdb *hwdb) {
                 return true;
         return false;
 }
+
+int hwdb_bypass(void) {
+        int r;
+
+        r = getenv_bool("SYSTEMD_HWDB_UPDATE_BYPASS");
+        if (r < 0 && r != -ENXIO)
+                log_debug_errno(r, "Failed to parse $SYSTEMD_HWDB_UPDATE_BYPASS, assuming no.");
+        if (r <= 0)
+                return false;
+
+        log_debug("$SYSTEMD_HWDB_UPDATE_BYPASS is enabled, skipping execution.");
+        return true;
+}
index cb93690ee8ddadb5e3e410cfffbeec6ef4793082..00610b18d49364158e4ed6574b20f6fa38bba6d7 100644 (file)
@@ -8,3 +8,4 @@
 bool hwdb_should_reload(sd_hwdb *hwdb);
 int hwdb_update(const char *root, const char *hwdb_bin_dir, bool strict, bool compat);
 int hwdb_query(const char *modalias, const char *root);
+int hwdb_bypass(void);
index 97707e50b406aed75039194e8b8e9520a66a1c08..265407f080e1e158ed9318c419b584081c42d12c 100644 (file)
@@ -747,7 +747,7 @@ static int remove_marked_symlinks(
         assert(config_path);
         assert(lp);
 
-        if (set_size(remove_symlinks_to) <= 0)
+        if (set_isempty(remove_symlinks_to))
                 return 0;
 
         fd = open(config_path, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC);
@@ -2271,13 +2271,13 @@ int unit_file_mask(
         if (!config_path)
                 return -ENXIO;
 
+        r = 0;
+
         STRV_FOREACH(name, names) {
                 _cleanup_free_ char *path = NULL;
-                int q;
 
                 if (!unit_name_is_valid(*name, UNIT_NAME_ANY)) {
-                        if (r == 0)
-                                r = -EINVAL;
+                        RET_GATHER(r, -EINVAL);
                         continue;
                 }
 
@@ -2285,9 +2285,7 @@ int unit_file_mask(
                 if (!path)
                         return -ENOMEM;
 
-                q = create_symlink(&lp, "/dev/null", path, flags & UNIT_FILE_FORCE, changes, n_changes);
-                if (q < 0 && r >= 0)
-                        r = q;
+                RET_GATHER(r, create_symlink(&lp, "/dev/null", path, flags & UNIT_FILE_FORCE, changes, n_changes));
         }
 
         return r;
@@ -2383,8 +2381,7 @@ int unit_file_unmask(
 
                 if (!dry_run && unlink(path) < 0) {
                         if (errno != ENOENT) {
-                                if (r >= 0)
-                                        r = -errno;
+                                RET_GATHER(r, -errno);
                                 install_changes_add(changes, n_changes, -errno, path, NULL);
                         }
 
@@ -2401,9 +2398,7 @@ int unit_file_unmask(
                         return q;
         }
 
-        q = remove_marked_symlinks(remove_symlinks_to, config_path, &lp, dry_run, changes, n_changes);
-        if (r >= 0)
-                r = q;
+        RET_GATHER(r, remove_marked_symlinks(remove_symlinks_to, config_path, &lp, dry_run, changes, n_changes));
 
         return r;
 }
index b11e9268aff5eb1c9acdd87c1ad7999c9ad3293c..8434587286cff9701ff21ba13e6e1cc3777bacc7 100644 (file)
@@ -149,8 +149,9 @@ static int loop_configure_verify(int fd, const struct loop_config *c) {
                  * effect hence. And if not use classic LOOP_SET_STATUS64. */
                 uint64_t z;
 
-                if (ioctl(fd, BLKGETSIZE64, &z) < 0)
-                        return -errno;
+                r = blockdev_get_device_size(fd, &z);
+                if (r < 0)
+                        return r;
 
                 if (z != c->info.lo_sizelimit) {
                         log_debug("LOOP_CONFIGURE is broken, doesn't honour .info.lo_sizelimit. Falling back to LOOP_SET_STATUS64.");
@@ -404,6 +405,11 @@ static int loop_configure(
                 assert_not_reached();
         }
 
+        uint64_t device_size;
+        r = blockdev_get_device_size(loop_with_fd, &device_size);
+        if (r < 0)
+                return log_device_debug_errno(dev, r, "Failed to get loopback device size: %m");
+
         LoopDevice *d = new(LoopDevice, 1);
         if (!d)
                 return log_oom_debug();
@@ -420,6 +426,8 @@ static int loop_configure(
                 .uevent_seqnum_not_before = seqnum,
                 .timestamp_not_before = timestamp,
                 .sector_size = c->block_size,
+                .device_size = device_size,
+                .created = true,
         };
 
         *ret = TAKE_PTR(d);
@@ -944,6 +952,11 @@ int loop_device_open(
         if (r < 0)
                 return r;
 
+        uint64_t device_size;
+        r = blockdev_get_device_size(fd, &device_size);
+        if (r < 0)
+                return r;
+
         r = sd_device_get_devnum(dev, &devnum);
         if (r < 0)
                 return r;
@@ -976,6 +989,8 @@ int loop_device_open(
                 .uevent_seqnum_not_before = UINT64_MAX,
                 .timestamp_not_before = USEC_INFINITY,
                 .sector_size = sector_size,
+                .device_size = device_size,
+                .created = false,
         };
 
         *ret = d;
@@ -1057,8 +1072,9 @@ static int resize_partition(int partition_fd, uint64_t offset, uint64_t size) {
                 return -EINVAL;
         current_offset *= 512U;
 
-        if (ioctl(partition_fd, BLKGETSIZE64, &current_size) < 0)
-                return -EINVAL;
+        r = blockdev_get_device_size(partition_fd, &current_size);
+        if (r < 0)
+                return r;
 
         if (size == UINT64_MAX && offset == UINT64_MAX)
                 return 0;
index d77c314af83530b997ddcf0a69f5c3b6219335da..94357bba71788e2b63e9dd54460fa9b124bcb09a 100644 (file)
@@ -22,12 +22,14 @@ struct LoopDevice {
         sd_device *dev;
         char *backing_file;
         bool relinquished;
+        bool created;        /* If we created the device */
         dev_t backing_devno; /* The backing file's dev_t */
         ino_t backing_inode; /* The backing file's ino_t */
         uint64_t diskseq; /* Block device sequence number, monothonically incremented by the kernel on create/attach, or 0 if we don't know */
         uint64_t uevent_seqnum_not_before; /* uevent sequm right before we attached the loopback device, or UINT64_MAX if we don't know */
         usec_t timestamp_not_before; /* CLOCK_MONOTONIC timestamp taken immediately before attaching the loopback device, or USEC_INFINITY if we don't know */
         uint32_t sector_size;
+        uint64_t device_size;
 };
 
 /* Returns true if LoopDevice object is not actually a loopback device but some other block device we just wrap */
index 17f7afc4a0fdf8cf0a8186b4f6e9a87a776fe087..c1e76e468e5285f1669ee2b88789578297310da2 100644 (file)
@@ -19,76 +19,82 @@ static void machine_credential_done(MachineCredential *cred) {
         cred->size = 0;
 }
 
-void machine_credential_free_all(MachineCredential *creds, size_t n) {
-        assert(creds || n == 0);
+void machine_credential_context_done(MachineCredentialContext *ctx) {
+        assert(ctx);
 
-        FOREACH_ARRAY(cred, creds, n)
+        FOREACH_ARRAY(cred, ctx->credentials, ctx->n_credentials)
                 machine_credential_done(cred);
 
-        free(creds);
+        free(ctx->credentials);
 }
 
-int machine_credential_set(MachineCredential **credentials, size_t *n_credentials, const char *cred_string) {
-        _cleanup_free_ char *word = NULL, *data = NULL;
+bool machine_credentials_contains(const MachineCredentialContext *ctx, const char *id) {
+        assert(ctx);
+        assert(id);
+
+        FOREACH_ARRAY(cred, ctx->credentials, ctx->n_credentials)
+                if (streq(cred->id, id))
+                        return true;
+
+        return false;
+}
+
+int machine_credential_set(MachineCredentialContext *ctx, const char *cred_str) {
+        _cleanup_(machine_credential_done) MachineCredential cred = {};
         ssize_t l;
         int r;
-        const char *p = ASSERT_PTR(cred_string);
 
-        assert(credentials && n_credentials);
-        assert(*credentials || *n_credentials == 0);
+        assert(ctx);
 
-        r = extract_first_word(&p, &word, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
+        const char *p = ASSERT_PTR(cred_str);
+
+        r = extract_first_word(&p, &cred.id, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
         if (r < 0)
                 return log_error_errno(r, "Failed to parse --set-credential= parameter: %m");
         if (r == 0 || !p)
-                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Missing value for --set-credential=: %s", cred_string);
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+                                       "Missing value for --set-credential=: %s", cred_str);
 
-        if (!credential_name_valid(word))
-                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Credential name is not valid: %s", word);
+        if (!credential_name_valid(cred.id))
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Credential name is not valid: %s", cred.id);
 
-        FOREACH_ARRAY(cred, *credentials, *n_credentials)
-                if (streq(cred->id, word))
-                        return log_error_errno(SYNTHETIC_ERRNO(EEXIST), "Duplicate credential '%s', refusing.", word);
+        if (machine_credentials_contains(ctx, cred.id))
+                return log_error_errno(SYNTHETIC_ERRNO(EEXIST), "Duplicate credential '%s', refusing.", cred.id);
 
-        l = cunescape(p, UNESCAPE_ACCEPT_NUL, &data);
+        l = cunescape(p, UNESCAPE_ACCEPT_NUL, &cred.data);
         if (l < 0)
                 return log_error_errno(l, "Failed to unescape credential data: %s", p);
+        cred.size = l;
 
-        if (!GREEDY_REALLOC(*credentials, *n_credentials + 1))
+        if (!GREEDY_REALLOC(ctx->credentials, ctx->n_credentials + 1))
                 return log_oom();
 
-        (*credentials)[(*n_credentials)++] = (MachineCredential) {
-                .id = TAKE_PTR(word),
-                .data = TAKE_PTR(data),
-                .size = l,
-        };
+        ctx->credentials[ctx->n_credentials++] = TAKE_STRUCT(cred);
 
         return 0;
 }
 
-int machine_credential_load(MachineCredential **credentials, size_t *n_credentials, const char *cred_path) {
+int machine_credential_load(MachineCredentialContext *ctx, const char *cred_path) {
+        _cleanup_(machine_credential_done) MachineCredential cred = {};
+        _cleanup_free_ char *path_alloc = NULL;
         ReadFullFileFlags flags = READ_FULL_FILE_SECURE;
-        _cleanup_(erase_and_freep) char *data = NULL;
-        _cleanup_free_ char *word = NULL, *j = NULL;
-        const char *p = ASSERT_PTR(cred_path);
-        size_t size;
         int r;
 
-        assert(credentials && n_credentials);
-        assert(*credentials || *n_credentials == 0);
+        assert(ctx);
+
+        const char *p = ASSERT_PTR(cred_path);
 
-        r = extract_first_word(&p, &word, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
+        r = extract_first_word(&p, &cred.id, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
         if (r < 0)
                 return log_error_errno(r, "Failed to parse --load-credential= parameter: %m");
         if (r == 0 || !p)
                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Missing value for --load-credential=: %s", cred_path);
 
-        if (!credential_name_valid(word))
-                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Credential name is not valid: %s", word);
+        if (!credential_name_valid(cred.id))
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Credential name is not valid: %s", cred.id);
 
-        FOREACH_ARRAY(cred, *credentials, *n_credentials)
-                if (streq(cred->id, word))
-                        return log_error_errno(SYNTHETIC_ERRNO(EEXIST), "Duplicate credential '%s', refusing.", word);
+        if (machine_credentials_contains(ctx, cred.id))
+                return log_error_errno(SYNTHETIC_ERRNO(EEXIST), "Duplicate credential '%s', refusing.", cred.id);
 
         if (is_path(p) && path_is_valid(p))
                 flags |= READ_FULL_FILE_CONNECT_SOCKET;
@@ -97,31 +103,29 @@ int machine_credential_load(MachineCredential **credentials, size_t *n_credentia
 
                 r = get_credentials_dir(&e);
                 if (r < 0)
-                        return log_error_errno(r, "Credential not available (no credentials passed at all): %s", word);
+                        return log_error_errno(r,
+                                               "Credential not available (no credentials passed at all): %s", cred.id);
 
-                j = path_join(e, p);
-                if (!j)
+                path_alloc = path_join(e, p);
+                if (!path_alloc)
                         return log_oom();
 
-                p = j;
+                p = path_alloc;
         } else
-                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Credential source appears to be neither a valid path nor a credential name: %s", p);
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+                                       "Credential source appears to be neither a valid path nor a credential name: %s", p);
 
         r = read_full_file_full(AT_FDCWD, p, UINT64_MAX, SIZE_MAX,
                                 flags,
                                 NULL,
-                                &data, &size);
+                                &cred.data, &cred.size);
         if (r < 0)
                 return log_error_errno(r, "Failed to read credential '%s': %m", p);
 
-        if (!GREEDY_REALLOC(*credentials, *n_credentials + 1))
+        if (!GREEDY_REALLOC(ctx->credentials, ctx->n_credentials + 1))
                 return log_oom();
 
-        (*credentials)[(*n_credentials)++] = (MachineCredential) {
-                .id = TAKE_PTR(word),
-                .data = TAKE_PTR(data),
-                .size = size,
-        };
+        ctx->credentials[ctx->n_credentials++] = TAKE_STRUCT(cred);
 
         return 0;
 }
index c9044a2edcc7fa903fa44e25746f70ffcd8a6d66..182f077fe7e4fbc01af5a5485dd4f7ed4d54e52e 100644 (file)
@@ -5,10 +5,18 @@
 
 typedef struct MachineCredential {
         char *id;
-        void *data;
+        char *data;
         size_t size;
 } MachineCredential;
 
-void machine_credential_free_all(MachineCredential *creds, size_t n);
-int machine_credential_set(MachineCredential **credentials, size_t *n_credentials, const char *cred_string);
-int machine_credential_load(MachineCredential **credentials, size_t *n_credentials, const char *cred_path);
+typedef struct MachineCredentialContext {
+        MachineCredential *credentials;
+        size_t n_credentials;
+} MachineCredentialContext;
+
+void machine_credential_context_done(MachineCredentialContext *ctx);
+
+bool machine_credentials_contains(const MachineCredentialContext *ctx, const char *id);
+
+int machine_credential_set(MachineCredentialContext *ctx, const char *cred_str);
+int machine_credential_load(MachineCredentialContext *ctx, const char *cred_path);
index b24a541de5d0bf64b1d6e09dc9b06549746e5411..bcd02d4cfb4e953c113496a44bd7080685be5ff5 100644 (file)
@@ -39,6 +39,7 @@ shared_sources = files(
         'chown-recursive.c',
         'clean-ipc.c',
         'clock-util.c',
+        'color-util.c',
         'common-signal.c',
         'compare-operator.c',
         'condition.c',
@@ -172,6 +173,7 @@ shared_sources = files(
         'varlink.c',
         'varlink-idl.c',
         'varlink-io.systemd.c',
+        'varlink-io.systemd.Credentials.c',
         'varlink-io.systemd.Journal.c',
         'varlink-io.systemd.ManagedOOM.c',
         'varlink-io.systemd.PCRExtend.c',
index 4f2acce513d4109ee0061f57d70a6111089fabbd..ba3a9e995d13102c31673bd15033861881432f60 100644 (file)
@@ -457,10 +457,6 @@ static int mount_switch_root_pivot(int fd_newroot, const char *path) {
         assert(fd_newroot >= 0);
         assert(path);
 
-        /* Change into the new rootfs. */
-        if (fchdir(fd_newroot) < 0)
-                return log_debug_errno(errno, "Failed to chdir into new rootfs '%s': %m", path);
-
         /* Let the kernel tuck the new root under the old one. */
         if (pivot_root(".", ".") < 0)
                 return log_debug_errno(errno, "Failed to pivot root to new rootfs '%s': %m", path);
@@ -477,10 +473,6 @@ static int mount_switch_root_move(int fd_newroot, const char *path) {
         assert(fd_newroot >= 0);
         assert(path);
 
-        /* Change into the new rootfs. */
-        if (fchdir(fd_newroot) < 0)
-                return log_debug_errno(errno, "Failed to chdir into new rootfs '%s': %m", path);
-
         /* Move the new root fs */
         if (mount(".", "/", NULL, MS_MOVE, NULL) < 0)
                 return log_debug_errno(errno, "Failed to move new rootfs '%s': %m", path);
@@ -494,7 +486,7 @@ static int mount_switch_root_move(int fd_newroot, const char *path) {
 
 int mount_switch_root_full(const char *path, unsigned long mount_propagation_flag, bool force_ms_move) {
         _cleanup_close_ int fd_newroot = -EBADF;
-        int r;
+        int r, is_current_root;
 
         assert(path);
         assert(mount_propagation_flag_is_valid(mount_propagation_flag));
@@ -503,19 +495,31 @@ int mount_switch_root_full(const char *path, unsigned long mount_propagation_fla
         if (fd_newroot < 0)
                 return log_debug_errno(errno, "Failed to open new rootfs '%s': %m", path);
 
-        if (!force_ms_move) {
-                r = mount_switch_root_pivot(fd_newroot, path);
-                if (r < 0) {
-                        log_debug_errno(r, "Failed to pivot into new rootfs '%s', will try to use MS_MOVE instead: %m", path);
-                        force_ms_move = true;
+        is_current_root = path_is_root_at(fd_newroot, NULL);
+        if (is_current_root < 0)
+                return log_debug_errno(is_current_root, "Failed to determine if target dir is our root already: %m");
+
+        /* Change into the new rootfs. */
+        if (fchdir(fd_newroot) < 0)
+                return log_debug_errno(errno, "Failed to chdir into new rootfs '%s': %m", path);
+
+        /* Make this a NOP if we are supposed to switch to our current root fs. After all, both pivot_root()
+         * and MS_MOVE don't like that. */
+        if (!is_current_root) {
+                if (!force_ms_move) {
+                        r = mount_switch_root_pivot(fd_newroot, path);
+                        if (r < 0) {
+                                log_debug_errno(r, "Failed to pivot into new rootfs '%s', will try to use MS_MOVE instead: %m", path);
+                                force_ms_move = true;
+                        }
+                }
+                if (force_ms_move) {
+                        /* Failed to pivot_root() fallback to MS_MOVE. For example, this may happen if the rootfs is
+                         * an initramfs in which case pivot_root() isn't supported. */
+                        r = mount_switch_root_move(fd_newroot, path);
+                        if (r < 0)
+                                return log_debug_errno(r, "Failed to switch to new rootfs '%s' with MS_MOVE: %m", path);
                 }
-        }
-        if (force_ms_move) {
-                /* Failed to pivot_root() fallback to MS_MOVE. For example, this may happen if the rootfs is
-                 * an initramfs in which case pivot_root() isn't supported. */
-                r = mount_switch_root_move(fd_newroot, path);
-                if (r < 0)
-                        return log_debug_errno(r, "Failed to switch to new rootfs '%s' with MS_MOVE: %m", path);
         }
 
         /* Finally, let's establish the requested propagation flags. */
index fbaf5c5a6081fe97b6bb74b7671115e6bef690ef..38c24760f0a9e60f1807f10fd27376a473a2470b 100644 (file)
@@ -1,6 +1,9 @@
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 
+#include "sd-device.h"
+
 #include "alloc-util.h"
+#include "device-private.h"
 #include "netif-naming-scheme.h"
 #include "proc-cmdline.h"
 #include "string-util.h"
@@ -101,3 +104,81 @@ static const char* const alternative_names_policy_table[_NAMEPOLICY_MAX] = {
 };
 
 DEFINE_STRING_TABLE_LOOKUP(alternative_names_policy, NamePolicy);
+
+static int naming_sysattr_allowed_by_default(sd_device *dev) {
+        int r;
+
+        assert(dev);
+
+        r = device_get_property_bool(dev, "ID_NET_NAME_ALLOW");
+        if (r == -ENOENT)
+                return true;
+
+        return r;
+}
+
+static int naming_sysattr_allowed(sd_device *dev, const char *sysattr) {
+        char *sysattr_property;
+        int r;
+
+        assert(dev);
+        assert(sysattr);
+
+        sysattr_property = strjoina("ID_NET_NAME_ALLOW_", sysattr);
+        ascii_strupper(sysattr_property);
+
+        r = device_get_property_bool(dev, sysattr_property);
+        if (r == -ENOENT)
+                /* If ID_NET_NAME_ALLOW is not set or set to 1 default is to allow */
+                return naming_sysattr_allowed_by_default(dev);
+
+        return r;
+}
+
+int device_get_sysattr_int_filtered(sd_device *device, const char *sysattr, int *ret_value) {
+        int r;
+
+        r = naming_sysattr_allowed(device, sysattr);
+        if (r < 0)
+                return r;
+        if (r == 0)
+                return -ENOENT;
+
+        return device_get_sysattr_int(device, sysattr, ret_value);
+}
+
+int device_get_sysattr_unsigned_filtered(sd_device *device, const char *sysattr, unsigned *ret_value) {
+        int r;
+
+        r = naming_sysattr_allowed(device, sysattr);
+        if (r < 0)
+                return r;
+        if (r == 0)
+                return -ENOENT;
+
+        return device_get_sysattr_unsigned(device, sysattr, ret_value);
+}
+
+int device_get_sysattr_bool_filtered(sd_device *device, const char *sysattr) {
+        int r;
+
+        r = naming_sysattr_allowed(device, sysattr);
+        if (r < 0)
+                return r;
+        if (r == 0)
+                return -ENOENT;
+
+        return device_get_sysattr_bool(device, sysattr);
+}
+
+int device_get_sysattr_value_filtered(sd_device *device, const char *sysattr, const char **ret_value) {
+        int r;
+
+        r = naming_sysattr_allowed(device, sysattr);
+        if (r < 0)
+                return r;
+        if (r == 0)
+                return -ENOENT;
+
+        return sd_device_get_sysattr_value(device, sysattr, ret_value);
+}
index 3f7be0883056121ff9843ec2c48f64cb2ff21d1f..62afdc514a9b5c19224871ebb98b786088272a52 100644 (file)
@@ -3,6 +3,8 @@
 
 #include <stdbool.h>
 
+#include "sd-device.h"
+
 #include "macro.h"
 
 /* So here's the deal: net_id is supposed to be an exercise in providing stable names for network devices. However, we
@@ -95,3 +97,8 @@ NamePolicy name_policy_from_string(const char *p) _pure_;
 
 const char *alternative_names_policy_to_string(NamePolicy p) _const_;
 NamePolicy alternative_names_policy_from_string(const char *p) _pure_;
+
+int device_get_sysattr_int_filtered(sd_device *device, const char *sysattr, int *ret_value);
+int device_get_sysattr_unsigned_filtered(sd_device *device, const char *sysattr, unsigned *ret_value);
+int device_get_sysattr_bool_filtered(sd_device *device, const char *sysattr);
+int device_get_sysattr_value_filtered(sd_device *device, const char *sysattr, const char **ret_value);
index 311fb4d9cbb04b16f52769cfbf5e2fc85aa35af1..d4a689cf852fc0e924b2ebd98be09bcfcf285453 100644 (file)
@@ -4,11 +4,12 @@
 #include "fd-util.h"
 #include "hexdecoct.h"
 #include "openssl-util.h"
+#include "random-util.h"
 #include "string-util.h"
 
 #if HAVE_OPENSSL
-/* For each error in the 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
+/* 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). */
 #define log_openssl_errors(fmt, ...) _log_openssl_errors(UNIQ, fmt, ##__VA_ARGS__)
 #define _log_openssl_errors(u, fmt, ...)                                \
@@ -523,7 +524,6 @@ int rsa_encrypt_bytes(
 
         *ret_encrypt_key = TAKE_PTR(b);
         *ret_encrypt_key_size = l;
-
         return 0;
 }
 
@@ -989,7 +989,7 @@ int ecc_ecdh(const EVP_PKEY *private_pkey,
         if (EVP_PKEY_derive(ctx, NULL, &shared_secret_size) <= 0)
                 return log_openssl_errors("Failed to get ECC shared secret size");
 
-        _cleanup_free_ void *shared_secret = malloc(shared_secret_size);
+        _cleanup_(erase_and_freep) void *shared_secret = malloc(shared_secret_size);
         if (!shared_secret)
                 return log_oom_debug();
 
@@ -1109,7 +1109,7 @@ int string_hashsum(
 
         _cleanup_free_ void *hash = NULL;
         size_t hash_size;
-        _cleanup_free_ char *enc;
+        _cleanup_free_ char *enc = NULL;
         int r;
 
         assert(s || len == 0);
@@ -1128,6 +1128,170 @@ int string_hashsum(
         return 0;
 }
 #  endif
+
+static int ecc_pkey_generate_volume_keys(
+                EVP_PKEY *pkey,
+                void **ret_decrypted_key,
+                size_t *ret_decrypted_key_size,
+                void **ret_saved_key,
+                size_t *ret_saved_key_size) {
+
+        _cleanup_(EVP_PKEY_freep) EVP_PKEY *pkey_new = NULL;
+        _cleanup_(erase_and_freep) void *decrypted_key = NULL;
+        _cleanup_free_ unsigned char *saved_key = NULL;
+        size_t decrypted_key_size, saved_key_size;
+        int nid = NID_undef;
+        int r;
+
+#if OPENSSL_VERSION_MAJOR >= 3
+        _cleanup_free_ char *curve_name = NULL;
+        size_t len = 0;
+
+        if (EVP_PKEY_get_group_name(pkey, NULL, 0, &len) != 1 || len == 0)
+                return log_openssl_errors("Failed to determine PKEY group name length");
+
+        len++;
+        curve_name = new(char, len);
+        if (!curve_name)
+                return log_oom_debug();
+
+        if (EVP_PKEY_get_group_name(pkey, curve_name, len, &len) != 1)
+                return log_openssl_errors("Failed to get PKEY group name");
+
+        nid = OBJ_sn2nid(curve_name);
+#else
+        EC_KEY *ec_key = EVP_PKEY_get0_EC_KEY(pkey);
+        if (!ec_key)
+                return log_openssl_errors("PKEY doesn't have EC_KEY associated");
+
+        if (EC_KEY_check_key(ec_key) != 1)
+                return log_openssl_errors("EC_KEY associated with PKEY is not valid");
+
+        nid = EC_GROUP_get_curve_name(EC_KEY_get0_group(ec_key));
+#endif
+
+        r = ecc_pkey_new(nid, &pkey_new);
+        if (r < 0)
+                return log_debug_errno(r, "Failed to generate a new EC keypair: %m");
+
+        r = ecc_ecdh(pkey_new, pkey, &decrypted_key, &decrypted_key_size);
+        if (r < 0)
+                return log_debug_errno(r, "Failed to derive shared secret: %m");
+
+#if OPENSSL_VERSION_MAJOR >= 3
+        /* EVP_PKEY_get1_encoded_public_key() always returns uncompressed format of EC points.
+           See https://github.com/openssl/openssl/discussions/22835 */
+        saved_key_size = EVP_PKEY_get1_encoded_public_key(pkey_new, &saved_key);
+        if (saved_key_size == 0)
+                return log_openssl_errors("Failed to convert the generated public key to SEC1 format");
+#else
+        EC_KEY *ec_key_new = EVP_PKEY_get0_EC_KEY(pkey_new);
+        if (!ec_key_new)
+                return log_openssl_errors("The generated key doesn't have associated EC_KEY");
+
+        if (EC_KEY_check_key(ec_key_new) != 1)
+                return log_openssl_errors("EC_KEY associated with the generated key is not valid");
+
+        saved_key_size = EC_POINT_point2oct(EC_KEY_get0_group(ec_key_new),
+                                            EC_KEY_get0_public_key(ec_key_new),
+                                            POINT_CONVERSION_UNCOMPRESSED,
+                                            NULL, 0, NULL);
+        if (saved_key_size == 0)
+                return log_openssl_errors("Failed to determine size of the generated public key");
+
+        saved_key = malloc(saved_key_size);
+        if (!saved_key)
+                return log_oom_debug();
+
+        saved_key_size = EC_POINT_point2oct(EC_KEY_get0_group(ec_key_new),
+                                            EC_KEY_get0_public_key(ec_key_new),
+                                            POINT_CONVERSION_UNCOMPRESSED,
+                                            saved_key, saved_key_size, NULL);
+        if (saved_key_size == 0)
+                return log_openssl_errors("Failed to convert the generated public key to SEC1 format");
+#endif
+
+        *ret_decrypted_key = TAKE_PTR(decrypted_key);
+        *ret_decrypted_key_size = decrypted_key_size;
+        *ret_saved_key = TAKE_PTR(saved_key);
+        *ret_saved_key_size = saved_key_size;
+        return 0;
+}
+
+static int rsa_pkey_generate_volume_keys(
+                EVP_PKEY *pkey,
+                void **ret_decrypted_key,
+                size_t *ret_decrypted_key_size,
+                void **ret_saved_key,
+                size_t *ret_saved_key_size) {
+
+        _cleanup_(erase_and_freep) void *decrypted_key = NULL;
+        _cleanup_free_ void *saved_key = NULL;
+        size_t decrypted_key_size, saved_key_size;
+        int r;
+
+        r = rsa_pkey_to_suitable_key_size(pkey, &decrypted_key_size);
+        if (r < 0)
+                return log_debug_errno(r, "Failed to determine RSA public key size.");
+
+        log_debug("Generating %zu bytes random key.", decrypted_key_size);
+
+        decrypted_key = malloc(decrypted_key_size);
+        if (!decrypted_key)
+                return log_oom_debug();
+
+        r = crypto_random_bytes(decrypted_key, decrypted_key_size);
+        if (r < 0)
+                return log_debug_errno(r, "Failed to generate random key: %m");
+
+        r = rsa_encrypt_bytes(pkey, decrypted_key, decrypted_key_size, &saved_key, &saved_key_size);
+        if (r < 0)
+                return log_debug_errno(r, "Failed to encrypt random key: %m");
+
+        *ret_decrypted_key = TAKE_PTR(decrypted_key);
+        *ret_decrypted_key_size = decrypted_key_size;
+        *ret_saved_key = TAKE_PTR(saved_key);
+        *ret_saved_key_size = saved_key_size;
+        return 0;
+}
+
+int x509_generate_volume_keys(
+                X509 *cert,
+                void **ret_decrypted_key,
+                size_t *ret_decrypted_key_size,
+                void **ret_saved_key,
+                size_t *ret_saved_key_size) {
+
+        assert(cert);
+        assert(ret_decrypted_key);
+        assert(ret_decrypted_key_size);
+        assert(ret_saved_key);
+        assert(ret_saved_key_size);
+
+        EVP_PKEY *pkey = X509_get0_pubkey(cert);
+        if (!pkey)
+                return log_openssl_errors("Failed to extract public key from X.509 certificate.");
+
+#if OPENSSL_VERSION_MAJOR >= 3
+        int type = EVP_PKEY_get_base_id(pkey);
+#else
+        int type = EVP_PKEY_base_id(pkey);
+#endif
+        switch (type) {
+
+        case EVP_PKEY_RSA:
+                return rsa_pkey_generate_volume_keys(pkey, ret_decrypted_key, ret_decrypted_key_size, ret_saved_key, ret_saved_key_size);
+
+        case EVP_PKEY_EC:
+                return ecc_pkey_generate_volume_keys(pkey, ret_decrypted_key, ret_decrypted_key_size, ret_saved_key, ret_saved_key_size);
+
+        case NID_undef:
+                return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to determine a type of public key");
+
+        default:
+                return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Unsupported public key type: %s", OBJ_nid2sn(type));
+        }
+}
 #endif
 
 int x509_fingerprint(X509 *cert, uint8_t buffer[static SHA256_DIGEST_SIZE]) {
index e3f34a8576e0d794b187a61e94f2acdbb4baa1fd..2ca3e8c1ce0904679217ff2516d8c7233fcbeda9 100644 (file)
@@ -108,6 +108,8 @@ int ecc_pkey_new(int curve_id, EVP_PKEY **ret);
 
 int ecc_ecdh(const EVP_PKEY *private_pkey, const EVP_PKEY *peer_pkey, void **ret_shared_secret, size_t *ret_shared_secret_size);
 
+int x509_generate_volume_keys(X509 *cert, void **ret_decrypted_key, size_t *ret_decrypted_key_size, void **ret_saved_key, size_t *ret_saved_key_size);
+
 int pubkey_fingerprint(EVP_PKEY *pk, const EVP_MD *md, void **ret, size_t *ret_size);
 
 int digest_and_sign(const EVP_MD *md, EVP_PKEY *privkey, const void *data, size_t size, void **ret, size_t *ret_size);
index 6e88dc3803860e8c2ef2a57b39ef173d24cc49fe..c3d97b80f9e4c30e870d53a0056a56f83eb14732 100644 (file)
@@ -586,143 +586,419 @@ int pkcs11_token_find_private_key(
                 P11KitUri *search_uri,
                 CK_OBJECT_HANDLE *ret_object) {
 
-        bool found_decrypt = false, found_class = false, found_key_type = false;
+        uint_fast8_t n_objects = 0;
+        bool found_class = false;
         _cleanup_free_ CK_ATTRIBUTE *attributes_buffer = NULL;
-        CK_ULONG n_attributes, a, n_objects;
-        CK_ATTRIBUTE *attributes = NULL;
-        CK_OBJECT_HANDLE objects[2];
-        CK_RV rv, rv2;
-        int r;
+        CK_OBJECT_HANDLE object, candidate;
+        static const CK_OBJECT_CLASS class = CKO_PRIVATE_KEY;
+        CK_BBOOL decrypt_value, derive_value;
+        CK_ATTRIBUTE optional_attributes[] = {
+                { CKA_DECRYPT, &decrypt_value, sizeof(decrypt_value) },
+                { CKA_DERIVE,  &derive_value,  sizeof(derive_value)  }
+        };
+        CK_RV rv;
 
         assert(m);
         assert(search_uri);
         assert(ret_object);
 
-        r = dlopen_p11kit();
-        if (r < 0)
-                return r;
-
-        attributes = sym_p11_kit_uri_get_attributes(search_uri, &n_attributes);
-        for (a = 0; a < n_attributes; a++) {
+        CK_ULONG n_attributes;
+        CK_ATTRIBUTE *attributes = sym_p11_kit_uri_get_attributes(search_uri, &n_attributes);
+        for (CK_ULONG i = 0; i < n_attributes; i++) {
 
                 /* We use the URI's included match attributes, but make them more strict. This allows users
                  * to specify a token URL instead of an object URL and the right thing should happen if
                  * there's only one suitable key on the token. */
 
-                switch (attributes[a].type) {
-
+                switch (attributes[i].type) {
                 case CKA_CLASS: {
                         CK_OBJECT_CLASS c;
 
-                        if (attributes[a].ulValueLen != sizeof(c))
+                        if (attributes[i].ulValueLen != sizeof(c))
                                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid PKCS#11 CKA_CLASS attribute size.");
 
-                        memcpy(&c, attributes[a].pValue, sizeof(c));
+                        memcpy(&c, attributes[i].pValue, sizeof(c));
                         if (c != CKO_PRIVATE_KEY)
                                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
                                                        "Selected PKCS#11 object is not a private key, refusing.");
 
                         found_class = true;
                         break;
-                }
+                }}
+        }
+
+        if (!found_class) {
+                /* Hmm, let's slightly extend the attribute list we search for */
 
-                case CKA_DECRYPT: {
-                        CK_BBOOL b;
+                attributes_buffer = new(CK_ATTRIBUTE, n_attributes + 1);
+                if (!attributes_buffer)
+                        return log_oom();
 
-                        if (attributes[a].ulValueLen != sizeof(b))
-                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid PKCS#11 CKA_DECRYPT attribute size.");
+                memcpy(attributes_buffer, attributes, sizeof(CK_ATTRIBUTE) * n_attributes);
 
-                        memcpy(&b, attributes[a].pValue, sizeof(b));
-                        if (!b)
-                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
-                                                       "Selected PKCS#11 object is not suitable for decryption, refusing.");
+                attributes_buffer[n_attributes++] = (CK_ATTRIBUTE) {
+                        .type = CKA_CLASS,
+                        .pValue = (CK_OBJECT_CLASS*) &class,
+                        .ulValueLen = sizeof(class),
+                };
+
+                attributes = attributes_buffer;
+        }
+
+        rv = m->C_FindObjectsInit(session, attributes, n_attributes);
+        if (rv != CKR_OK)
+                return log_error_errno(SYNTHETIC_ERRNO(EIO),
+                                       "Failed to initialize object find call: %s", sym_p11_kit_strerror(rv));
+
+        for (;;) {
+                CK_ULONG b;
+                rv = m->C_FindObjects(session, &candidate, 1, &b);
+                if (rv != CKR_OK)
+                        return log_error_errno(SYNTHETIC_ERRNO(EIO),
+                                "Failed to find objects: %s", sym_p11_kit_strerror(rv));
 
-                        found_decrypt = true;
+                if (b == 0)
                         break;
+
+                bool can_decrypt = false, can_derive = false;
+                optional_attributes[0].ulValueLen = sizeof(decrypt_value);
+                optional_attributes[1].ulValueLen = sizeof(derive_value);
+
+                rv = m->C_GetAttributeValue(session, candidate, optional_attributes, ELEMENTSOF(optional_attributes));
+                if (rv != CKR_OK && rv != CKR_ATTRIBUTE_TYPE_INVALID)
+                        return log_error_errno(SYNTHETIC_ERRNO(EIO),
+                                "Failed to get attributes of a selected private key: %s", sym_p11_kit_strerror(rv));
+
+                if (optional_attributes[0].ulValueLen != CK_UNAVAILABLE_INFORMATION && decrypt_value == CK_TRUE)
+                        can_decrypt = true;
+
+                if (optional_attributes[1].ulValueLen != CK_UNAVAILABLE_INFORMATION && derive_value == CK_TRUE)
+                        can_derive = true;
+
+                if (can_decrypt || can_derive) {
+                        n_objects++;
+                        if (n_objects > 1)
+                                break;
+                        object = candidate;
                 }
+        }
 
-                case CKA_KEY_TYPE: {
-                        CK_KEY_TYPE t;
+        rv = m->C_FindObjectsFinal(session);
+        if (rv != CKR_OK)
+                return log_error_errno(SYNTHETIC_ERRNO(EIO),
+                        "Failed to finalize object find call: %s", sym_p11_kit_strerror(rv));
 
-                        if (attributes[a].ulValueLen != sizeof(t))
-                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid PKCS#11 CKA_KEY_TYPE attribute size.");
+        if (n_objects == 0)
+                return log_error_errno(SYNTHETIC_ERRNO(ENOENT),
+                        "Failed to find selected private key suitable for decryption or derivation on token.");
 
-                        memcpy(&t, attributes[a].pValue, sizeof(t));
-                        if (t != CKK_RSA)
-                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Selected PKCS#11 object is not an RSA key, refusing.");
+        if (n_objects > 1)
+                return log_error_errno(SYNTHETIC_ERRNO(ENOTUNIQ),
+                        "Configured private key URI matches multiple keys, refusing.");
 
-                        found_key_type = true;
-                        break;
-                }}
+        *ret_object = object;
+        return 0;
+}
+
+static const char* object_class_to_string(CK_OBJECT_CLASS class) {
+        switch (class) {
+        case CKO_CERTIFICATE:
+                return "CKO_CERTIFICATE";
+        case CKO_PUBLIC_KEY:
+                return "CKO_PUBLIC_KEY";
+        case CKO_PRIVATE_KEY:
+                return "CKO_PRIVATE_KEY";
+        case CKO_SECRET_KEY:
+                return "CKO_SECRET_KEY";
+        default:
+                return NULL;
         }
+}
 
-        if (!found_decrypt || !found_class || !found_key_type) {
-                /* Hmm, let's slightly extend the attribute list we search for */
+/* Returns an object with the given class and the same CKA_ID or CKA_LABEL as prototype */
+int pkcs11_token_find_related_object(
+                CK_FUNCTION_LIST *m,
+                CK_SESSION_HANDLE session,
+                CK_OBJECT_HANDLE prototype,
+                CK_OBJECT_CLASS class,
+                CK_OBJECT_HANDLE *ret_object ) {
 
-                attributes_buffer = new(CK_ATTRIBUTE, n_attributes + !found_decrypt + !found_class + !found_key_type);
-                if (!attributes_buffer)
+        _cleanup_free_ void *buffer = NULL;
+        CK_ATTRIBUTE attributes[] = {
+                { CKA_ID,    NULL_PTR, 0 },
+                { CKA_LABEL, NULL_PTR, 0 }
+        };
+        CK_OBJECT_CLASS search_class = class;
+        CK_ATTRIBUTE search_attributes[2] = {
+                { CKA_CLASS, &search_class, sizeof(search_class) }
+        };
+        CK_ULONG n_objects;
+        CK_OBJECT_HANDLE objects[2];
+        CK_RV rv;
+
+        rv = m->C_GetAttributeValue(session, prototype, attributes, ELEMENTSOF(attributes));
+        if (rv != CKR_OK && rv != CKR_ATTRIBUTE_TYPE_INVALID)
+                return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Failed to retrieve length of attributes: %s", sym_p11_kit_strerror(rv));
+
+        if (attributes[0].ulValueLen != CK_UNAVAILABLE_INFORMATION) {
+                buffer = malloc(attributes[0].ulValueLen);
+                if (!buffer)
                         return log_oom();
 
-                memcpy(attributes_buffer, attributes, sizeof(CK_ATTRIBUTE) * n_attributes);
+                attributes[0].pValue = buffer;
+                rv = m->C_GetAttributeValue(session, prototype, &attributes[0], 1);
+                if (rv != CKR_OK)
+                        return log_debug_errno(SYNTHETIC_ERRNO(EIO),
+                                "Failed to retrieve CKA_ID: %s", sym_p11_kit_strerror(rv));
 
-                if (!found_decrypt) {
-                        static const CK_BBOOL yes = true;
+                search_attributes[1] = attributes[0];
 
-                        attributes_buffer[n_attributes++] = (CK_ATTRIBUTE) {
-                                .type = CKA_DECRYPT,
-                                .pValue = (CK_BBOOL*) &yes,
-                                .ulValueLen = sizeof(yes),
-                        };
-                }
+        } else if (attributes[1].ulValueLen != CK_UNAVAILABLE_INFORMATION) {
+                buffer = malloc(attributes[1].ulValueLen);
+                if (!buffer)
+                        return log_oom();
 
-                if (!found_class) {
-                        static const CK_OBJECT_CLASS class = CKO_PRIVATE_KEY;
+                attributes[1].pValue = buffer;
+                rv = m->C_GetAttributeValue(session, prototype, &attributes[1], 1);
+                if (rv != CKR_OK)
+                        return log_debug_errno(SYNTHETIC_ERRNO(EIO),
+                                "Failed to retrieve CKA_LABEL: %s", sym_p11_kit_strerror(rv));
 
-                        attributes_buffer[n_attributes++] = (CK_ATTRIBUTE) {
-                                .type = CKA_CLASS,
-                                .pValue = (CK_OBJECT_CLASS*) &class,
-                                .ulValueLen = sizeof(class),
-                        };
-                }
+                search_attributes[1] = attributes[1];
 
-                if (!found_key_type) {
-                        static const CK_KEY_TYPE type = CKK_RSA;
+        } else
+                return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "The prototype does not have CKA_ID or CKA_LABEL");
 
-                        attributes_buffer[n_attributes++] = (CK_ATTRIBUTE) {
-                                .type = CKA_KEY_TYPE,
-                                .pValue = (CK_KEY_TYPE*) &type,
-                                .ulValueLen = sizeof(type),
-                        };
-                }
+        rv = m->C_FindObjectsInit(session, search_attributes, 2);
+        if (rv != CKR_OK)
+                return log_debug_errno(SYNTHETIC_ERRNO(EIO),
+                        "Failed to initialize object find call: %s", sym_p11_kit_strerror(rv));
 
-                attributes = attributes_buffer;
+        rv = m->C_FindObjects(session, objects, 2, &n_objects);
+        if (rv != CKR_OK)
+                return log_debug_errno(SYNTHETIC_ERRNO(EIO),
+                        "Failed to find objects: %s", sym_p11_kit_strerror(rv));
+
+        rv = m->C_FindObjectsFinal(session);
+        if (rv != CKR_OK)
+                return log_debug_errno(SYNTHETIC_ERRNO(EIO),
+                        "Failed to finalize object find call: %s", sym_p11_kit_strerror(rv));
+
+         if (n_objects == 0)
+                return log_debug_errno(SYNTHETIC_ERRNO(ENOENT),
+                        "Failed to find a related object with class %s", object_class_to_string(class));
+
+         if (n_objects > 1)
+                log_warning("Found multiple related objects with class %s, using the first object.",
+                        object_class_to_string(class));
+
+        *ret_object = objects[0];
+        return 0;
+}
+
+#if HAVE_OPENSSL
+static int ecc_convert_to_compressed(
+                CK_FUNCTION_LIST *m,
+                CK_SESSION_HANDLE session,
+                CK_OBJECT_HANDLE object,
+                const void *uncompressed_point,
+                size_t uncompressed_point_size,
+                void **ret_compressed_point,
+                size_t *ret_compressed_point_size) {
+
+        _cleanup_free_ void *ec_params_buffer = NULL;
+        CK_ATTRIBUTE ec_params_attr = { CKA_EC_PARAMS, NULL_PTR, 0 };
+        CK_RV rv;
+        int r;
+
+        rv = m->C_GetAttributeValue(session, object, &ec_params_attr, 1);
+        if (rv != CKR_OK && rv != CKR_ATTRIBUTE_TYPE_INVALID)
+                return log_error_errno(SYNTHETIC_ERRNO(EIO),
+                        "Failed to retrieve length of CKA_EC_PARAMS: %s", sym_p11_kit_strerror(rv));
+
+        if (ec_params_attr.ulValueLen != CK_UNAVAILABLE_INFORMATION) {
+                ec_params_buffer = malloc(ec_params_attr.ulValueLen);
+                if (!ec_params_buffer)
+                        return log_oom();
+
+                ec_params_attr.pValue = ec_params_buffer;
+                rv = m->C_GetAttributeValue(session, object, &ec_params_attr, 1);
+                if (rv != CKR_OK)
+                        return log_error_errno(SYNTHETIC_ERRNO(EIO),
+                                "Failed to retrieve CKA_EC_PARAMS from a private key: %s", sym_p11_kit_strerror(rv));
+        } else {
+                CK_OBJECT_HANDLE public_key;
+                r = pkcs11_token_find_related_object(m, session, object, CKO_PUBLIC_KEY, &public_key);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to find a public key for compressing a EC point");
+
+                ec_params_attr.ulValueLen = 0;
+                rv = m->C_GetAttributeValue(session, public_key, &ec_params_attr, 1);
+                if (rv != CKR_OK && rv != CKR_ATTRIBUTE_TYPE_INVALID)
+                        return log_error_errno(SYNTHETIC_ERRNO(EIO),
+                                "Failed to retrieve length of CKA_EC_PARAMS: %s", sym_p11_kit_strerror(rv));
+
+                if (ec_params_attr.ulValueLen == CK_UNAVAILABLE_INFORMATION)
+                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+                                "The public key does not have CKA_EC_PARAMS");
+
+                ec_params_buffer = malloc(ec_params_attr.ulValueLen);
+                if (!ec_params_buffer)
+                        return log_oom();
+
+                ec_params_attr.pValue = ec_params_buffer;
+                rv = m->C_GetAttributeValue(session, public_key, &ec_params_attr, 1);
+                if (rv != CKR_OK)
+                        return log_error_errno(SYNTHETIC_ERRNO(EIO),
+                                "Failed to retrieve CKA_EC_PARAMS from a public key: %s", sym_p11_kit_strerror(rv));
         }
 
-        rv = m->C_FindObjectsInit(session, attributes, n_attributes);
+        _cleanup_(EC_GROUP_freep) EC_GROUP *group = NULL;
+        _cleanup_(EC_POINT_freep) EC_POINT *point = NULL;
+        _cleanup_(BN_CTX_freep) BN_CTX *bnctx = NULL;
+        _cleanup_free_ void *compressed_point = NULL;
+        size_t compressed_point_size;
+
+        const unsigned char *ec_params_value = ec_params_attr.pValue;
+        group = d2i_ECPKParameters(NULL, &ec_params_value, ec_params_attr.ulValueLen);
+        if (!group)
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unable to decode CKA_EC_PARAMS");
+
+        point = EC_POINT_new(group);
+        if (!point)
+                return log_oom();
+
+        bnctx = BN_CTX_new();
+        if (!bnctx)
+                return log_oom();
+
+        if (EC_POINT_oct2point(group, point, uncompressed_point, uncompressed_point_size, bnctx) != 1)
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unable to decode an uncompressed EC point");
+
+        compressed_point_size = EC_POINT_point2oct(group, point, POINT_CONVERSION_COMPRESSED, NULL, 0, bnctx);
+        if (compressed_point_size == 0)
+                return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to determine size of a compressed EC point");
+
+        compressed_point = malloc(compressed_point_size);
+        if (!compressed_point)
+                return log_oom();
+
+        compressed_point_size = EC_POINT_point2oct(group, point, POINT_CONVERSION_COMPRESSED, compressed_point, compressed_point_size, bnctx);
+        if (compressed_point_size == 0)
+                return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to convert a EC point to compressed format");
+
+        *ret_compressed_point = TAKE_PTR(compressed_point);
+        *ret_compressed_point_size = compressed_point_size;
+        return 0;
+}
+#endif
+
+/* Since EC keys doesn't support encryption directly, we use ECDH protocol to derive shared secret here.
+ * We use PKCS#11 C_DeriveKey function to derive a shared secret with a private key stored in the token and
+ * a public key saved on enrollment. */
+static int pkcs11_token_decrypt_data_ecc(
+                CK_FUNCTION_LIST *m,
+                CK_SESSION_HANDLE session,
+                CK_OBJECT_HANDLE object,
+                const void *encrypted_data,
+                size_t encrypted_data_size,
+                void **ret_decrypted_data,
+                size_t *ret_decrypted_data_size) {
+
+        static const CK_BBOOL yes = CK_TRUE, no = CK_FALSE;
+        static const CK_OBJECT_CLASS shared_secret_class = CKO_SECRET_KEY;
+        static const CK_KEY_TYPE shared_secret_type = CKK_GENERIC_SECRET;
+        static const CK_ATTRIBUTE shared_secret_template[] = {
+                { CKA_TOKEN,       (void*) &no,                  sizeof(no)                  },
+                { CKA_CLASS,       (void*) &shared_secret_class, sizeof(shared_secret_class) },
+                { CKA_KEY_TYPE,    (void*) &shared_secret_type,  sizeof(shared_secret_type)  },
+                { CKA_SENSITIVE,   (void*) &no,                  sizeof(no)                  },
+                { CKA_EXTRACTABLE, (void*) &yes,                 sizeof(yes)                 }
+        };
+        CK_ECDH1_DERIVE_PARAMS params = {
+                .kdf = CKD_NULL,
+                .pPublicData = (void*) encrypted_data,
+                .ulPublicDataLen = encrypted_data_size
+        };
+        CK_MECHANISM mechanism = {
+                .mechanism = CKM_ECDH1_DERIVE,
+                .pParameter = &params,
+                .ulParameterLen = sizeof(params)
+        };
+        CK_OBJECT_HANDLE shared_secret_handle;
+        CK_SESSION_INFO session_info;
+        CK_MECHANISM_INFO mechanism_info;
+        CK_RV rv, rv2;
+#if HAVE_OPENSSL
+        _cleanup_free_ void *compressed_point = NULL;
+        int r;
+#endif
+
+        rv = m->C_GetSessionInfo(session, &session_info);
         if (rv != CKR_OK)
                 return log_error_errno(SYNTHETIC_ERRNO(EIO),
-                                       "Failed to initialize object find call: %s", sym_p11_kit_strerror(rv));
+                        "Failed to get information about the PKCS#11 session: %s", sym_p11_kit_strerror(rv));
 
-        rv = m->C_FindObjects(session, objects, ELEMENTSOF(objects), &n_objects);
-        rv2 = m->C_FindObjectsFinal(session);
+        rv = m->C_GetMechanismInfo(session_info.slotID, CKM_ECDH1_DERIVE, &mechanism_info);
         if (rv != CKR_OK)
                 return log_error_errno(SYNTHETIC_ERRNO(EIO),
-                                       "Failed to find objects: %s", sym_p11_kit_strerror(rv));
+                        "Failed to get information about CKM_ECDH1_DERIVE: %s", sym_p11_kit_strerror(rv));
+
+        if (!(mechanism_info.flags & CKF_EC_UNCOMPRESS)) {
+                if (mechanism_info.flags & CKF_EC_COMPRESS) {
+#if HAVE_OPENSSL
+                        log_debug("CKM_ECDH1_DERIVE accepts compressed EC points only, trying to convert.");
+                        size_t compressed_point_size;
+                        r = ecc_convert_to_compressed(m, session, object, encrypted_data, encrypted_data_size, &compressed_point, &compressed_point_size);
+                        if (r < 0)
+                                return r;
+
+                        params.pPublicData = compressed_point;
+                        params.ulPublicDataLen = compressed_point_size;
+#else
+                        return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
+                                "CKM_ECDH1_DERIVE does not support uncompressed format of EC points");
+#endif
+                } else
+                        log_debug("Both CKF_EC_UNCOMPRESS and CKF_EC_COMPRESS are false for CKM_ECDH1_DERIVE, ignoring.");
+        }
+
+        rv = m->C_DeriveKey(session, &mechanism, object, (CK_ATTRIBUTE*) shared_secret_template, ELEMENTSOF(shared_secret_template), &shared_secret_handle);
+        if (rv != CKR_OK)
+                return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to derive a shared secret: %s", sym_p11_kit_strerror(rv));
+
+        CK_ATTRIBUTE shared_secret_attr = { CKA_VALUE, NULL_PTR, 0};
+
+        rv = m->C_GetAttributeValue(session, shared_secret_handle, &shared_secret_attr, 1);
+        if (rv != CKR_OK) {
+                rv2 = m->C_DestroyObject(session, shared_secret_handle);
+                if (rv2 != CKR_OK)
+                        log_warning("Failed to destroy a shared secret, ignoring: %s", sym_p11_kit_strerror(rv2));
+                return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to retrieve shared secret length: %s", sym_p11_kit_strerror(rv));
+        }
+
+        shared_secret_attr.pValue = malloc(shared_secret_attr.ulValueLen);
+        if (!shared_secret_attr.pValue)
+                return log_oom();
+
+        rv = m->C_GetAttributeValue(session, shared_secret_handle, &shared_secret_attr, 1);
+        rv2 = m->C_DestroyObject(session, shared_secret_handle);
         if (rv2 != CKR_OK)
-                return log_error_errno(SYNTHETIC_ERRNO(EIO),
-                                       "Failed to finalize object find call: %s", sym_p11_kit_strerror(rv));
-        if (n_objects == 0)
-                return log_error_errno(SYNTHETIC_ERRNO(ENOENT),
-                                       "Failed to find selected private key suitable for decryption on token.");
-        if (n_objects > 1)
-                return log_error_errno(SYNTHETIC_ERRNO(ENOTUNIQ),
-                                       "Configured private key URI matches multiple keys, refusing.");
+                log_warning("Failed to destroy a shared secret, ignoring: %s", sym_p11_kit_strerror(rv2));
 
-        *ret_object = objects[0];
+        if (rv != CKR_OK) {
+                erase_and_free(shared_secret_attr.pValue);
+                return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to retrieve a shared secret: %s", sym_p11_kit_strerror(rv));
+        }
+
+        log_info("Successfully derived key with security token.");
+
+        *ret_decrypted_data = shared_secret_attr.pValue;
+        *ret_decrypted_data_size = shared_secret_attr.ulValueLen;
         return 0;
 }
 
-int pkcs11_token_decrypt_data(
+static int pkcs11_token_decrypt_data_rsa(
                 CK_FUNCTION_LIST *m,
                 CK_SESSION_HANDLE session,
                 CK_OBJECT_HANDLE object,
@@ -737,17 +1013,6 @@ int pkcs11_token_decrypt_data(
         _cleanup_(erase_and_freep) CK_BYTE *dbuffer = NULL;
         CK_ULONG dbuffer_size = 0;
         CK_RV rv;
-        int r;
-
-        assert(m);
-        assert(encrypted_data);
-        assert(encrypted_data_size > 0);
-        assert(ret_decrypted_data);
-        assert(ret_decrypted_data_size);
-
-        r = dlopen_p11kit();
-        if (r < 0)
-                return r;
 
         rv = m->C_DecryptInit(session, (CK_MECHANISM*) &mechanism, object);
         if (rv != CKR_OK)
@@ -780,6 +1045,42 @@ int pkcs11_token_decrypt_data(
         return 0;
 }
 
+int pkcs11_token_decrypt_data(
+                CK_FUNCTION_LIST *m,
+                CK_SESSION_HANDLE session,
+                CK_OBJECT_HANDLE object,
+                const void *encrypted_data,
+                size_t encrypted_data_size,
+                void **ret_decrypted_data,
+                size_t *ret_decrypted_data_size) {
+
+        CK_KEY_TYPE key_type;
+        CK_ATTRIBUTE key_type_template = { CKA_KEY_TYPE, &key_type, sizeof(key_type) };
+        CK_RV rv;
+
+        assert(m);
+        assert(encrypted_data);
+        assert(encrypted_data_size > 0);
+        assert(ret_decrypted_data);
+        assert(ret_decrypted_data_size);
+
+        rv = m->C_GetAttributeValue(session, object, &key_type_template, 1);
+        if (rv != CKR_OK)
+                return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to retrieve private key type");
+
+        switch (key_type) {
+
+        case CKK_RSA:
+                return pkcs11_token_decrypt_data_rsa(m, session, object, encrypted_data, encrypted_data_size, ret_decrypted_data, ret_decrypted_data_size);
+
+        case CKK_EC:
+                return pkcs11_token_decrypt_data_ecc(m, session, object, encrypted_data, encrypted_data_size, ret_decrypted_data, ret_decrypted_data_size);
+
+        default:
+                return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Unsupported private key type: %lu", key_type);
+        }
+}
+
 int pkcs11_token_acquire_rng(
                 CK_FUNCTION_LIST *m,
                 CK_SESSION_HANDLE session) {
index 5bc23c14c4c977d6700d4869e6b67535c67fac4f..2ff6997823e1b4a486fbac75ab5eebcb3bc885f9 100644 (file)
@@ -1,6 +1,9 @@
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 #pragma once
 
+#if HAVE_OPENSSL
+#  include <openssl/x509.h>
+#endif
 #include <stdbool.h>
 
 #if HAVE_P11KIT
@@ -10,7 +13,6 @@
 
 #include "ask-password-api.h"
 #include "macro.h"
-#include "openssl-util.h"
 #include "time-util.h"
 
 bool pkcs11_uri_valid(const char *uri);
@@ -50,6 +52,7 @@ char *pkcs11_token_model(const CK_TOKEN_INFO *token_info);
 int pkcs11_token_login_by_pin(CK_FUNCTION_LIST *m, CK_SESSION_HANDLE session, const CK_TOKEN_INFO *token_info, const char *token_label, const void *pin, size_t pin_size);
 int pkcs11_token_login(CK_FUNCTION_LIST *m, CK_SESSION_HANDLE session, CK_SLOT_ID slotid, const CK_TOKEN_INFO *token_info, const char *friendly_name, const char *icon_name, const char *key_name, const char *credential_name, usec_t until, AskPasswordFlags ask_password_flags, bool headless, char **ret_used_pin);
 
+int pkcs11_token_find_related_object(CK_FUNCTION_LIST *m, CK_SESSION_HANDLE session, CK_OBJECT_HANDLE prototype, CK_OBJECT_CLASS class, CK_OBJECT_HANDLE *ret_object);
 int pkcs11_token_find_x509_certificate(CK_FUNCTION_LIST *m, CK_SESSION_HANDLE session, P11KitUri *search_uri, CK_OBJECT_HANDLE *ret_object);
 #if HAVE_OPENSSL
 int pkcs11_token_read_x509_certificate(CK_FUNCTION_LIST *m, CK_SESSION_HANDLE session, CK_OBJECT_HANDLE object, X509 **ret_cert);
index 4950932a96789a2e2f4c3c59c8455b4a52004c44..03660ad4178fc0d3bc5aa4ed0cca43d5397c6bb8 100644 (file)
 
 #include "alloc-util.h"
 #include "errno-util.h"
+#include "extract-word.h"
 #include "fd-util.h"
+#include "io-util.h"
 #include "log.h"
 #include "macro.h"
 #include "ptyfwd.h"
 #include "stat-util.h"
+#include "strv.h"
 #include "terminal-util.h"
 #include "time-util.h"
 
+typedef enum AnsiColorState  {
+        ANSI_COLOR_STATE_TEXT,
+        ANSI_COLOR_STATE_ESC,
+        ANSI_COLOR_STATE_CSI_SEQUENCE,
+        ANSI_COLOR_STATE_NEWLINE,
+        _ANSI_COLOR_STATE_MAX,
+        _ANSI_COLOR_STATE_INVALID = -EINVAL,
+} AnsiColorState;
+
 struct PTYForward {
         sd_event *event;
 
@@ -66,7 +78,8 @@ struct PTYForward {
         bool last_char_set:1;
         char last_char;
 
-        char in_buffer[LINE_MAX], out_buffer[LINE_MAX];
+        char in_buffer[LINE_MAX], *out_buffer;
+        size_t out_buffer_size;
         size_t in_buffer_full, out_buffer_full;
 
         usec_t escape_timestamp;
@@ -74,6 +87,10 @@ struct PTYForward {
 
         PTYForwardHandler handler;
         void *userdata;
+
+        char *background_color;
+        AnsiColorState ansi_color_state;
+        char *csi_sequence;
 };
 
 #define ESCAPE_USEC (1*USEC_PER_SEC)
@@ -96,6 +113,10 @@ static void pty_forward_disconnect(PTYForward *f) {
 
                 /* STDIN/STDOUT should not be non-blocking normally, so let's reset it */
                 (void) fd_nonblock(f->output_fd, false);
+
+                if (colors_enabled())
+                        (void) loop_write(f->output_fd, ANSI_NORMAL ANSI_ERASE_TO_END_OF_LINE, SIZE_MAX);
+
                 if (f->close_output_fd)
                         f->output_fd = safe_close(f->output_fd);
         }
@@ -110,6 +131,14 @@ static void pty_forward_disconnect(PTYForward *f) {
         }
 
         f->saved_stdout = f->saved_stdin = false;
+
+        f->out_buffer = mfree(f->out_buffer);
+        f->out_buffer_size = 0;
+        f->out_buffer_full = 0;
+        f->in_buffer_full = 0;
+
+        f->csi_sequence = mfree(f->csi_sequence);
+        f->ansi_color_state = _ANSI_COLOR_STATE_INVALID;
 }
 
 static int pty_forward_done(PTYForward *f, int rcode) {
@@ -197,11 +226,244 @@ static bool drained(PTYForward *f) {
         return true;
 }
 
+static char *background_color_sequence(PTYForward *f) {
+        assert(f);
+        assert(f->background_color);
+
+        /* This sets the background color to the desired one, and erase the rest of the line with it */
+
+        return strjoin("\x1B[", f->background_color, "m", ANSI_ERASE_TO_END_OF_LINE);
+}
+
+static int insert_string(PTYForward *f, size_t offset, const char *s) {
+        assert(f);
+        assert(offset <= f->out_buffer_full);
+        assert(s);
+
+        size_t l = strlen(s);
+        assert(l <= INT_MAX); /* Make sure we can still return this */
+
+        void *p = realloc(f->out_buffer, MAX(f->out_buffer_full + l, (size_t) LINE_MAX));
+        if (!p)
+                return -ENOMEM;
+
+        f->out_buffer = p;
+        f->out_buffer_size = MALLOC_SIZEOF_SAFE(f->out_buffer);
+
+        memmove(f->out_buffer + offset + l, f->out_buffer + offset, f->out_buffer_full - offset);
+        memcpy(f->out_buffer + offset, s, l);
+        f->out_buffer_full += l;
+
+        return (int) l;
+}
+
+static int insert_erase_newline(PTYForward *f, size_t offset) {
+        _cleanup_free_ char *s = NULL;
+
+        assert(f);
+        assert(f->background_color);
+
+        s = background_color_sequence(f);
+        if (!s)
+                return -ENOMEM;
+
+        return insert_string(f, offset, s);
+}
+
+static int is_csi_background_reset_sequence(const char *seq) {
+        enum {
+                COLOR_TOKEN_NO,
+                COLOR_TOKEN_START,
+                COLOR_TOKEN_8BIT,
+                COLOR_TOKEN_24BIT_R,
+                COLOR_TOKEN_24BIT_G,
+                COLOR_TOKEN_24BIT_B,
+        } token_state = COLOR_TOKEN_NO;
+
+        bool b = false;
+        int r;
+
+        assert(seq);
+
+        /* This parses CSI "m" sequences, and determines if they reset the background color. If so returns
+         * 1. This can then be used to insert another sequence that sets the color to the desired
+         * replacement. */
+
+        for (;;) {
+                _cleanup_free_ char *token = NULL;
+
+                r = extract_first_word(&seq, &token, ";", EXTRACT_RELAX|EXTRACT_DONT_COALESCE_SEPARATORS|EXTRACT_RETAIN_ESCAPE);
+                if (r < 0)
+                        return r;
+                if (r == 0)
+                        break;
+
+                switch (token_state) {
+
+                case COLOR_TOKEN_NO:
+
+                        if (STR_IN_SET(token, "", "0", "00", "49"))
+                                b = true; /* These tokens set the background back to normal */
+                        else if (STR_IN_SET(token, "40", "41", "42", "43", "44", "45", "46", "47", "48"))
+                                b = false; /* And these tokens set them to something other than normal */
+
+                        if (STR_IN_SET(token, "38", "48", "58"))
+                                token_state = COLOR_TOKEN_START; /* These tokens mean an 8bit or 24bit color will follow */
+                        break;
+
+                case COLOR_TOKEN_START:
+
+                        if (STR_IN_SET(token, "5", "05"))
+                                token_state = COLOR_TOKEN_8BIT; /* 8bit color */
+                        else if (STR_IN_SET(token, "2", "02"))
+                                token_state = COLOR_TOKEN_24BIT_R;  /* 24bit color */
+                        else
+                                token_state = COLOR_TOKEN_NO; /* something weird? */
+                        break;
+
+                case COLOR_TOKEN_24BIT_R:
+                        token_state = COLOR_TOKEN_24BIT_G;
+                        break;
+
+                case COLOR_TOKEN_24BIT_G:
+                        token_state = COLOR_TOKEN_24BIT_B;
+                        break;
+
+                case COLOR_TOKEN_8BIT:
+                case COLOR_TOKEN_24BIT_B:
+                        token_state = COLOR_TOKEN_NO;
+                        break;
+                }
+        }
+
+        return b;
+}
+
+static int insert_background_fix(PTYForward *f, size_t offset) {
+        assert(f);
+        assert(f->background_color);
+
+        if (!is_csi_background_reset_sequence(strempty(f->csi_sequence)))
+                return 0;
+
+        _cleanup_free_ char *s = NULL;
+        s = strjoin(";", f->background_color);
+        if (!s)
+                return -ENOMEM;
+
+        return insert_string(f, offset, s);
+}
+
+static int pty_forward_ansi_process(PTYForward *f, size_t offset) {
+        int r;
+
+        assert(f);
+        assert(offset <= f->out_buffer_full);
+
+        if (!f->background_color)
+                return 0;
+
+        for (size_t i = offset; i < f->out_buffer_full; i++) {
+                char c = f->out_buffer[i];
+
+                switch (f->ansi_color_state) {
+
+                case ANSI_COLOR_STATE_TEXT:
+                        if (c == '\n')
+                                f->ansi_color_state = ANSI_COLOR_STATE_NEWLINE;
+                        if (c == 0x1B) /* ESC */
+                                f->ansi_color_state = ANSI_COLOR_STATE_ESC;
+                        break;
+
+                case ANSI_COLOR_STATE_NEWLINE: {
+                        /* Immediately after a newline insert an ANSI sequence to erase the line with a background color */
+
+                        r = insert_erase_newline(f, i);
+                        if (r < 0)
+                                return r;
+
+                        i += r;
+
+                        f->ansi_color_state = ANSI_COLOR_STATE_TEXT;
+                        break;
+                }
+
+                case ANSI_COLOR_STATE_ESC: {
+
+                        if (c == '[')
+                                f->ansi_color_state = ANSI_COLOR_STATE_CSI_SEQUENCE;
+                        else
+                                f->ansi_color_state = ANSI_COLOR_STATE_TEXT;
+
+                        break;
+                }
+
+                case ANSI_COLOR_STATE_CSI_SEQUENCE: {
+
+                        if (c >= 0x20 && c <= 0x3F) {
+                                /* If this is a "parameter" or "intermediary" byte (i.e. ranges 0x20…0x2F and
+                                 * 0x30…0x3F) then we are still in the CSI sequence */
+
+                                if (strlen_ptr(f->csi_sequence) >= 64) {
+                                        /* Safety check: lets not accept unbounded CSI sequences */
+
+                                        f->csi_sequence = mfree(f->csi_sequence);
+                                        f->ansi_color_state = ANSI_COLOR_STATE_TEXT;
+                                } else if (!strextend(&f->csi_sequence, CHAR_TO_STR(c)))
+                                        return -ENOMEM;
+                        } else {
+                                /* Otherwise, the CSI sequence is over */
+
+                                if (c == 'm') {
+                                        /* This is an "SGR" (Select Graphic Rendition) sequence. Patch in our background color. */
+                                        r = insert_background_fix(f, i);
+                                        if (r < 0)
+                                                return r;
+
+                                        i += r;
+                                }
+
+                                f->csi_sequence = mfree(f->csi_sequence);
+                                f->ansi_color_state = ANSI_COLOR_STATE_TEXT;
+                        }
+
+                        break;
+                }
+
+                default:
+                        assert_not_reached();
+                }
+        }
+
+        return 0;
+}
+
 static int shovel(PTYForward *f) {
         ssize_t k;
+        int r;
 
         assert(f);
 
+        if (f->out_buffer_size == 0 && f->background_color) {
+                /* Erase the first line when we start */
+                f->out_buffer = background_color_sequence(f);
+                if (!f->out_buffer)
+                        return pty_forward_done(f, log_oom());
+
+                f->out_buffer_full = strlen(f->out_buffer);
+                f->out_buffer_size = MALLOC_SIZEOF_SAFE(f->out_buffer);
+        }
+
+        if (f->out_buffer_size < LINE_MAX) {
+                /* Make sure we always have room for at least one "line" */
+                void *p = realloc(f->out_buffer, LINE_MAX);
+                if (!p)
+                        return pty_forward_done(f, log_oom());
+
+                f->out_buffer = p;
+                f->out_buffer_size = MALLOC_SIZEOF_SAFE(p);
+        }
+
         while ((f->stdin_readable && f->in_buffer_full <= 0) ||
                (f->master_writable && f->in_buffer_full > 0) ||
                (f->master_readable && f->out_buffer_full <= 0) ||
@@ -262,9 +524,9 @@ static int shovel(PTYForward *f) {
                         }
                 }
 
-                if (f->master_readable && f->out_buffer_full < LINE_MAX) {
+                if (f->master_readable && f->out_buffer_full < MIN(f->out_buffer_size, (size_t) LINE_MAX)) {
 
-                        k = read(f->master, f->out_buffer + f->out_buffer_full, LINE_MAX - f->out_buffer_full);
+                        k = read(f->master, f->out_buffer + f->out_buffer_full, f->out_buffer_size - f->out_buffer_full);
                         if (k < 0) {
 
                                 /* Note that EIO on the master device might be caused by vhangup() or
@@ -284,7 +546,12 @@ static int shovel(PTYForward *f) {
                                 }
                         } else {
                                 f->read_from_master = true;
+                                size_t scan_index = f->out_buffer_full;
                                 f->out_buffer_full += (size_t) k;
+
+                                r = pty_forward_ansi_process(f, scan_index);
+                                if (r < 0)
+                                        return pty_forward_done(f, log_error_errno(r, "Failed to scan for ANSI sequences: %m"));
                         }
                 }
 
@@ -547,7 +814,10 @@ int pty_forward_new(
 }
 
 PTYForward *pty_forward_free(PTYForward *f) {
+        if (!f)
+                return NULL;
         pty_forward_disconnect(f);
+        free(f->background_color);
         return mfree(f);
 }
 
@@ -682,3 +952,9 @@ int pty_forward_set_width_height(PTYForward *f, unsigned width, unsigned height)
 
         return 0;
 }
+
+int pty_forward_set_background_color(PTYForward *f, const char *color) {
+        assert(f);
+
+        return free_and_strdup(&f->background_color, color);
+}
index f0ae6e949d04b76d8b5f83c73e94d73b67ecad9f..bae8d3591e44834b59259b568178291f90418cc4 100644 (file)
@@ -39,4 +39,6 @@ int pty_forward_set_priority(PTYForward *f, int64_t priority);
 
 int pty_forward_set_width_height(PTYForward *f, unsigned width, unsigned height);
 
+int pty_forward_set_background_color(PTYForward *f, const char *color);
+
 DEFINE_TRIVIAL_CLEANUP_FUNC(PTYForward*, pty_forward_free);
index e5a1f94f2a2945478c6c9f2918ea18fb19ccfaa3..a56b8365fb4bd9569683c3d7576e235ec30c9df4 100644 (file)
@@ -78,8 +78,7 @@ int specifier_printf(const char *text, size_t max_length, const Specifier table[
 
                                         if (!GREEDY_REALLOC(result, j + k + l + 1))
                                                 return -ENOMEM;
-                                        memcpy(result + j, w, k);
-                                        t = result + j + k;
+                                        t = mempcpy(result + j, w, k);
                                 } else if (strchr(POSSIBLE_SPECIFIERS, *f))
                                         /* Oops, an unknown specifier. */
                                         return -EBADSLT;
index 3882a180c4ada07d7dbc251b422fc91b58be74e3..195be70b039acce5dd0a33ca350c7131af9a3d14 100644 (file)
@@ -21,6 +21,7 @@
 #include "fd-util.h"
 #include "fs-util.h"
 #include "log.h"
+#include "mountpoint-util.h"
 #include "namespace-util.h"
 #include "path-util.h"
 #include "process-util.h"
@@ -249,7 +250,7 @@ static int allocate_scope(void) {
         if (r < 0)
                 return bus_log_parse_error(r);
 
-        r = bus_wait_for_jobs_one(w, object, false, NULL);
+        r = bus_wait_for_jobs_one(w, object, BUS_WAIT_JOBS_LOG_ERROR, NULL);
         if (r < 0)
                 return r;
 
@@ -266,7 +267,7 @@ static int enter_cgroup(char **ret_cgroup, bool enter_subroot) {
                 log_warning_errno(r, "Couldn't allocate a scope unit for this test, proceeding without.");
 
         r = cg_pid_get_path(NULL, 0, &cgroup_root);
-        if (r == -ENOMEDIUM)
+        if (IN_SET(r, -ENOMEDIUM, -ENOENT))
                 return log_warning_errno(r, "cg_pid_get_path(NULL, 0, ...) failed: %m");
         assert(r >= 0);
 
diff --git a/src/shared/varlink-io.systemd.Credentials.c b/src/shared/varlink-io.systemd.Credentials.c
new file mode 100644 (file)
index 0000000..b827337
--- /dev/null
@@ -0,0 +1,34 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "varlink-io.systemd.Credentials.h"
+
+static VARLINK_DEFINE_METHOD(
+                Encrypt,
+                VARLINK_DEFINE_INPUT(name, VARLINK_STRING, VARLINK_NULLABLE),
+                VARLINK_DEFINE_INPUT(text, VARLINK_STRING, VARLINK_NULLABLE),
+                VARLINK_DEFINE_INPUT(data, VARLINK_STRING, VARLINK_NULLABLE),
+                VARLINK_DEFINE_INPUT(timestamp, VARLINK_INT, VARLINK_NULLABLE),
+                VARLINK_DEFINE_INPUT(notAfter, VARLINK_INT, VARLINK_NULLABLE),
+                VARLINK_DEFINE_INPUT(allowInteractiveAuthentication, VARLINK_BOOL, VARLINK_NULLABLE),
+                VARLINK_DEFINE_OUTPUT(blob, VARLINK_STRING, 0));
+
+static VARLINK_DEFINE_METHOD(
+                Decrypt,
+                VARLINK_DEFINE_INPUT(name, VARLINK_STRING, VARLINK_NULLABLE),
+                VARLINK_DEFINE_INPUT(blob, VARLINK_STRING, 0),
+                VARLINK_DEFINE_INPUT(timestamp, VARLINK_INT, VARLINK_NULLABLE),
+                VARLINK_DEFINE_INPUT(allowInteractiveAuthentication, VARLINK_BOOL, VARLINK_NULLABLE),
+                VARLINK_DEFINE_OUTPUT(data, VARLINK_STRING, 0));
+
+static VARLINK_DEFINE_ERROR(BadFormat);
+static VARLINK_DEFINE_ERROR(NameMismatch);
+static VARLINK_DEFINE_ERROR(TimeMismatch);
+
+VARLINK_DEFINE_INTERFACE(
+                io_systemd_Credentials,
+                "io.systemd.Credentials",
+                &vl_method_Encrypt,
+                &vl_method_Decrypt,
+                &vl_error_BadFormat,
+                &vl_error_NameMismatch,
+                &vl_error_TimeMismatch);
diff --git a/src/shared/varlink-io.systemd.Credentials.h b/src/shared/varlink-io.systemd.Credentials.h
new file mode 100644 (file)
index 0000000..c0ecc3d
--- /dev/null
@@ -0,0 +1,6 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include "varlink-idl.h"
+
+extern const VarlinkInterface vl_interface_io_systemd_Credentials;
index 2ba942f30e2a240b1858c379a92182f6539e1fd1..12a27fcb8dfc43061af25d12cc50e24dd1cbf7e9 100644 (file)
@@ -1006,7 +1006,7 @@ static int varlink_dispatch_local_error(Varlink *v, const char *error) {
 
         r = v->reply_callback(v, NULL, error, VARLINK_REPLY_ERROR|VARLINK_REPLY_LOCAL, v->userdata);
         if (r < 0)
-                log_debug_errno(r, "Reply callback returned error, ignoring: %m");
+                varlink_log_errno(v, r, "Reply callback returned error, ignoring: %m");
 
         return 1;
 }
@@ -1038,12 +1038,25 @@ static int varlink_dispatch_disconnect(Varlink *v) {
 }
 
 static int varlink_sanitize_parameters(JsonVariant **v) {
+        int r;
+
         assert(v);
 
         /* Varlink always wants a parameters list, hence make one if the caller doesn't want any */
         if (!*v)
                 return json_variant_new_object(v, NULL, 0);
-        else if (!json_variant_is_object(*v))
+        if (json_variant_is_null(*v)) {
+                JsonVariant *empty;
+
+                r = json_variant_new_object(&empty, NULL, 0);
+                if (r < 0)
+                        return r;
+
+                json_variant_unref(*v);
+                *v = empty;
+                return 0;
+        }
+        if (!json_variant_is_object(*v))
                 return -EINVAL;
 
         return 0;
@@ -1083,7 +1096,7 @@ static int varlink_dispatch_reply(Varlink *v) {
                 } else if (streq(k, "parameters")) {
                         if (parameters)
                                 goto invalid;
-                        if (!json_variant_is_object(e))
+                        if (!json_variant_is_object(e) && !json_variant_is_null(e))
                                 goto invalid;
 
                         parameters = json_variant_ref(e);
@@ -1119,7 +1132,7 @@ static int varlink_dispatch_reply(Varlink *v) {
                 if (v->reply_callback) {
                         r = v->reply_callback(v, parameters, error, flags, v->userdata);
                         if (r < 0)
-                                log_debug_errno(r, "Reply callback returned error, ignoring: %m");
+                                varlink_log_errno(v, r, "Reply callback returned error, ignoring: %m");
                 }
 
                 varlink_clear_current(v);
@@ -1163,9 +1176,7 @@ static int generic_method_get_info(
         assert(link);
 
         if (json_variant_elements(parameters) != 0)
-                return varlink_errorb(link, VARLINK_ERROR_INVALID_PARAMETER,
-                                      JSON_BUILD_OBJECT(
-                                                      JSON_BUILD_PAIR_VARIANT("parameter", json_variant_by_index(parameters, 0))));
+                return varlink_error_invalid_parameter(link, parameters);
 
         product = strjoin("systemd (", program_invocation_short_name, ")");
         if (!product)
@@ -1256,7 +1267,7 @@ static int varlink_dispatch_method(Varlink *v) {
                 } else if (streq(k, "parameters")) {
                         if (parameters)
                                 goto invalid;
-                        if (!json_variant_is_object(e))
+                        if (!json_variant_is_object(e) && !json_variant_is_null(e))
                                 goto invalid;
 
                         parameters = json_variant_ref(e);
@@ -1314,16 +1325,16 @@ static int varlink_dispatch_method(Varlink *v) {
 
                 v->current_method = hashmap_get(v->server->symbols, method);
                 if (!v->current_method)
-                        log_debug("No interface description defined for method '%s', not validating.", method);
+                        varlink_log(v, "No interface description defined for method '%s', not validating.", method);
                 else {
                         const char *bad_field;
 
                         r = varlink_idl_validate_method_call(v->current_method, parameters, &bad_field);
                         if (r < 0) {
-                                log_debug_errno(r, "Parameters for method %s() didn't pass validation on field '%s': %m", method, strna(bad_field));
+                                varlink_log_errno(v, r, "Parameters for method %s() didn't pass validation on field '%s': %m", method, strna(bad_field));
 
-                                if (!FLAGS_SET(flags, VARLINK_METHOD_ONEWAY)) {
-                                        r = varlink_errorb(v, VARLINK_ERROR_INVALID_PARAMETER, JSON_BUILD_OBJECT(JSON_BUILD_PAIR_STRING("parameter", bad_field)));
+                                if (IN_SET(v->state, VARLINK_PROCESSING_METHOD, VARLINK_PROCESSING_METHOD_MORE)) {
+                                        r = varlink_error_invalid_parameter_name(v, bad_field);
                                         if (r < 0)
                                                 return r;
                                 }
@@ -1334,24 +1345,21 @@ static int varlink_dispatch_method(Varlink *v) {
                 if (!invalid) {
                         r = callback(v, parameters, flags, v->userdata);
                         if (r < 0) {
-                                log_debug_errno(r, "Callback for %s returned error: %m", method);
+                                varlink_log_errno(v, r, "Callback for %s returned error: %m", method);
 
                                 /* We got an error back from the callback. Propagate it to the client if the method call remains unanswered. */
-                                if (v->state == VARLINK_PROCESSED_METHOD)
-                                        r = 0; /* already processed */
-                                else if (!FLAGS_SET(flags, VARLINK_METHOD_ONEWAY)) {
+                                if (IN_SET(v->state, VARLINK_PROCESSING_METHOD, VARLINK_PROCESSING_METHOD_MORE)) {
                                         r = varlink_error_errno(v, r);
                                         if (r < 0)
                                                 return r;
                                 }
                         }
                 }
-        } else if (!FLAGS_SET(flags, VARLINK_METHOD_ONEWAY)) {
+        } else if (IN_SET(v->state, VARLINK_PROCESSING_METHOD, VARLINK_PROCESSING_METHOD_MORE)) {
                 r = varlink_errorb(v, VARLINK_ERROR_METHOD_NOT_FOUND, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("method", JSON_BUILD_STRING(method))));
                 if (r < 0)
                         return r;
-        } else
-                r = 0;
+        }
 
         switch (v->state) {
 
@@ -1373,7 +1381,7 @@ static int varlink_dispatch_method(Varlink *v) {
                 assert_not_reached();
         }
 
-        return r;
+        return 1;
 
 invalid:
         r = -EINVAL;
@@ -2259,7 +2267,7 @@ int varlink_reply(Varlink *v, JsonVariant *parameters) {
 
                 r = varlink_idl_validate_method_reply(v->current_method, parameters, &bad_field);
                 if (r < 0)
-                        log_debug_errno(r, "Return parameters for method reply %s() didn't pass validation on field '%s', ignoring: %m", v->current_method->name, strna(bad_field));
+                        varlink_log_errno(v, r, "Return parameters for method reply %s() didn't pass validation on field '%s', ignoring: %m", v->current_method->name, strna(bad_field));
         }
 
         r = varlink_enqueue_json(v, m);
@@ -2330,13 +2338,13 @@ int varlink_error(Varlink *v, const char *error_id, JsonVariant *parameters) {
 
         VarlinkSymbol *symbol = hashmap_get(v->server->symbols, error_id);
         if (!symbol)
-                log_debug("No interface description defined for error '%s', not validating.", error_id);
+                varlink_log(v, "No interface description defined for error '%s', not validating.", error_id);
         else {
                 const char *bad_field = NULL;
 
                 r = varlink_idl_validate_error(symbol, parameters, &bad_field);
                 if (r < 0)
-                        log_debug_errno(r, "Parameters for error %s didn't pass validation on field '%s', ignoring: %m", error_id, strna(bad_field));
+                        varlink_log_errno(v, r, "Parameters for error %s didn't pass validation on field '%s', ignoring: %m", error_id, strna(bad_field));
         }
 
         r = varlink_enqueue_json(v, m);
@@ -2412,6 +2420,13 @@ int varlink_error_invalid_parameter(Varlink *v, JsonVariant *parameters) {
         return -EINVAL;
 }
 
+int varlink_error_invalid_parameter_name(Varlink *v, const char *name) {
+        return varlink_errorb(
+                        v,
+                        VARLINK_ERROR_INVALID_PARAMETER,
+                        JSON_BUILD_OBJECT(JSON_BUILD_PAIR("parameter", JSON_BUILD_STRING(name))));
+}
+
 int varlink_error_errno(Varlink *v, int error) {
         return varlink_errorb(
                         v,
@@ -2451,7 +2466,7 @@ int varlink_notify(Varlink *v, JsonVariant *parameters) {
 
                 r = varlink_idl_validate_method_reply(v->current_method, parameters, &bad_field);
                 if (r < 0)
-                        log_debug_errno(r, "Return parameters for method reply %s() didn't pass validation on field '%s', ignoring: %m", v->current_method->name, strna(bad_field));
+                        varlink_log_errno(v, r, "Return parameters for method reply %s() didn't pass validation on field '%s', ignoring: %m", v->current_method->name, strna(bad_field));
         }
 
         r = varlink_enqueue_json(v, m);
@@ -2491,8 +2506,7 @@ int varlink_dispatch(Varlink *v, JsonVariant *parameters, const JsonDispatch tab
         r = json_dispatch_full(parameters, table, /* bad= */ NULL, /* flags= */ 0, userdata, &bad_field);
         if (r < 0) {
                 if (bad_field)
-                        return varlink_errorb(v, VARLINK_ERROR_INVALID_PARAMETER,
-                                              JSON_BUILD_OBJECT(JSON_BUILD_PAIR("parameter", JSON_BUILD_STRING(bad_field))));
+                        return varlink_error_invalid_parameter_name(v, bad_field);
                 return r;
         }
 
@@ -3008,9 +3022,11 @@ static int count_connection(VarlinkServer *server, const struct ucred *ucred) {
         server->n_connections++;
 
         if (FLAGS_SET(server->flags, VARLINK_SERVER_ACCOUNT_UID)) {
+                assert(uid_is_valid(ucred->uid));
+
                 r = hashmap_ensure_allocated(&server->by_uid, NULL);
                 if (r < 0)
-                        return log_debug_errno(r, "Failed to allocate UID hash table: %m");
+                        return varlink_server_log_errno(server, r, "Failed to allocate UID hash table: %m");
 
                 c = PTR_TO_UINT(hashmap_get(server->by_uid, UID_TO_PTR(ucred->uid)));
 
@@ -3019,7 +3035,7 @@ static int count_connection(VarlinkServer *server, const struct ucred *ucred) {
 
                 r = hashmap_replace(server->by_uid, UID_TO_PTR(ucred->uid), UINT_TO_PTR(c + 1));
                 if (r < 0)
-                        return log_debug_errno(r, "Failed to increment counter in UID hash table: %m");
+                        return varlink_server_log_errno(server, r, "Failed to increment counter in UID hash table: %m");
         }
 
         return 0;
@@ -3459,7 +3475,7 @@ int varlink_server_bind_method(VarlinkServer *s, const char *method, VarlinkMeth
 
         if (varlink_symbol_in_interface(method, "org.varlink.service") ||
             varlink_symbol_in_interface(method, "io.systemd"))
-                return log_debug_errno(SYNTHETIC_ERRNO(EEXIST), "Cannot bind server to '%s'.", method);
+                return varlink_server_log_errno(s, SYNTHETIC_ERRNO(EEXIST), "Cannot bind server to '%s'.", method);
 
         m = strdup(method);
         if (!m)
@@ -3469,7 +3485,7 @@ int varlink_server_bind_method(VarlinkServer *s, const char *method, VarlinkMeth
         if (r == -ENOMEM)
                 return log_oom_debug();
         if (r < 0)
-                return log_debug_errno(r, "Failed to register callback: %m");
+                return varlink_server_log_errno(s, r, "Failed to register callback: %m");
         if (r > 0)
                 TAKE_PTR(m);
 
@@ -3506,7 +3522,7 @@ int varlink_server_bind_connect(VarlinkServer *s, VarlinkConnect callback) {
         assert_return(s, -EINVAL);
 
         if (callback && s->connect_callback && callback != s->connect_callback)
-                return log_debug_errno(SYNTHETIC_ERRNO(EBUSY), "A different callback was already set.");
+                return varlink_server_log_errno(s, SYNTHETIC_ERRNO(EBUSY), "A different callback was already set.");
 
         s->connect_callback = callback;
         return 0;
@@ -3516,7 +3532,7 @@ int varlink_server_bind_disconnect(VarlinkServer *s, VarlinkDisconnect callback)
         assert_return(s, -EINVAL);
 
         if (callback && s->disconnect_callback && callback != s->disconnect_callback)
-                return log_debug_errno(SYNTHETIC_ERRNO(EBUSY), "A different callback was already set.");
+                return varlink_server_log_errno(s, SYNTHETIC_ERRNO(EBUSY), "A different callback was already set.");
 
         s->disconnect_callback = callback;
         return 0;
@@ -3530,7 +3546,7 @@ int varlink_server_add_interface(VarlinkServer *s, const VarlinkInterface *inter
         assert_return(interface->name, -EINVAL);
 
         if (hashmap_contains(s->interfaces, interface->name))
-                return log_debug_errno(SYNTHETIC_ERRNO(EEXIST), "Duplicate registration of interface '%s'.", interface->name);
+                return varlink_server_log_errno(s, SYNTHETIC_ERRNO(EEXIST), "Duplicate registration of interface '%s'.", interface->name);
 
         r = hashmap_ensure_put(&s->interfaces, &string_hash_ops, interface->name, (void*) interface);
         if (r < 0)
@@ -3683,11 +3699,11 @@ int varlink_server_deserialize_one(VarlinkServer *s, const char *value, FDSet *f
                 return log_oom_debug();
 
         if (v[n] != ' ')
-                return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
+                return varlink_server_log_errno(s, SYNTHETIC_ERRNO(EINVAL),
                                        "Failed to deserialize VarlinkServerSocket: %s: %m", value);
         v = startswith(v + n + 1, "varlink-server-socket-fd=");
         if (!v)
-                return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
+                return varlink_server_log_errno(s, SYNTHETIC_ERRNO(EINVAL),
                                        "Failed to deserialize VarlinkServerSocket fd %s: %m", value);
 
         n = strcspn(v, " ");
@@ -3695,9 +3711,9 @@ int varlink_server_deserialize_one(VarlinkServer *s, const char *value, FDSet *f
 
         fd = parse_fd(buf);
         if (fd < 0)
-                return log_debug_errno(fd, "Unable to parse VarlinkServerSocket varlink-server-socket-fd=%s: %m", buf);
+                return varlink_server_log_errno(s, fd, "Unable to parse VarlinkServerSocket varlink-server-socket-fd=%s: %m", buf);
         if (!fdset_contains(fds, fd))
-                return log_debug_errno(SYNTHETIC_ERRNO(EBADF),
+                return varlink_server_log_errno(s, SYNTHETIC_ERRNO(EBADF),
                                        "VarlinkServerSocket varlink-server-socket-fd= has unknown fd %d: %m", fd);
 
         ss = new(VarlinkServerSocket, 1);
@@ -3712,7 +3728,7 @@ int varlink_server_deserialize_one(VarlinkServer *s, const char *value, FDSet *f
 
         r = varlink_server_add_socket_event_source(s, ss, SD_EVENT_PRIORITY_NORMAL);
         if (r < 0)
-                return log_debug_errno(r, "Failed to add VarlinkServerSocket event source to the event loop: %m");
+                return varlink_server_log_errno(s, r, "Failed to add VarlinkServerSocket event source to the event loop: %m");
 
         LIST_PREPEND(sockets, s->sockets, TAKE_PTR(ss));
         return 0;
index 6ec708aba20137dfcf83ca0676d6be909ee72060..c60f695be78ec77acd062b76fefa985355680a9c 100644 (file)
@@ -109,6 +109,7 @@ int varlink_replyb(Varlink *v, ...);
 int varlink_error(Varlink *v, const char *error_id, JsonVariant *parameters);
 int varlink_errorb(Varlink *v, const char *error_id, ...);
 int varlink_error_invalid_parameter(Varlink *v, JsonVariant *parameters);
+int varlink_error_invalid_parameter_name(Varlink *v, const char *name);
 int varlink_error_errno(Varlink *v, int error);
 
 /* Enqueue a "more" reply */
@@ -218,7 +219,5 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(VarlinkServer *, varlink_server_unref);
 #define VARLINK_ERROR_METHOD_NOT_FOUND "org.varlink.service.MethodNotFound"
 #define VARLINK_ERROR_METHOD_NOT_IMPLEMENTED "org.varlink.service.MethodNotImplemented"
 #define VARLINK_ERROR_INVALID_PARAMETER "org.varlink.service.InvalidParameter"
-
-/* These are errors we came up with and squatted the namespace with */
 #define VARLINK_ERROR_PERMISSION_DENIED "org.varlink.service.PermissionDenied"
 #define VARLINK_ERROR_EXPECTED_MORE "org.varlink.service.ExpectedMore"
index b976b7d8cf60d299cc96d4a8192dddfc62003f34..b709078afed190056bb3b47951d87a4cb415ef08 100644 (file)
@@ -387,6 +387,13 @@ int main(int argc, char *argv[]) {
                 goto error;
         }
 
+        /* This is primarily useful when running systemd in a VM, as it provides the user running the VM with
+         * a mechanism to pick up systemd's exit status in the VM. Note that we execute this as early as
+         * possible since otherwise we might shut down the VM before the AF_VSOCK buffers have been flushed.
+         * While this doesn't guarantee the message will arrive, in practice we do enough work after this
+         * that the message should always arrive on the host */
+        (void) sd_notifyf(0, "EXIT_STATUS=%i", arg_exit_code);
+
         (void) cg_get_root_path(&cgroup);
         bool in_container = detect_container() > 0;
 
@@ -582,10 +589,6 @@ int main(int argc, char *argv[]) {
         if (!in_container)
                 sync_with_progress();
 
-        /* This is primarily useful when running systemd in a VM, as it provides the user running the VM with
-         * a mechanism to pick up systemd's exit status in the VM. */
-        (void) sd_notifyf(0, "EXIT_STATUS=%i", arg_exit_code);
-
         if (streq(arg_verb, "exit")) {
                 if (in_container) {
                         log_info("Exiting container.");
index 8dc515e4d5241cbe1ecfa87f814e3ecba538bd04..33cb8c19635239839c6dad12e704c1ecf6b0b091 100644 (file)
@@ -342,7 +342,7 @@ static int parse_image_class_parameter(Varlink *link, const char *value, ImageCl
 
         c = image_class_from_string(value);
         if (!IN_SET(c, IMAGE_SYSEXT, IMAGE_CONFEXT))
-                return varlink_errorb(link, VARLINK_ERROR_INVALID_PARAMETER, JSON_BUILD_OBJECT(JSON_BUILD_PAIR_STRING("parameter", "class")));
+                return varlink_error_invalid_parameter_name(link, "class");
 
         if (hierarchies) {
                 r = parse_env_extension_hierarchies(&h, image_class_info[c].name_env);
index 2cf746c5a6057d7b5e424b9fa67e3013c1b0f453..d486b406da556cd8e9bd156dd994f454082645d7 100644 (file)
@@ -121,8 +121,7 @@ static int set_exit_code(uint8_t code) {
 }
 
 int verb_start_special(int argc, char *argv[], void *userdata) {
-        bool termination_action; /* An action that terminates the manager, can be performed also by
-                                  * signal. */
+        bool termination_action; /* An action that terminates the system, can be performed also by signal. */
         enum action a;
         int r;
 
@@ -140,17 +139,21 @@ int verb_start_special(int argc, char *argv[], void *userdata) {
                         return r;
         }
 
-        r = prepare_firmware_setup();
-        if (r < 0)
-                return r;
+        termination_action = IN_SET(a, ACTION_HALT, ACTION_POWEROFF, ACTION_REBOOT);
 
-        r = prepare_boot_loader_menu();
-        if (r < 0)
-                return r;
+        if (termination_action) {
+                r = prepare_firmware_setup();
+                if (r < 0)
+                        return r;
 
-        r = prepare_boot_loader_entry();
-        if (r < 0)
-                return r;
+                r = prepare_boot_loader_menu();
+                if (r < 0)
+                        return r;
+
+                r = prepare_boot_loader_entry();
+                if (r < 0)
+                        return r;
+        }
 
         if (a == ACTION_REBOOT) {
                 if (arg_reboot_argument) {
@@ -181,10 +184,6 @@ int verb_start_special(int argc, char *argv[], void *userdata) {
                         return r;
         }
 
-        termination_action = IN_SET(a,
-                                    ACTION_HALT,
-                                    ACTION_POWEROFF,
-                                    ACTION_REBOOT);
         if (termination_action && arg_force >= 2)
                 return halt_now(a);
 
index ae7e25eedb626003d11b5780b9a3770887d37d65..c844f7e1e7f63274a9bd27c954d97863e275bfc3 100644 (file)
@@ -388,8 +388,11 @@ int verb_start(int argc, char *argv[], void *userdata) {
 
         if (!arg_no_block) {
                 const char* extra_args[4];
+                WaitJobsFlags flags = 0;
 
-                r = bus_wait_for_jobs(w, arg_quiet, make_extra_args(extra_args));
+                SET_FLAG(flags, BUS_WAIT_JOBS_LOG_ERROR, !arg_quiet);
+                SET_FLAG(flags, BUS_WAIT_JOBS_LOG_SUCCESS, arg_show_transaction);
+                r = bus_wait_for_jobs(w, flags, make_extra_args(extra_args));
                 if (r < 0)
                         return r;
 
index a59461acc44afa2bcf2a08d092160b1e13e2e7c1..aec125d483ac9d68fb4170f24c0bd341c1b6c0bb 100644 (file)
@@ -57,6 +57,7 @@ simple_tests += files(
         'test-cgroup.c',
         'test-chase.c',
         'test-clock.c',
+        'test-color-util.c',
         'test-compare-operator.c',
         'test-condition.c',
         'test-conf-files.c',
@@ -578,12 +579,18 @@ executables += [
         },
         test_template + {
                 'sources' : files('../libsystemd/sd-device/test-sd-device-thread.c'),
-                'link_with' : libsystemd,
+                'link_with' : [
+                        libbasic,
+                        libsystemd,
+                ],
                 'dependencies' : threads,
         },
         test_template + {
                 'sources' : files('../libudev/test-udev-device-thread.c'),
-                'link_with' : libudev,
+                'link_with' : [
+                        libbasic,
+                        libudev,
+                ],
                 'dependencies' : threads,
         },
         test_template + {
index 18611fc051fdd6fb54e9b9e4d31b05b10320abc5..86d7702bf8a615fbd06ee1230cf2ea026ef5e0f7 100644 (file)
@@ -188,23 +188,22 @@ TEST_RET(bootspec_boot_config_find_entry) {
         assert_se(boot_config_load(&config, d, NULL) >= 0);
         assert_se(config.n_entries == 2);
 
-        // Test finding the first entry
+        /* Test finding the first entry */
         BootEntry *entry = boot_config_find_entry(&config, "a-10.conf");
         assert_se(entry && streq(entry->id, "a-10.conf"));
 
-        // Test finding the second entry
+        /* Test finding the second entry */
         entry = boot_config_find_entry(&config, "a-05.conf");
         assert_se(entry && streq(entry->id, "a-05.conf"));
 
-        // Test finding a non-existent entry
+        /* Test finding a non-existent entry */
         entry = boot_config_find_entry(&config, "nonexistent.conf");
         assert_se(entry == NULL);
 
-        // Test case-insensitivity
+        /* Test case-insensitivity */
         entry = boot_config_find_entry(&config, "A-10.CONF");
         assert_se(entry && streq(entry->id, "a-10.conf"));
 
-
         return 0;
 }
 
index 51f52d9a4ef933c1febc7e186370c390d87a40bf..cb5a981dc3c0871fa528dc0d2a52c8fd0226bf45 100644 (file)
@@ -362,7 +362,7 @@ TEST(cg_tests) {
         int all, hybrid, systemd, r;
 
         r = cg_unified();
-        if (r == -ENOMEDIUM) {
+        if (IN_SET(r, -ENOENT, -ENOMEDIUM)) {
                 log_tests_skipped("cgroup not mounted");
                 return;
         }
@@ -395,7 +395,7 @@ TEST(cg_get_keyed_attribute) {
         int i, r;
 
         r = cg_get_keyed_attribute("cpu", "/init.scope", "no_such_file", STRV_MAKE("no_such_attr"), &val);
-        if (r == -ENOMEDIUM || ERRNO_IS_PRIVILEGE(r)) {
+        if (IN_SET(r, -ENOMEDIUM, -ENOENT) || ERRNO_IS_PRIVILEGE(r)) {
                 log_info_errno(r, "Skipping most of %s, /sys/fs/cgroup not accessible: %m", __func__);
                 return;
         }
index 0fbd6351a4abfa0a5f20a4e9e4f9a3f06b1b0f26..9b174d708b562b572793202cf54f7a39743045f4 100644 (file)
@@ -44,8 +44,8 @@ TEST(cg_create) {
         int r;
 
         r = cg_unified_cached(false);
-        if (r == -ENOMEDIUM) {
-                log_tests_skipped("cgroup not mounted");
+        if (IN_SET(r, -ENOMEDIUM, -ENOENT)) {
+                log_tests_skipped("cgroupfs is not mounted");
                 return;
         }
         assert_se(r >= 0);
diff --git a/src/test/test-color-util.c b/src/test/test-color-util.c
new file mode 100644 (file)
index 0000000..3d00d87
--- /dev/null
@@ -0,0 +1,63 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "color-util.h"
+#include "tests.h"
+
+TEST(hsv_to_rgb) {
+        uint8_t r, g, b;
+
+        hsv_to_rgb(0, 0, 0, &r, &g, &b);
+        assert(r == 0 && g == 0 && b == 0);
+
+        hsv_to_rgb(60, 0, 0, &r, &g, &b);
+        assert(r == 0 && g == 0 && b == 0);
+
+        hsv_to_rgb(0, 0, 100, &r, &g, &b);
+        assert(r == 255 && g == 255 && b == 255);
+
+        hsv_to_rgb(0, 100, 100, &r, &g, &b);
+        assert(r == 255 && g == 0 && b == 0);
+
+        hsv_to_rgb(120, 100, 100, &r, &g, &b);
+        assert(r == 0 && g == 255 && b == 0);
+
+        hsv_to_rgb(240, 100, 100, &r, &g, &b);
+        assert(r == 0 && g == 0 && b == 255);
+
+        hsv_to_rgb(311, 52, 62, &r, &g, &b);
+        assert(r == 158 && g == 75 && b == 143);
+}
+
+TEST(rgb_to_hsv) {
+
+        double h, s, v;
+        rgb_to_hsv(0, 0, 0, &h, &s, &v);
+        assert(s <= 0);
+        assert(v <= 0);
+
+        rgb_to_hsv(1, 1, 1, &h, &s, &v);
+        assert(s <= 0);
+        assert(v >= 100);
+
+        rgb_to_hsv(1, 0, 0, &h, &s, &v);
+        assert(h >= 359 || h <= 1);
+        assert(s >= 100);
+        assert(v >= 100);
+
+        rgb_to_hsv(0, 1, 0, &h, &s, &v);
+        assert(h >= 119 && h <= 121);
+        assert(s >= 100);
+        assert(v >= 100);
+
+        rgb_to_hsv(0, 0, 1, &h, &s, &v);
+        assert(h >= 239 && h <= 241);
+        assert(s >= 100);
+        assert(v >= 100);
+
+        rgb_to_hsv(0.5, 0.6, 0.7, &h, &s, &v);
+        assert(h >= 209 && h <= 211);
+        assert(s >= 28 && s <= 31);
+        assert(v >= 69 && v <= 71);
+}
+
+DEFINE_TEST_MAIN(LOG_DEBUG);
index bb987613e151fa786f559e6d3034079aa6ebb6e3..66208463606c8a04b1c1ad2a1350e2bffe307abd 100644 (file)
@@ -139,8 +139,8 @@ TEST(condition_test_control_group_hierarchy) {
         int r;
 
         r = cg_unified();
-        if (r == -ENOMEDIUM) {
-                log_tests_skipped("cgroup not mounted");
+        if (IN_SET(r, -ENOMEDIUM, -ENOENT)) {
+                log_tests_skipped("cgroupfs is not mounted");
                 return;
         }
         assert_se(r >= 0);
@@ -163,8 +163,8 @@ TEST(condition_test_control_group_controller) {
         int r;
 
         r = cg_unified();
-        if (r == -ENOMEDIUM) {
-                log_tests_skipped("cgroup not mounted");
+        if (IN_SET(r, -ENOMEDIUM, -ENOENT)) {
+                log_tests_skipped("cgroupfs is not mounted");
                 return;
         }
         assert_se(r >= 0);
index dffbad6327ba4e03decb3e9313683dd71ae9a76d..c267c2e34189116acae7c18b1d179e2eaf377151 100644 (file)
@@ -133,6 +133,27 @@ TEST(strv_env_assign) {
         assert_se(streq(a[0], "a=A"));
 }
 
+TEST(strv_env_assignf) {
+        _cleanup_strv_free_ char **a = NULL;
+
+        assert_se(strv_env_assignf(&a, "a", "a") > 0);
+        assert_se(strv_env_assignf(&a, "a", "%c", 'a') == 0);
+
+        assert_se(strv_env_assignf(&a, "c", "xxx%iyyy", 5) > 0);
+        assert_se(strv_length(a) == 2);
+        assert_se(strv_equal(a, STRV_MAKE("a=a", "c=xxx5yyy")));
+        assert_se(strv_env_assignf(&a, "c", NULL) == 0);
+
+        assert_se(strv_env_assignf(&a, "b", "b") > 0);
+        assert_se(strv_env_assignf(&a, "a", "A") == 0);
+        assert_se(strv_env_assignf(&a, "b", NULL) == 0);
+
+        assert_se(strv_env_assignf(&a, "a=", "B") == -EINVAL);
+
+        assert_se(strv_length(a) == 1);
+        assert_se(streq(a[0], "a=A"));
+}
+
 TEST(strv_env_assign_many) {
         _cleanup_strv_free_ char **a = NULL;
 
index 26ce4cee7970532fdbcf976980d52bbdbd3b60c2..23d09813d91473858d5049fe83e3fd8c179cf588 100644 (file)
@@ -220,7 +220,7 @@ TEST(real_pressure) {
 
         assert_se(sd_bus_message_read(reply, "o", &object) >= 0);
 
-        assert_se(bus_wait_for_jobs_one(w, object, /* quiet= */ false, /* extra_args= */ NULL) >= 0);
+        assert_se(bus_wait_for_jobs_one(w, object, /* flags= */ BUS_WAIT_JOBS_LOG_ERROR, /* extra_args= */ NULL) >= 0);
 
         assert_se(sd_event_default(&e) >= 0);
 
index c3d0acb6afe71049b767090eff343fd06675b34c..069a63290cecf529ba2ca96dd1d89781df68f9cd 100644 (file)
@@ -276,7 +276,17 @@ TEST(make_mount_switch_root) {
         assert_se(s);
         assert_se(touch(s) >= 0);
 
-        for (int force_ms_move = 0; force_ms_move < 2; force_ms_move++) {
+        struct {
+                const char *path;
+                bool force_ms_move;
+        } table[] = {
+                { t,   false },
+                { t,   true  },
+                { "/", false },
+                { "/", true  },
+        };
+
+        FOREACH_ARRAY(i, table, ELEMENTSOF(table)) {
                 r = safe_fork("(switch-root)",
                               FORK_RESET_SIGNALS |
                               FORK_CLOSE_ALL_FDS |
@@ -290,12 +300,14 @@ TEST(make_mount_switch_root) {
                 assert_se(r >= 0);
 
                 if (r == 0) {
-                        assert_se(make_mount_point(t) >= 0);
-                        assert_se(mount_switch_root_full(t, /* mount_propagation_flag= */ 0, force_ms_move) >= 0);
+                        assert_se(make_mount_point(i->path) >= 0);
+                        assert_se(mount_switch_root_full(i->path, /* mount_propagation_flag= */ 0, i->force_ms_move) >= 0);
 
-                        assert_se(access(ASSERT_PTR(strrchr(s, '/')), F_OK) >= 0);       /* absolute */
-                        assert_se(access(ASSERT_PTR(strrchr(s, '/')) + 1, F_OK) >= 0);   /* relative */
-                        assert_se(access(s, F_OK) < 0 && errno == ENOENT);               /* doesn't exist in our new environment */
+                        if (!path_equal(i->path, "/")) {
+                                assert_se(access(ASSERT_PTR(strrchr(s, '/')), F_OK) >= 0);       /* absolute */
+                                assert_se(access(ASSERT_PTR(strrchr(s, '/')) + 1, F_OK) >= 0);   /* relative */
+                                assert_se(access(s, F_OK) < 0 && errno == ENOENT);               /* doesn't exist in our new environment */
+                        }
 
                         _exit(EXIT_SUCCESS);
                 }
index ff447c658218bfafad6bb42233cf6f6351952cdd..40b12dd0880724cb4f0faf7481be391f9d6e8461 100644 (file)
@@ -138,11 +138,6 @@ TEST(path_is_mount_point) {
         assert_se(path_is_mount_point("/proc/1/", NULL, AT_SYMLINK_FOLLOW) == 0);
         assert_se(path_is_mount_point("/proc/1/", NULL, 0) == 0);
 
-        assert_se(path_is_mount_point("/sys", NULL, AT_SYMLINK_FOLLOW) > 0);
-        assert_se(path_is_mount_point("/sys", NULL, 0) > 0);
-        assert_se(path_is_mount_point("/sys/", NULL, AT_SYMLINK_FOLLOW) > 0);
-        assert_se(path_is_mount_point("/sys/", NULL, 0) > 0);
-
         /* we'll create a hierarchy of different kinds of dir/file/link
          * layouts:
          *
index 7a485f390fb518c4c74cbf1aab95cab37197dbc8..58d22b6cfeed013ec9344aaa0d479896ee78842a 100644 (file)
@@ -914,15 +914,30 @@ TEST(parse_mtu) {
         assert_se(parse_mtu(AF_UNSPEC, "4294967295", &mtu) >= 0 && mtu == 4294967295);
         assert_se(parse_mtu(AF_UNSPEC, "500", &mtu) >= 0 && mtu == 500);
         assert_se(parse_mtu(AF_UNSPEC, "1280", &mtu) >= 0 && mtu == 1280);
+        assert_se(parse_mtu(AF_UNSPEC, "4294967296", &mtu) == -ERANGE);
+        assert_se(parse_mtu(AF_UNSPEC, "68", &mtu) >= 0 && mtu == 68);
+        assert_se(parse_mtu(AF_UNSPEC, "67", &mtu) >= 0 && mtu == 67);
+        assert_se(parse_mtu(AF_UNSPEC, "0", &mtu) >= 0 && mtu == 0);
+        assert_se(parse_mtu(AF_UNSPEC, "", &mtu) == -EINVAL);
+
+        assert_se(parse_mtu(AF_INET, "1500", &mtu) >= 0 && mtu == 1500);
+        assert_se(parse_mtu(AF_INET, "1400", &mtu) >= 0 && mtu == 1400);
+        assert_se(parse_mtu(AF_INET, "65535", &mtu) >= 0 && mtu == 65535);
+        assert_se(parse_mtu(AF_INET, "65536", &mtu) >= 0 && mtu == 65536);
+        assert_se(parse_mtu(AF_INET, "4294967295", &mtu) >= 0 && mtu == 4294967295);
+        assert_se(parse_mtu(AF_INET, "500", &mtu) >= 0 && mtu == 500);
+        assert_se(parse_mtu(AF_INET, "1280", &mtu) >= 0 && mtu == 1280);
+        assert_se(parse_mtu(AF_INET, "4294967296", &mtu) == -ERANGE);
+        assert_se(parse_mtu(AF_INET, "68", &mtu) >= 0 && mtu == 68);
+        assert_se(parse_mtu(AF_INET, "67", &mtu) == -ERANGE);
+        assert_se(parse_mtu(AF_INET, "0", &mtu) == -ERANGE);
+        assert_se(parse_mtu(AF_INET, "", &mtu) == -EINVAL);
+
         assert_se(parse_mtu(AF_INET6, "1280", &mtu) >= 0 && mtu == 1280);
         assert_se(parse_mtu(AF_INET6, "1279", &mtu) == -ERANGE);
-        assert_se(parse_mtu(AF_UNSPEC, "4294967296", &mtu) == -ERANGE);
         assert_se(parse_mtu(AF_INET6, "4294967296", &mtu) == -ERANGE);
         assert_se(parse_mtu(AF_INET6, "68", &mtu) == -ERANGE);
-        assert_se(parse_mtu(AF_UNSPEC, "68", &mtu) >= 0 && mtu == 68);
-        assert_se(parse_mtu(AF_UNSPEC, "67", &mtu) == -ERANGE);
-        assert_se(parse_mtu(AF_UNSPEC, "0", &mtu) == -ERANGE);
-        assert_se(parse_mtu(AF_UNSPEC, "", &mtu) == -EINVAL);
+        assert_se(parse_mtu(AF_INET6, "", &mtu) == -EINVAL);
 }
 
 TEST(parse_loadavg_fixed_point) {
index 86d0c045555224d3feecaee4f4f8822551fda605..e14f27df7935c75312274972bfd118b679fdd901 100644 (file)
@@ -1,15 +1,20 @@
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 
 #include <sys/resource.h>
+#if HAVE_VALGRIND_VALGRIND_H
+#include <valgrind/valgrind.h>
+#endif
 
 #include "alloc-util.h"
 #include "capability-util.h"
 #include "macro.h"
 #include "missing_resource.h"
+#include "process-util.h"
 #include "rlimit-util.h"
 #include "string-util.h"
 #include "tests.h"
 #include "time-util.h"
+#include "user-util.h"
 
 static void test_rlimit_parse_format_one(int resource, const char *string, rlim_t soft, rlim_t hard, int ret, const char *formatted) {
         _cleanup_free_ char *f = NULL;
@@ -136,4 +141,45 @@ TEST(setrlimit) {
         assert_se(old.rlim_max == new.rlim_max);
 }
 
+TEST(pid_getrlimit) {
+        int r;
+
+        /* We fork off a child and read the parent's resource limit from there (i.e. our own), and compare
+         * with what getrlimit() gives us */
+
+        for (int resource = 0; resource < _RLIMIT_MAX; resource++) {
+                struct rlimit direct;
+
+                assert_se(getrlimit(resource, &direct) >= 0);
+
+                /* We fork off a child so that getrlimit() doesn't work anymore */
+                r = safe_fork("(getrlimit)", FORK_RESET_SIGNALS|FORK_DEATHSIG_SIGKILL|FORK_LOG|FORK_WAIT, /* ret_pid= */ NULL);
+                assert_se(r >= 0);
+
+                if (r == 0) {
+                        struct rlimit indirect;
+                        /* child */
+
+                        /* Drop privs, so that prlimit() doesn't work anymore */
+                        (void) setresgid(GID_NOBODY, GID_NOBODY, GID_NOBODY);
+                        (void) setresuid(UID_NOBODY, UID_NOBODY, UID_NOBODY);
+
+                        assert_se(pid_getrlimit(getppid(), resource, &indirect) >= 0);
+
+#if HAVE_VALGRIND_VALGRIND_H
+                        /* Valgrind fakes some changes in RLIMIT_NOFILE getrlimit() returns, work around that */
+                        if (RUNNING_ON_VALGRIND && resource == RLIMIT_NOFILE) {
+                                log_info("Skipping pid_getrlimit() check for RLIMIT_NOFILE, running in valgrind");
+                                _exit(EXIT_SUCCESS);
+                        }
+#endif
+
+                        assert_se(direct.rlim_cur == indirect.rlim_cur);
+                        assert_se(direct.rlim_max == indirect.rlim_max);
+
+                        _exit(EXIT_SUCCESS);
+                }
+        }
+}
+
 DEFINE_TEST_MAIN(LOG_INFO);
index a2b7101573404f490c0e9eea325e24d876cdbcd7..0b46cad9827a40b81adf46964c01b8d719d1d6e0 100644 (file)
@@ -165,4 +165,15 @@ TEST(get_ctty) {
                 log_notice("Not invoked with stdin == ctty, cutting get_ctty() test short");
 }
 
+TEST(get_default_background_color) {
+        double red, green, blue;
+        int r;
+
+        r = get_default_background_color(&red, &green, &blue);
+        if (r < 0)
+                log_notice_errno(r, "Can't get terminal default background color: %m");
+        else
+                log_notice("R=%g G=%g B=%g", red, green, blue);
+}
+
 DEFINE_TEST_MAIN(LOG_INFO);
index cbdb9c64fbb35cc764e815a7f85bdf49b3df9684..6aa1fa6b1df679f81b6160996db682a5a2570d58 100644 (file)
@@ -8,6 +8,7 @@
 #include "varlink.h"
 #include "varlink-idl.h"
 #include "varlink-io.systemd.h"
+#include "varlink-io.systemd.Credentials.h"
 #include "varlink-io.systemd.Journal.h"
 #include "varlink-io.systemd.ManagedOOM.h"
 #include "varlink-io.systemd.PCRExtend.h"
@@ -143,6 +144,8 @@ TEST(parse_format) {
         print_separator();
         test_parse_format_one(&vl_interface_io_systemd_sysext);
         print_separator();
+        test_parse_format_one(&vl_interface_io_systemd_Credentials);
+        print_separator();
         test_parse_format_one(&vl_interface_xyz_test);
 }
 
index b0c2c065e42904c4db698fc190544e020910c8e5..271af24c8040af4d5a89be7a296f2880def43643 100644 (file)
@@ -18,7 +18,7 @@ int main(int argc, char *argv[]) {
         if (getuid() != 0)
                 return log_tests_skipped("not root");
         r = enter_cgroup_subroot(NULL);
-        if (r == -ENOMEDIUM)
+        if (r < 0)
                 return log_tests_skipped("cgroupfs not available");
 
         _cleanup_free_ char *unit_dir = NULL;
index c7be30f563f9c1f98924cc2438a44d7c66bfeda5..53c2a6fb710cd4afb1f1c19f51e9087d58537166 100644 (file)
@@ -665,13 +665,12 @@ static int method_set_timezone(sd_bus_message *m, void *userdata, sd_bus_error *
         if (streq_ptr(z, c->zone))
                 return sd_bus_reply_method_return(m, NULL);
 
-        r = bus_verify_polkit_async(
+        r = bus_verify_polkit_async_full(
                         m,
-                        CAP_SYS_TIME,
                         "org.freedesktop.timedate1.set-timezone",
-                        NULL,
+                        /* details= */ NULL,
                         interactive,
-                        UID_INVALID,
+                        /* good_user= */ UID_INVALID,
                         &c->polkit_registry,
                         error);
         if (r < 0)
@@ -740,13 +739,12 @@ static int method_set_local_rtc(sd_bus_message *m, void *userdata, sd_bus_error
         if (lrtc == c->local_rtc && !fix_system)
                 return sd_bus_reply_method_return(m, NULL);
 
-        r = bus_verify_polkit_async(
+        r = bus_verify_polkit_async_full(
                         m,
-                        CAP_SYS_TIME,
                         "org.freedesktop.timedate1.set-local-rtc",
-                        NULL,
+                        /* details= */ NULL,
                         interactive,
-                        UID_INVALID,
+                        /* good_user= */ UID_INVALID,
                         &c->polkit_registry,
                         error);
         if (r < 0)
@@ -860,13 +858,12 @@ static int method_set_time(sd_bus_message *m, void *userdata, sd_bus_error *erro
         } else
                 timespec_store(&ts, (usec_t) utc);
 
-        r = bus_verify_polkit_async(
+        r = bus_verify_polkit_async_full(
                         m,
-                        CAP_SYS_TIME,
                         "org.freedesktop.timedate1.set-time",
-                        NULL,
+                        /* details= */ NULL,
                         interactive,
-                        UID_INVALID,
+                        /* good_user= */ UID_INVALID,
                         &c->polkit_registry,
                         error);
         if (r < 0)
@@ -924,13 +921,12 @@ static int method_set_ntp(sd_bus_message *m, void *userdata, sd_bus_error *error
         if (context_ntp_service_exists(c) <= 0)
                 return sd_bus_error_set(error, BUS_ERROR_NO_NTP_SUPPORT, "NTP not supported");
 
-        r = bus_verify_polkit_async(
+        r = bus_verify_polkit_async_full(
                         m,
-                        CAP_SYS_TIME,
                         "org.freedesktop.timedate1.set-ntp",
-                        NULL,
+                        /* details= */ NULL,
                         interactive,
-                        UID_INVALID,
+                        /* good_user= */ UID_INVALID,
                         &c->polkit_registry,
                         error);
         if (r < 0)
index 7237080f325be0c62d8eed4c7f03ca76bbc54b89..d1d2a14c0f577dc1d8f58f37e7f552fc2eb40a92 100644 (file)
@@ -67,10 +67,12 @@ static int method_set_runtime_servers(sd_bus_message *message, void *userdata, s
                         return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid NTP server name or address, refusing: %s", *name);
         }
 
-        r = bus_verify_polkit_async(message, CAP_NET_ADMIN,
-                                    "org.freedesktop.timesync1.set-runtime-servers",
-                                    NULL, true, UID_INVALID,
-                                    &m->polkit_registry, error);
+        r = bus_verify_polkit_async(
+                        message,
+                        "org.freedesktop.timesync1.set-runtime-servers",
+                        /* details= */ NULL,
+                        &m->polkit_registry,
+                        error);
         if (r < 0)
                 return r;
         if (r == 0)
index d8e38082fdcbfdc7ef1813b75a026c4da736258e..18a60b351e57586bd0c332bb3e426c4301889ec9 100644 (file)
@@ -1,5 +1,6 @@
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 
+#include "mountpoint-util.h"
 #include "string-util.h"
 #include "tests.h"
 #include "udev-format.h"
@@ -34,4 +35,11 @@ TEST(udev_resolve_subsys_kernel) {
         test_udev_resolve_subsys_kernel_one("[net/lo]/address", true, 0, "00:00:00:00:00:00");
 }
 
-DEFINE_TEST_MAIN(LOG_DEBUG);
+static int intro(void) {
+        if (path_is_mount_point("/sys", NULL, 0) <= 0)
+                return log_tests_skipped("/sys is not mounted");
+
+        return EXIT_SUCCESS;
+}
+
+DEFINE_TEST_MAIN_WITH_INTRO(LOG_DEBUG, intro);
index 4f43facd8054b0c9a7d5e4f742c0707ee446795a..447e50d978fcb90fe26ce8610f3a93997e3835e8 100644 (file)
@@ -1,5 +1,6 @@
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 
+#include "mountpoint-util.h"
 #include "path-util.h"
 #include "signal-util.h"
 #include "strv.h"
@@ -80,6 +81,9 @@ static void test2(void) {
 int main(int argc, char *argv[]) {
         _cleanup_free_ char *self = NULL;
 
+        if (path_is_mount_point("/sys", NULL, 0) <= 0)
+                return log_tests_skipped("/sys is not mounted");
+
         if (argc > 1) {
                 if (streq(argv[1], "test1"))
                         test1();
index 91b40088f49844f6b84098e935d001c296a0e29d..841e4615fcb1456d36a6e360e1fa75fefacfb614 100644 (file)
@@ -188,7 +188,7 @@ static int get_dev_port(sd_device *dev, bool fallback_to_dev_id, unsigned *ret)
 
         /* Get kernel provided port index for the case when multiple ports on a single PCI function. */
 
-        r = device_get_sysattr_unsigned(dev, "dev_port", &v);
+        r = device_get_sysattr_unsigned_filtered(dev, "dev_port", &v);
         if (r < 0)
                 return r;
         if (r > 0) {
@@ -204,7 +204,7 @@ static int get_dev_port(sd_device *dev, bool fallback_to_dev_id, unsigned *ret)
         if (fallback_to_dev_id) {
                 unsigned iftype;
 
-                r = device_get_sysattr_unsigned(dev, "type", &iftype);
+                r = device_get_sysattr_unsigned_filtered(dev, "type", &iftype);
                 if (r < 0)
                         return r;
 
@@ -212,7 +212,7 @@ static int get_dev_port(sd_device *dev, bool fallback_to_dev_id, unsigned *ret)
         }
 
         if (fallback_to_dev_id)
-                return device_get_sysattr_unsigned(dev, "dev_id", ret);
+                return device_get_sysattr_unsigned_filtered(dev, "dev_id", ret);
 
         /* Otherwise, return the original index 0. */
         *ret = 0;
@@ -229,7 +229,7 @@ static int get_port_specifier(sd_device *dev, bool fallback_to_dev_id, char **re
         assert(ret);
 
         /* First, try to use the kernel provided front panel port name for multiple port PCI device. */
-        r = sd_device_get_sysattr_value(dev, "phys_port_name", &phys_port_name);
+        r = device_get_sysattr_value_filtered(dev, "phys_port_name", &phys_port_name);
         if (r >= 0 && !isempty(phys_port_name)) {
                 if (naming_scheme_has(NAMING_SR_IOV_R)) {
                         int vf_id = -1;
@@ -292,10 +292,10 @@ static int pci_get_onboard_index(sd_device *dev, unsigned *ret) {
         assert(ret);
 
         /* ACPI _DSM — device specific method for naming a PCI or PCI Express device */
-        r = device_get_sysattr_unsigned(dev, "acpi_index", &idx);
+        r = device_get_sysattr_unsigned_filtered(dev, "acpi_index", &idx);
         if (r < 0)
                 /* SMBIOS type 41 — Onboard Devices Extended Information */
-                r = device_get_sysattr_unsigned(dev, "index", &idx);
+                r = device_get_sysattr_unsigned_filtered(dev, "index", &idx);
         if (r < 0)
                 return log_device_debug_errno(dev, r, "Could not obtain onboard index: %m");
 
@@ -347,7 +347,7 @@ static int names_pci_onboard_label(sd_device *dev, sd_device *pci_dev, const cha
         assert(prefix);
 
         /* retrieve on-board label from firmware */
-        r = sd_device_get_sysattr_value(pci_dev, "label", &label);
+        r = device_get_sysattr_value_filtered(pci_dev, "label", &label);
         if (r < 0)
                 return log_device_debug_errno(pci_dev, r, "Failed to get PCI onboard label: %m");
 
@@ -392,7 +392,7 @@ static int is_pci_multifunction(sd_device *dev) {
 static bool is_pci_ari_enabled(sd_device *dev) {
         assert(dev);
 
-        return device_get_sysattr_bool(dev, "ari_enabled") > 0;
+        return device_get_sysattr_bool_filtered(dev, "ari_enabled") > 0;
 }
 
 static bool is_pci_bridge(sd_device *dev) {
@@ -400,7 +400,7 @@ static bool is_pci_bridge(sd_device *dev) {
 
         assert(dev);
 
-        if (sd_device_get_sysattr_value(dev, "modalias", &v) < 0)
+        if (device_get_sysattr_value_filtered(dev, "modalias", &v) < 0)
                 return false;
 
         if (!startswith(v, "pci:"))
@@ -442,7 +442,7 @@ static int parse_hotplug_slot_from_function_id(sd_device *dev, int slots_dirfd,
                 return 0;
         }
 
-        if (sd_device_get_sysattr_value(dev, "function_id", &attr) < 0) {
+        if (device_get_sysattr_value_filtered(dev, "function_id", &attr) < 0) {
                 *ret = 0;
                 return 0;
         }
@@ -505,7 +505,7 @@ static int pci_get_hotplug_slot_from_address(
                 if (!path)
                         return log_oom_debug();
 
-                if (sd_device_get_sysattr_value(pci, path, &address) < 0)
+                if (device_get_sysattr_value_filtered(pci, path, &address) < 0)
                         continue;
 
                 /* match slot address with device by stripping the function */
@@ -845,7 +845,7 @@ static int names_devicetree(sd_device *dev, const char *prefix, bool test) {
                 if (!alias_index)
                         continue;
 
-                if (sd_device_get_sysattr_value(aliases_dev, alias, &alias_path) < 0)
+                if (device_get_sysattr_value_filtered(aliases_dev, alias, &alias_path) < 0)
                         continue;
 
                 if (!path_equal(ofnode_path, alias_path))
@@ -864,7 +864,7 @@ static int names_devicetree(sd_device *dev, const char *prefix, bool test) {
                 }
 
                 /* ...but make sure we don't have an alias conflict */
-                if (i == 0 && sd_device_get_sysattr_value(aliases_dev, conflict, NULL) >= 0)
+                if (i == 0 && device_get_sysattr_value_filtered(aliases_dev, conflict, NULL) >= 0)
                         return log_device_debug_errno(dev, SYNTHETIC_ERRNO(EEXIST),
                                         "Ethernet alias conflict: ethernet and ethernet0 both exist");
 
@@ -1133,7 +1133,7 @@ static int names_mac(sd_device *dev, const char *prefix, bool test) {
         assert(dev);
         assert(prefix);
 
-        r = device_get_sysattr_unsigned(dev, "type", &iftype);
+        r = device_get_sysattr_unsigned_filtered(dev, "type", &iftype);
         if (r < 0)
                 return log_device_debug_errno(dev, r, "Failed to read 'type' attribute: %m");
 
@@ -1145,7 +1145,7 @@ static int names_mac(sd_device *dev, const char *prefix, bool test) {
                                               "Not generating MAC name for infiniband device.");
 
         /* check for NET_ADDR_PERM, skip random MAC addresses */
-        r = device_get_sysattr_unsigned(dev, "addr_assign_type", &assign_type);
+        r = device_get_sysattr_unsigned_filtered(dev, "addr_assign_type", &assign_type);
         if (r < 0)
                 return log_device_debug_errno(dev, r, "Failed to read/parse addr_assign_type: %m");
 
@@ -1153,7 +1153,7 @@ static int names_mac(sd_device *dev, const char *prefix, bool test) {
                 return log_device_debug_errno(dev, SYNTHETIC_ERRNO(EINVAL),
                                               "addr_assign_type=%u, MAC address is not permanent.", assign_type);
 
-        r = sd_device_get_sysattr_value(dev, "address", &s);
+        r = device_get_sysattr_value_filtered(dev, "address", &s);
         if (r < 0)
                 return log_device_debug_errno(dev, r, "Failed to read 'address' attribute: %m");
 
@@ -1203,7 +1203,7 @@ static int names_netdevsim(sd_device *dev, const char *prefix, bool test) {
         if (r < 0)
                 return log_device_debug_errno(netdevsimdev, r, "Failed to parse device sysnum: %m");
 
-        r = sd_device_get_sysattr_value(dev, "phys_port_name", &phys_port_name);
+        r = device_get_sysattr_value_filtered(dev, "phys_port_name", &phys_port_name);
         if (r < 0)
                 return log_device_debug_errno(dev, r, "Failed to get 'phys_port_name' attribute: %m");
         if (isempty(phys_port_name))
@@ -1265,7 +1265,7 @@ static int get_ifname_prefix(sd_device *dev, const char **ret) {
         assert(dev);
         assert(ret);
 
-        r = device_get_sysattr_unsigned(dev, "type", &iftype);
+        r = device_get_sysattr_unsigned_filtered(dev, "type", &iftype);
         if (r < 0)
                 return r;
 
@@ -1311,7 +1311,7 @@ static int device_is_stacked(sd_device *dev) {
         if (r < 0)
                 return r;
 
-        r = device_get_sysattr_int(dev, "iflink", &iflink);
+        r = device_get_sysattr_int_filtered(dev, "iflink", &iflink);
         if (r < 0)
                 return r;
 
index 2f5429fd89d1e9f7ff0043c127a851bdb3079ea8..f306a4ffd6d82975d078be0c5eb010f0f69c923c 100644 (file)
@@ -89,7 +89,7 @@ int hwdb_main(int argc, char *argv[], void *userdata) {
 
         log_notice("udevadm hwdb is deprecated. Use systemd-hwdb instead.");
 
-        if (arg_update) {
+        if (arg_update && !hwdb_bypass()) {
                 r = hwdb_update(arg_root, arg_hwdb_bin_dir, arg_strict, true);
                 if (r < 0)
                         return r;
index b33c8cf744cc9ae894832db5a928fb585cc97ef6..6e9d86b783dee4b849054ca27a4225e4d161a2a8 100755 (executable)
@@ -773,7 +773,7 @@ def make_uki(opts):
         if opts.pcr_public_keys and len(opts.pcr_public_keys) == 1:
             pcrpkey = opts.pcr_public_keys[0]
         elif opts.pcr_private_keys and len(opts.pcr_private_keys) == 1:
-            import cryptography.hazmat.primitives.serialization as serialization
+            from cryptography.hazmat.primitives import serialization
             privkey = serialization.load_pem_private_key(opts.pcr_private_keys[0].read_bytes(), password=None)
             pcrpkey = privkey.public_key().public_bytes(
                 encoding=serialization.Encoding.PEM,
index 62a1fa5f4bf51c58aa21fad3092fde6b374a3140..bdaaab2c0557d01eb60c31e35e2f452def66e096 100644 (file)
@@ -13,6 +13,7 @@
 #include "signal-util.h"
 #include "socket-util.h"
 #include "stdio-util.h"
+#include "strv.h"
 #include "umask-util.h"
 #include "userdbd-manager.h"
 
@@ -124,6 +125,8 @@ Manager* manager_free(Manager *m) {
 
         m->deferred_start_worker_event_source = sd_event_source_unref(m->deferred_start_worker_event_source);
 
+        safe_close(m->listen_fd);
+
         sd_event_unref(m->event);
 
         return mfree(m);
@@ -183,7 +186,6 @@ static int start_one_worker(Manager *m) {
                         _exit(EXIT_FAILURE);
                 }
 
-
                 if (setenv("USERDB_FIXED_WORKER", one_zero(fixed), 1) < 0) {
                         log_error_errno(errno, "Failed to set $USERDB_FIXED_WORKER: %m");
                         _exit(EXIT_FAILURE);
@@ -263,57 +265,85 @@ static int start_workers(Manager *m, bool explicit_request) {
         return 0;
 }
 
-int manager_startup(Manager *m) {
-        int n, r;
+static int manager_make_listen_socket(Manager *m) {
+        static const union sockaddr_union sockaddr = {
+                .un.sun_family = AF_UNIX,
+                .un.sun_path = "/run/systemd/userdb/io.systemd.Multiplexer",
+        };
+        int r;
+
+        assert(m);
+
+        if (m->listen_fd >= 0)
+                return 0;
+
+        r = mkdir_p("/run/systemd/userdb", 0755);
+        if (r < 0)
+                return log_error_errno(r, "Failed to create /run/systemd/userdb: %m");
+
+        m->listen_fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0);
+        if (m->listen_fd < 0)
+                return log_error_errno(errno, "Failed to bind on socket: %m");
+
+        (void) sockaddr_un_unlink(&sockaddr.un);
+
+        WITH_UMASK(0000)
+                if (bind(m->listen_fd, &sockaddr.sa, SOCKADDR_UN_LEN(sockaddr.un)) < 0)
+                        return log_error_errno(errno, "Failed to bind socket: %m");
+
+        FOREACH_STRING(alias,
+                       "/run/systemd/userdb/io.systemd.NameServiceSwitch",
+                       "/run/systemd/userdb/io.systemd.DropIn") {
+
+                r = symlink_idempotent("io.systemd.Multiplexer", alias, /* make_relative= */ false);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to symlink '%s': %m", alias);
+        }
+
+        if (listen(m->listen_fd, SOMAXCONN_DELUXE) < 0)
+                return log_error_errno(errno, "Failed to listen on socket: %m");
+
+        return 1;
+}
+
+static int manager_scan_listen_fds(Manager *m) {
+        int n;
 
         assert(m);
-        assert(m->listen_fd < 0);
 
-        n = sd_listen_fds(false);
+        n = sd_listen_fds(/* unset_environment= */ true);
         if (n < 0)
                 return log_error_errno(n, "Failed to determine number of passed file descriptors: %m");
         if (n > 1)
                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Expected one listening fd, got %i.", n);
         if (n == 1)
                 m->listen_fd = SD_LISTEN_FDS_START;
-        else {
-                static const union sockaddr_union sockaddr = {
-                        .un.sun_family = AF_UNIX,
-                        .un.sun_path = "/run/systemd/userdb/io.systemd.Multiplexer",
-                };
-
-                r = mkdir_p("/run/systemd/userdb", 0755);
-                if (r < 0)
-                        return log_error_errno(r, "Failed to create /run/systemd/userdb: %m");
 
-                m->listen_fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0);
-                if (m->listen_fd < 0)
-                        return log_error_errno(errno, "Failed to bind on socket: %m");
-
-                (void) sockaddr_un_unlink(&sockaddr.un);
+        return 0;
+}
 
-                WITH_UMASK(0000)
-                        if (bind(m->listen_fd, &sockaddr.sa, SOCKADDR_UN_LEN(sockaddr.un)) < 0)
-                                return log_error_errno(errno, "Failed to bind socket: %m");
+int manager_startup(Manager *m) {
+        int r;
 
-                r = symlink_idempotent("io.systemd.Multiplexer",
-                                       "/run/systemd/userdb/io.systemd.NameServiceSwitch", false);
-                if (r < 0)
-                        return log_error_errno(r, "Failed to bind io.systemd.Multiplexer: %m");
+        assert(m);
+        assert(m->listen_fd < 0);
 
-                r = symlink_idempotent("io.systemd.Multiplexer",
-                                       "/run/systemd/userdb/io.systemd.DropIn", false);
-                if (r < 0)
-                        return log_error_errno(r, "Failed to bind io.systemd.Multiplexer: %m");
+        r = manager_scan_listen_fds(m);
+        if (r < 0)
+                return r;
 
-                if (listen(m->listen_fd, SOMAXCONN_DELUXE) < 0)
-                        return log_error_errno(errno, "Failed to listen on socket: %m");
-        }
+        r = manager_make_listen_socket(m);
+        if (r < 0)
+                return r;
 
         /* Let's make sure every accept() call on this socket times out after 25s. This allows workers to be
          * GC'ed on idle */
         if (setsockopt(m->listen_fd, SOL_SOCKET, SO_RCVTIMEO, TIMEVAL_STORE(LISTEN_TIMEOUT_USEC), sizeof(struct timeval)) < 0)
                 return log_error_errno(errno, "Failed to se SO_RCVTIMEO: %m");
 
-        return start_workers(m, /* explicit_request= */ false);
+        r = start_workers(m, /* explicit_request= */ false);
+        if (r < 0)
+                return r;
+
+        return 0;
 }
index b49dbbd52304d6770a62267c7d0e5a921703ca06..729a9a1bb6f7e67b3c54db11a04ea2e9b97f59e3 100644 (file)
@@ -353,9 +353,9 @@ static int vl_method_get_group_record(Varlink *link, JsonVariant *parameters, Va
 
 static int vl_method_get_memberships(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata) {
         static const JsonDispatch dispatch_table[] = {
-                { "userName",  JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParameters, user_name), 0 },
+                { "userName",  JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParameters, user_name),  0 },
                 { "groupName", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParameters, group_name), 0 },
-                { "service",   JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParameters, service),   0 },
+                { "service",   JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParameters, service),    0 },
                 {}
         };
 
@@ -425,16 +425,16 @@ static int vl_method_get_memberships(Varlink *link, JsonVariant *parameters, Var
                                               JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(last_group_name))));
 }
 
-static int process_connection(VarlinkServer *server, int fd) {
+static int process_connection(VarlinkServer *server, int _fd) {
+        _cleanup_close_ int fd = TAKE_FD(_fd); /* always take possession */
         _cleanup_(varlink_close_unrefp) Varlink *vl = NULL;
         int r;
 
         r = varlink_server_add_connection(server, fd, &vl);
-        if (r < 0) {
-                fd = safe_close(fd);
+        if (r < 0)
                 return log_error_errno(r, "Failed to add connection: %m");
-        }
 
+        TAKE_FD(fd);
         vl = varlink_ref(vl);
 
         for (;;) {
@@ -461,6 +461,7 @@ static int process_connection(VarlinkServer *server, int fd) {
 static int run(int argc, char *argv[]) {
         usec_t start_time, listen_idle_usec, last_busy_usec = USEC_INFINITY;
         _cleanup_(varlink_server_unrefp) VarlinkServer *server = NULL;
+        _cleanup_(pidref_done) PidRef parent = PIDREF_NULL;
         unsigned n_iterations = 0;
         int m, listen_fd, r;
 
@@ -505,6 +506,12 @@ static int run(int argc, char *argv[]) {
         if (r < 0)
                 return log_error_errno(r, "Failed to disable userdb NSS compatibility: %m");
 
+        r = pidref_set_parent(&parent);
+        if (r < 0)
+                return log_error_errno(r, "Failed to acquire pidfd of parent process: %m");
+        if (parent.pid == 1) /* We got reparented away from userdbd? */
+                return log_error_errno(SYNTHETIC_ERRNO(ESRCH), "Parent already died, exiting.");
+
         start_time = now(CLOCK_MONOTONIC);
 
         for (;;) {
@@ -554,14 +561,11 @@ static int run(int argc, char *argv[]) {
                                 return log_error_errno(r, "Failed to test for POLLIN on listening socket: %m");
 
                         if (FLAGS_SET(r, POLLIN)) {
-                                pid_t parent;
-
-                                parent = getppid();
-                                if (parent <= 1)
-                                        return log_error_errno(SYNTHETIC_ERRNO(ESRCH), "Parent already died?");
-
-                                if (kill(parent, SIGUSR2) < 0)
-                                        return log_error_errno(errno, "Failed to kill our own parent: %m");
+                                r = pidref_kill(&parent, SIGUSR2);
+                                if (r == -ESRCH)
+                                        return log_error_errno(r, "Parent already died?");
+                                if (r < 0)
+                                        return log_error_errno(r, "Failed to send SIGUSR2 signal to parent: %m");
                         }
                 }
 
index 4d82c65f0af31154caa73d345a600ac51bd808b6..83e43b16ff98532661593e299da407075013c522 100644 (file)
@@ -217,6 +217,8 @@ static int verify_vc_allocation_byfd(int fd) {
 static int verify_vc_kbmode(int fd) {
         int curr_mode;
 
+        assert(fd >= 0);
+
         /*
          * Make sure we only adjust consoles in K_XLATE or K_UNICODE mode.
          * Otherwise we would (likely) interfere with X11's processing of the
@@ -231,6 +233,20 @@ static int verify_vc_kbmode(int fd) {
         return IN_SET(curr_mode, K_XLATE, K_UNICODE) ? 0 : -EBUSY;
 }
 
+static int verify_vc_display_mode(int fd) {
+        int mode;
+
+        assert(fd >= 0);
+
+        /* Similarly the vc is likely busy if it is in KD_GRAPHICS mode. If it's not the case and it's been
+         * left in graphics mode, the kernel will refuse to operate on the font settings anyway. */
+
+        if (ioctl(fd, KDGETMODE, &mode) < 0)
+                return -errno;
+
+        return mode != KD_TEXT ? -EBUSY : 0;
+}
+
 static int toggle_utf8_vc(const char *name, int fd, bool utf8) {
         int r;
         struct termios tc = {};
@@ -470,24 +486,14 @@ static void setup_remaining_vcs(int src_fd, unsigned src_idx, bool utf8) {
                 if (cfo.op != KD_FONT_OP_SET)
                         continue;
 
-                r = ioctl(fd_d, KDFONTOP, &cfo);
+                r = verify_vc_display_mode(fd_d);
                 if (r < 0) {
-                        int last_errno, mode;
-
-                        /* The fonts couldn't have been copied. It might be due to the
-                         * terminal being in graphical mode. In this case the kernel
-                         * returns -EINVAL which is too generic for distinguishing this
-                         * specific case. So we need to retrieve the terminal mode and if
-                         * the graphical mode is in used, let's assume that something else
-                         * is using the terminal and the failure was expected as we
-                         * shouldn't have tried to copy the fonts. */
-
-                        last_errno = errno;
-                        if (ioctl(fd_d, KDGETMODE, &mode) >= 0 && mode != KD_TEXT)
-                                log_debug("KD_FONT_OP_SET skipped: tty%u is not in text mode", i);
-                        else
-                                log_warning_errno(last_errno, "KD_FONT_OP_SET failed, fonts will not be copied to tty%u: %m", i);
+                        log_debug_errno(r, "KD_FONT_OP_SET skipped: tty%u is not in text mode", i);
+                        continue;
+                }
 
+                if (ioctl(fd_d, KDFONTOP, &cfo) < 0) {
+                        log_warning_errno(errno, "KD_FONT_OP_SET failed, fonts will not be copied to tty%u: %m", i);
                         continue;
                 }
 
@@ -531,14 +537,19 @@ static int find_source_vc(char **ret_path, unsigned *ret_idx) {
 
                 fd = open_terminal(path, O_RDWR|O_CLOEXEC|O_NOCTTY);
                 if (fd < 0) {
-                        log_debug_errno(fd, "Failed to open terminal %s, ignoring: %m", path);
-                        RET_GATHER(err, r);
+                        RET_GATHER(err, log_debug_errno(fd, "Failed to open terminal %s, ignoring: %m", path));
                         continue;
                 }
+
                 r = verify_vc_kbmode(fd);
                 if (r < 0) {
-                        log_debug_errno(r, "Failed to check VC %s keyboard mode: %m", path);
-                        RET_GATHER(err, r);
+                        RET_GATHER(err, log_debug_errno(r, "Failed to check VC %s keyboard mode: %m", path));
+                        continue;
+                }
+
+                r = verify_vc_display_mode(fd);
+                if (r < 0) {
+                        RET_GATHER(err, log_debug_errno(r, "Failed to check VC %s display mode: %m", path));
                         continue;
                 }
 
@@ -572,6 +583,13 @@ static int verify_source_vc(char **ret_path, const char *src_vc) {
         if (r < 0)
                 return log_error_errno(r, "Virtual console %s is not in K_XLATE or K_UNICODE: %m", src_vc);
 
+        /* setfont(8) silently ignores when the font can't be applied due to the vc being in
+         * KD_GRAPHICS. Hence we continue to accept this case however we now let the user know that the vc
+         * will be initialized only partially.*/
+        r = verify_vc_display_mode(fd);
+        if (r < 0)
+                log_notice_errno(r, "Virtual console %s is not in KD_TEXT, font settings likely won't be applied.", src_vc);
+
         path = strdup(src_vc);
         if (!path)
                 return log_oom();
index ebae681893ed4a4123d2cfefbab49fd10ab90c6f..4ccda8bae8b8d8d1b5526c30ae8e961bdadd7d9c 100644 (file)
@@ -45,8 +45,7 @@ static int arg_qemu_vsock = -1;
 static uint64_t arg_vsock_cid = UINT64_MAX;
 static bool arg_qemu_gui = false;
 static int arg_secure_boot = -1;
-static MachineCredential *arg_credentials = NULL;
-static size_t arg_n_credentials = 0;
+static MachineCredentialContext arg_credentials = {};
 static SettingsMask arg_settings_mask = 0;
 static char **arg_parameters = NULL;
 
@@ -54,6 +53,7 @@ STATIC_DESTRUCTOR_REGISTER(arg_image, freep);
 STATIC_DESTRUCTOR_REGISTER(arg_machine, freep);
 STATIC_DESTRUCTOR_REGISTER(arg_qemu_smp, freep);
 STATIC_DESTRUCTOR_REGISTER(arg_parameters, strv_freep);
+STATIC_DESTRUCTOR_REGISTER(arg_credentials, machine_credential_context_done);
 
 static int help(void) {
         _cleanup_free_ char *link = NULL;
@@ -224,7 +224,7 @@ static int parse_argv(int argc, char *argv[]) {
                         break;
 
                 case ARG_SET_CREDENTIAL: {
-                        r = machine_credential_set(&arg_credentials, &arg_n_credentials, optarg);
+                        r = machine_credential_set(&arg_credentials, optarg);
                         if (r < 0)
                                 return r;
                         arg_settings_mask |= SETTING_CREDENTIALS;
@@ -232,7 +232,7 @@ static int parse_argv(int argc, char *argv[]) {
                 }
 
                 case ARG_LOAD_CREDENTIAL: {
-                        r = machine_credential_load(&arg_credentials, &arg_n_credentials, optarg);
+                        r = machine_credential_load(&arg_credentials, optarg);
                         if (r < 0)
                                 return r;
 
@@ -536,10 +536,10 @@ static int run_virtual_machine(void) {
                         return log_oom();
         }
 
-        if (ARCHITECTURE_SUPPORTS_SMBIOS) {
-                ssize_t n;
-                FOREACH_ARRAY(cred, arg_credentials, arg_n_credentials) {
+        if (ARCHITECTURE_SUPPORTS_SMBIOS)
+                FOREACH_ARRAY(cred, arg_credentials.credentials, arg_credentials.n_credentials) {
                         _cleanup_free_ char *cred_data_b64 = NULL;
+                        ssize_t n;
 
                         n = base64mem(cred->data, cred->size, &cred_data_b64);
                         if (n < 0)
@@ -553,7 +553,6 @@ static int run_virtual_machine(void) {
                         if (r < 0)
                                 return log_oom();
                 }
-        }
 
         r = strv_extend(&cmdline, "-drive");
         if (r < 0)
@@ -737,30 +736,21 @@ static int determine_names(void) {
 }
 
 static int run(int argc, char *argv[]) {
-        int r, ret = EXIT_SUCCESS;
+        int r;
 
         log_setup();
 
         r = parse_argv(argc, argv);
         if (r <= 0)
-                goto finish;
+                return r;
 
         r = determine_names();
         if (r < 0)
-                goto finish;
+                return r;
 
         assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGCHLD, SIGTERM, SIGINT, SIGRTMIN+18, -1) >= 0);
 
-        r = run_virtual_machine();
-        if (r > 0)
-                ret = r;
-finish:
-        machine_credential_free_all(arg_credentials, arg_n_credentials);
-
-        if (r < 0)
-                return r;
-
-        return ret;
+        return run_virtual_machine();
 }
 
 DEFINE_MAIN_FUNCTION_WITH_POSITIVE_FAILURE(run);
diff --git a/test/TEST-08-INITRD/deny-list-ubuntu-ci b/test/TEST-08-INITRD/deny-list-ubuntu-ci
deleted file mode 100644 (file)
index e69de29..0000000
index 29fd1f78998d100bde164a8594e00cd06fa07a74..caa27f69fdf5c5eb63ce5d1ca39831f32414d6c1 100755 (executable)
@@ -44,8 +44,13 @@ EOF
 }
 
 check_result_qemu_hook() {
+    local workspace="${1:?}"
     local console_log="${TESTDIR:?}/console.log"
 
+    if [[ -e "$workspace/skipped" ]]; then
+        return 0
+    fi
+
     if [[ ! -e "$console_log" ]]; then
         dfatal "Missing console log - this shouldn't happen"
         return 1
diff --git a/test/TEST-24-CRYPTSETUP/template.cfg b/test/TEST-24-CRYPTSETUP/template.cfg
new file mode 100644 (file)
index 0000000..42f8b9d
--- /dev/null
@@ -0,0 +1,8 @@
+dn = "cn = systemd"
+expiration_days = 30
+
+signing_key
+encryption_key
+
+tls_www_client
+email_protection_key
index d0ec63d870874090b5aca41e15396cfa0e49a8c5..a6739eee1cd104824df627c83f2e13bbd9bdecdb 100755 (executable)
@@ -27,7 +27,7 @@ check_result_qemu() {
 
     mount_initdir
 
-    cryptsetup luksOpen "${LOOPDEV:?}p2" "${DM_NAME:?}" <"$TESTDIR/keyfile"
+    cryptsetup luksOpen "${LOOPDEV:?}p4" "${DM_NAME:?}" <"$TESTDIR/keyfile"
     mount "/dev/mapper/$DM_NAME" "$initdir/var"
 
     check_result_common "${initdir:?}" && ret=0 || ret=$?
@@ -39,12 +39,123 @@ check_result_qemu() {
     return $ret
 }
 
+can_test_pkcs11() {
+    if ! command -v "softhsm2-util" >/dev/null; then
+        ddebug "softhsm2-util not available, skipping the PKCS#11 test"
+        return 1
+    fi
+    if ! command -v "pkcs11-tool" >/dev/null; then
+        ddebug "pkcs11-tool not available, skipping the PKCS#11 test"
+        return 1
+    fi
+    if ! command -v "certtool" >/dev/null; then
+        ddebug "certtool not available, skipping the PKCS#11 test"
+        return 1
+    fi
+    if ! "${SYSTEMCTL:?}" --version | grep -q "+P11KIT"; then
+        ddebug "Support for p11-kit is disabled, skipping the PKCS#11 test"
+        return 1
+    fi
+    if ! "${SYSTEMCTL:?}" --version | grep -q "+LIBCRYPTSETUP\b"; then
+        ddebug "Support for libcryptsetup is disabled, skipping the PKCS#11 test"
+        return 1
+    fi
+    if ! "${SYSTEMCTL:?}" --version | grep -q "+LIBCRYPTSETUP_PLUGINS"; then
+        ddebug "Support for libcryptsetup plugins is disabled, skipping the PKCS#11 test"
+        return 1
+    fi
+
+    return 0
+}
+
+setup_pkcs11_token() {
+    dinfo "Setup PKCS#11 token"
+    local P11_MODULE_CONFIGS_DIR P11_MODULE_DIR SOFTHSM_MODULE
+
+    export SOFTHSM2_CONF="/tmp/softhsm2.conf"
+    mkdir -p "$initdir/var/lib/softhsm/tokens/"
+    cat >${SOFTHSM2_CONF} <<EOF
+directories.tokendir = $initdir/var/lib/softhsm/tokens/
+objectstore.backend = file
+slots.removable = false
+slots.mechanisms = ALL
+EOF
+    export GNUTLS_PIN="1234"
+    export GNUTLS_SO_PIN="12345678"
+    softhsm2-util --init-token --free --label "TestToken" --pin ${GNUTLS_PIN} --so-pin ${GNUTLS_SO_PIN}
+
+    if ! P11_MODULE_CONFIGS_DIR=$(pkg-config --variable=p11_module_configs p11-kit-1); then
+        echo "WARNING! Cannot get p11_module_configs from p11-kit-1.pc, assuming /usr/share/p11-kit/modules" >&2
+        P11_MODULE_CONFIGS_DIR="/usr/share/p11-kit/modules"
+    fi
+
+    if ! P11_MODULE_DIR=$(pkg-config --variable=p11_module_path p11-kit-1); then
+        echo "WARNING! Cannot get p11_module_path from p11-kit-1.pc, assuming /usr/lib/pkcs11" >&2
+        P11_MODULE_DIR="/usr/lib/pkcs11"
+    fi
+
+    SOFTHSM_MODULE=$(grep -F 'module:' "$P11_MODULE_CONFIGS_DIR/softhsm2.module"| cut -d ':' -f 2| xargs)
+    if [[ "$SOFTHSM_MODULE" =~ ^[^/] ]]; then
+        SOFTHSM_MODULE="$P11_MODULE_DIR/$SOFTHSM_MODULE"
+    fi
+
+    # RSA #####################################################
+    pkcs11-tool --module "$SOFTHSM_MODULE" --token-label "TestToken" --pin "env:GNUTLS_PIN" --so-pin "env:GNUTLS_SO_PIN" --keypairgen --key-type "RSA:2048" --label "RSATestKey" --usage-decrypt
+
+    certtool --generate-self-signed \
+      --load-privkey="pkcs11:token=TestToken;object=RSATestKey;type=private" \
+      --load-pubkey="pkcs11:token=TestToken;object=RSATestKey;type=public" \
+      --template "$TEST_BASE_DIR/$TESTNAME/template.cfg" \
+      --outder --outfile "/tmp/rsa_test.crt"
+
+    pkcs11-tool --module "$SOFTHSM_MODULE" --token-label "TestToken" --pin "env:GNUTLS_PIN" --so-pin "env:GNUTLS_SO_PIN" --write-object "/tmp/rsa_test.crt" --type cert --label "RSATestKey"
+    rm "/tmp/rsa_test.crt"
+
+    # prime256v1 ##############################################
+    pkcs11-tool --module "$SOFTHSM_MODULE" --token-label "TestToken" --pin "env:GNUTLS_PIN" --so-pin "env:GNUTLS_SO_PIN" --keypairgen --key-type "EC:prime256v1" --label "ECTestKey" --usage-derive
+
+    certtool --generate-self-signed \
+      --load-privkey="pkcs11:token=TestToken;object=ECTestKey;type=private" \
+      --load-pubkey="pkcs11:token=TestToken;object=ECTestKey;type=public" \
+      --template "$TEST_BASE_DIR/$TESTNAME/template.cfg" \
+      --outder --outfile "/tmp/ec_test.crt"
+
+    pkcs11-tool --module "$SOFTHSM_MODULE" --token-label "TestToken" --pin "env:GNUTLS_PIN" --so-pin "env:GNUTLS_SO_PIN" --write-object "/tmp/ec_test.crt" --type cert --label "ECTestKey"
+    rm "/tmp/ec_test.crt"
+
+    ###########################################################
+    rm ${SOFTHSM2_CONF}
+    unset SOFTHSM2_CONF
+
+    inst_libs "$SOFTHSM_MODULE"
+    inst_library "$SOFTHSM_MODULE"
+    inst_simple "$P11_MODULE_CONFIGS_DIR/softhsm2.module"
+
+    cat >"$initdir/etc/softhsm2.conf" <<EOF
+directories.tokendir = /var/lib/softhsm/tokens/
+objectstore.backend = file
+slots.removable = false
+slots.mechanisms = ALL
+log.level = INFO
+EOF
+
+    mkdir -p "$initdir/etc/systemd/system/systemd-cryptsetup@.service.d"
+    cat >"$initdir/etc/systemd/system/systemd-cryptsetup@.service.d/PKCS11.conf" <<EOF
+[Service]
+Environment="SOFTHSM2_CONF=/etc/softhsm2.conf"
+Environment="PIN=$GNUTLS_PIN"
+EOF
+
+    unset GNUTLS_PIN
+    unset GNUTLS_SO_PIN
+}
+
 test_create_image() {
     create_empty_image_rootdir
 
     echo -n test >"${TESTDIR:?}/keyfile"
-    cryptsetup -q luksFormat --uuid="$PART_UUID" --pbkdf pbkdf2 --pbkdf-force-iterations 1000 "${LOOPDEV:?}p2" "$TESTDIR/keyfile"
-    cryptsetup luksOpen "${LOOPDEV}p2" "${DM_NAME:?}" <"$TESTDIR/keyfile"
+    cryptsetup -q luksFormat --uuid="$PART_UUID" --pbkdf pbkdf2 --pbkdf-force-iterations 1000 "${LOOPDEV:?}p4" "$TESTDIR/keyfile"
+    cryptsetup luksOpen "${LOOPDEV}p4" "${DM_NAME:?}" <"$TESTDIR/keyfile"
     mkfs.ext4 -L var "/dev/mapper/$DM_NAME"
     mkdir -p "${initdir:?}/var"
     mount "/dev/mapper/$DM_NAME" "$initdir/var"
@@ -57,6 +168,10 @@ test_create_image() {
     install_dmevent
     generate_module_dependencies
 
+    if can_test_pkcs11; then
+        setup_pkcs11_token
+    fi
+
     # Create a keydev
     dd if=/dev/zero of="${STATEDIR:?}/keydev.img" bs=1M count=16
     mkfs.ext4 -L varcrypt_keydev "$STATEDIR/keydev.img"
@@ -83,7 +198,7 @@ EOF
         if command -v dracut >/dev/null; then
             dracut --force --verbose --add crypt "$INITRD"
         elif command -v mkinitcpio >/dev/null; then
-            mkinitcpio --addhooks sd-encrypt --generate "$INITRD"
+            mkinitcpio -S autodetect --addhooks sd-encrypt --generate "$INITRD"
         elif command -v mkinitramfs >/dev/null; then
             # The cryptroot hook is provided by the cryptsetup-initramfs package
             if ! dpkg-query -s cryptsetup-initramfs; then
index d41a4f00f9addab055589d16fb1c53498d4521cf..b9e7bdf18ad14376d3c40ff11cedbc0114b38bf3 100755 (executable)
@@ -24,7 +24,7 @@ _host_has_feature() {(
 
     case "${1:?}" in
         btrfs)
-            modprobe -nv btrfs && command -v mkfs.btrfs && command -v btrfs || return $?
+            host_has_btrfs
             ;;
         iscsi)
             # Client/initiator (Open-iSCSI)
@@ -36,7 +36,7 @@ _host_has_feature() {(
             command -v lvm || return $?
             ;;
         mdadm)
-            command -v mdadm || return $?
+            host_has_mdadm
             ;;
         multipath)
             command -v multipath && command -v multipathd || return $?
index f033ec469f38dec0d74011f41635015b479edc66..7940600612d3694b0341f70dbc9213eed04b961a 100755 (executable)
@@ -16,6 +16,12 @@ test_append_files() {
         # the QEMU test, as nspawn refuses the invalid machine ID with -EUCLEAN
         printf "556f48e837bc4424a710fa2e2c9d3e3c\ne3d\n" >"$workspace/etc/machine-id"
     fi
+
+    if host_has_btrfs && host_has_mdadm; then
+        install_btrfs
+        install_mdadm
+        generate_module_dependencies
+    fi
 }
 
 do_test "$@"
index 432a49fa1b4d3bd1ded55ddfc637ffef186bc50f..89a5c7ed4e12022c2a205a2c6b7ba951ce077f1f 100755 (executable)
@@ -10,6 +10,7 @@
 set -e
 
 export SYSTEMD_LOG_LEVEL=info
+export SYSTEMD_HWDB_UPDATE_BYPASS=0
 ROOTDIR="$(dirname "$(dirname "$(readlink -f "$0")")")"
 SYSTEMD_HWDB="${1:?}"
 
index 42b003878986ef0a02fa5ac8243d83f786b6b1e5..5af7b8cd91debadf07bfb162f9ae360507002069 100644 (file)
@@ -1167,29 +1167,42 @@ install_multipath() {
 }
 
 install_lvm() {
+    local lvm_rules rule_prefix
+
     image_install lvm
     image_install "${ROOTLIBDIR:?}"/system/lvm2-lvmpolld.{service,socket}
     image_install "${ROOTLIBDIR:?}"/system/{blk-availability,lvm2-monitor}.service
     image_install -o "/lib/tmpfiles.d/lvm2.conf"
+
     if get_bool "$LOOKS_LIKE_DEBIAN"; then
-        inst_rules 56-lvm.rules 69-lvm-metad.rules
+        lvm_rules="56-lvm.rules"
+        rule_prefix=""
     else
-        # Support the new udev autoactivation introduced in lvm 2.03.14
-        # https://sourceware.org/git/?p=lvm2.git;a=commit;h=67722b312390cdab29c076c912e14bd739c5c0f6
-        # Static autoactivation (via lvm2-activation-generator) was dropped
-        # in lvm 2.03.15
-        # https://sourceware.org/git/?p=lvm2.git;a=commit;h=ee8fb0310c53ed003a43b324c99cdfd891dd1a7c
-        if [[ -f /lib/udev/rules.d/69-dm-lvm.rules ]]; then
-            inst_rules 11-dm-lvm.rules 69-dm-lvm.rules
-        else
-            image_install "${ROOTLIBDIR:?}"/system-generators/lvm2-activation-generator
-            image_install "${ROOTLIBDIR:?}"/system/lvm2-pvscan@.service
-            inst_rules 11-dm-lvm.rules 69-dm-lvm-metad.rules
-        fi
+        lvm_rules="11-dm-lvm.rules"
+        rule_prefix="dm-"
+    fi
+
+    # Support the new udev autoactivation introduced in lvm 2.03.14
+    # https://sourceware.org/git/?p=lvm2.git;a=commit;h=67722b312390cdab29c076c912e14bd739c5c0f6
+    # Static autoactivation (via lvm2-activation-generator) was dropped
+    # in lvm 2.03.15
+    # https://sourceware.org/git/?p=lvm2.git;a=commit;h=ee8fb0310c53ed003a43b324c99cdfd891dd1a7c
+    if [[ -f "/lib/udev/rules.d/69-${rule_prefix}lvm.rules" ]]; then
+        inst_rules "$lvm_rules" "69-${rule_prefix}lvm.rules"
+    else
+        image_install "${ROOTLIBDIR:?}"/system-generators/lvm2-activation-generator
+        image_install "${ROOTLIBDIR:?}"/system/lvm2-pvscan@.service
+        inst_rules "$lvm_rules" "69-${rule_prefix}lvm-metad.rules"
     fi
+
     mkdir -p "${initdir:?}/etc/lvm"
 }
 
+host_has_btrfs() (
+    set -e
+    modprobe -nv btrfs && command -v mkfs.btrfs && command -v btrfs || return $?
+)
+
 install_btrfs() {
     instmods btrfs
     # Not all utilities provided by btrfs-progs are listed here; extend the list
@@ -1257,6 +1270,11 @@ install_iscsi() {
     fi
 }
 
+host_has_mdadm() (
+    set -e
+    command -v mdadm || return $?
+)
+
 install_mdadm() {
     local unit
     local mdadm_units=(
@@ -1270,6 +1288,7 @@ install_mdadm() {
         system-shutdown/mdadm.shutdown
     )
 
+    instmods "=md"
     image_install mdadm mdmon
     inst_rules 01-md-raid-creating.rules 63-md-raid-arrays.rules 64-md-raid-assembly.rules 69-md-clustered-confirm-device.rules
     # Fedora/CentOS/RHEL ships this rule file
@@ -1278,6 +1297,10 @@ install_mdadm() {
     for unit in "${mdadm_units[@]}"; do
         image_install "${ROOTLIBDIR:?}/$unit"
     done
+
+    # Disable the mdmonitor service, since it fails if there's no valid email address
+    # configured in /etc/mdadm.conf, which just unnecessarily pollutes the logs
+    "${SYSTEMCTL:?}" mask --root "${initdir:?}" mdmonitor.service || :
 }
 
 install_compiled_systemd() {
@@ -1302,19 +1325,35 @@ install_compiled_systemd() {
     fi
 }
 
+install_package_file() {
+    local file="${1:?}"
+
+    # Skip missing files (like /etc/machine-info)
+    [[ ! -e "$file" ]] && return 0
+    # Skip python unit tests, since the image_install machinery will try to pull
+    # in the whole python stack in a very questionable state, making the tests fail.
+    # And given we're trying to transition to mkosi-based images anyway I'm not even
+    # going to bother
+    [[ "$file" =~ /tests/unit-tests/.*.py$ ]] && return 0
+    # If the current file is a directory, create it with the original
+    # mode; if it's a symlink to a directory, copy it as-is
+    if [[ -d "$file" ]]; then
+        inst_dir "$file"
+    else
+        inst "$file"
+    fi
+}
+
 install_debian_systemd() {
     dinfo "Install debian systemd"
 
-    local files
+    local deb file
 
     while read -r deb; do
-        files="$(dpkg-query -L "$deb" 2>/dev/null)" || continue
         ddebug "Install debian files from package $deb"
-        for file in $files; do
-            [ -e "$file" ] || continue
-            [ ! -L "$file" ] && [ -d "$file" ] && continue
-            inst "$file"
-        done
+        while read -r file; do
+            install_package_file "$file"
+        done < <(dpkg-query -L "$deb" 2>/dev/null)
     done < <(grep -E '^Package:' "${SOURCE_DIR}/debian/control" | cut -d ':' -f 2)
 }
 
@@ -1329,17 +1368,7 @@ install_rpm() {
 
     dinfo "Installing contents of RPM $rpm"
     while read -r file; do
-        # Skip missing files (like /etc/machine-info)
-        [[ ! -e "$file" ]] && continue
-        # Skip directories unless they are a symlink (both -L and -d pass in this case)
-        [[ -d "$file" && ! -L "$file" ]] && continue
-        # Skip python unit tests, since the image_install machinery will try to pull
-        # in the whole python stack in a very questionable state, making the tests fail.
-        # And given we're trying to transition to mkosi-based images anyway I'm not even
-        # going to bother
-        [[ "$file" =~ /tests/unit-tests/.*.py$ ]] && continue
-
-        image_install "$file"
+        install_package_file "$file"
     done < <(rpm -ql "$rpm")
 }
 
@@ -1583,6 +1612,9 @@ create_empty_image() {
     # Partition sizes are in MiBs
     local root_size=768
     local data_size=100
+    local esp_size=128
+    local boot_size=128
+    local total=
     if ! get_bool "$NO_BUILD"; then
         if meson configure "${BUILD_DIR:?}" | grep 'static-lib\|standalone-binaries' | awk '{ print $2 }' | grep -q 'true'; then
             root_size=$((root_size + 200))
@@ -1605,28 +1637,44 @@ create_empty_image() {
         data_size=$((data_size + IMAGE_ADDITIONAL_DATA_SIZE))
     fi
 
-    echo "Setting up ${IMAGE_PUBLIC:?} (${root_size} MB)"
+    total=$((root_size + data_size + esp_size + boot_size))
+
+    echo "Setting up ${IMAGE_PUBLIC:?} (${total} MB)"
     rm -f "${IMAGE_PRIVATE:?}" "$IMAGE_PUBLIC"
 
     # Create the blank file to use as a root filesystem
-    truncate -s "${root_size}M" "$IMAGE_PUBLIC"
+    truncate -s "${total}M" "$IMAGE_PUBLIC"
 
     LOOPDEV="$(losetup --show -P -f "$IMAGE_PUBLIC")"
     [[ -b "$LOOPDEV" ]] || return 1
     # Create two partitions - a root one and a data one (utilized by some tests)
     sfdisk "$LOOPDEV" <<EOF
 label: gpt
-type=0FC63DAF-8483-4772-8E79-3D69D8477DE4 name=root size=$((root_size - data_size))M bootable
+type=C12A7328-F81F-11D2-BA4B-00A0C93EC93B name=esp  size=${esp_size}M
+type=0FC63DAF-8483-4772-8E79-3D69D8477DE4 name=root size=${root_size}M bootable
+type=BC13C2FF-59E6-4262-A352-B275FD6F7172 name=boot size=${boot_size}M
 type=0FC63DAF-8483-4772-8E79-3D69D8477DE4 name=data
 EOF
 
     udevadm settle
 
+    if ! mkfs -t vfat "${LOOPDEV}p1"; then
+        dfatal "Failed to mkfs -t vfat ${LOOPDEV}p1"
+        exit 1
+    fi
+
     local label=(-L systemd_boot)
     # mkfs.reiserfs doesn't know -L. so, use --label instead
     [[ "$FSTYPE" == "reiserfs" ]] && label=(--label systemd_boot)
-    if ! mkfs -t "${FSTYPE}" "${label[@]}" "${LOOPDEV}p1" -q; then
-        dfatal "Failed to mkfs -t ${FSTYPE}"
+    if ! mkfs -t "${FSTYPE}" "${label[@]}" "${LOOPDEV}p2" -q; then
+        dfatal "Failed to mkfs -t ${FSTYPE} ${label[*]} ${LOOPDEV}p2 -q"
+        exit 1
+    fi
+
+    local label=(-L xbootldr)
+    [[ "$FSTYPE" == "reiserfs" ]] && label=(--label xbootldr)
+    if ! mkfs -t "${FSTYPE}" "${label[@]}" "${LOOPDEV}p3" -q; then
+        dfatal "Failed to mkfs -t ${FSTYPE} ${label[*]} ${LOOPDEV}p3 -q"
         exit 1
     fi
 }
@@ -1642,7 +1690,7 @@ mount_initdir() {
 
     if ! mountpoint -q "${initdir:?}"; then
         mkdir -p "$initdir"
-        mount "${LOOPDEV}p1" "$initdir"
+        mount "${LOOPDEV}p2" "$initdir"
         TEST_SETUP_CLEANUP_ROOTDIR=1
     fi
 }
@@ -3295,9 +3343,10 @@ test_create_image() {
 }
 
 test_setup() {
-    if get_bool "${TEST_REQUIRE_INSTALL_TESTS:?}" && \
-            command -v meson >/dev/null && \
-            [[ "$(meson configure "${BUILD_DIR:?}" | grep install-tests | awk '{ print $2 }')" != "true" ]]; then
+    if ! get_bool "$NO_BUILD" && \
+         get_bool "${TEST_REQUIRE_INSTALL_TESTS:?}" && \
+         command -v meson >/dev/null && \
+         [[ "$(meson configure "${BUILD_DIR:?}" | grep install-tests | awk '{ print $2 }')" != "true" ]]; then
         dfatal "$BUILD_DIR needs to be built with -Dinstall-tests=true"
         exit 1
     fi
index 6224a4d04f96a4839514dbd6b918f4602ae146d0..9a4732c981dc9c78c9cf75edb1ab95d57890b2f5 100755 (executable)
@@ -296,7 +296,7 @@ INVALID_COMMAND_LINES=(
     "ip=10.0.0.1:::255.255.255::foo99:off"
     "ip=10.0.0.1:::255.255.255.0:invalid_hostname:foo99:off"
     "ip=10.0.0.1:::255.255.255.0::verylonginterfacename:off"
-    "ip=:::::dhcp99:dhcp6:0"
+    "ip=:::::dhcp99:dhcp6:4294967296"
     "ip=:::::dhcp99:dhcp6:-1"
     "ip=:::::dhcp99:dhcp6:666:52:54:00"
     "ip=fdef:c400:bd01:1096::2::[fdef:c400:bd01:1096::1]:64::ipv6:off:[fdef:c400:bd01:1096::aaaa]"
diff --git a/test/test-network/conf/11-test-unit-file.link b/test/test-network/conf/11-test-unit-file.link
new file mode 100644 (file)
index 0000000..429ac31
--- /dev/null
@@ -0,0 +1,20 @@
+# SPDX-License-Identifier: MIT-0
+#
+# This config file is installed as part of systemd.
+# It may be freely copied and edited (following the MIT No Attribution license).
+#
+# To make local modifications, one of the following methods may be used:
+# 1. add a drop-in file that extends this file by creating the
+#    /etc/systemd/network/99-default.link.d/ directory and creating a
+#    new .conf file there.
+# 2. copy this file into /etc/systemd/network or one of the other paths checked
+#    by systemd-udevd and edit it there.
+# This file should not be edited in place, because it'll be overwritten on upgrades.
+
+[Match]
+OriginalName=*
+
+[Link]
+NamePolicy=keep kernel database onboard slot path
+AlternativeNamesPolicy=database onboard slot path
+MACAddressPolicy=persistent
diff --git a/test/test-network/conf/11-test-unit-file.link.d/dropin.conf b/test/test-network/conf/11-test-unit-file.link.d/dropin.conf
new file mode 100644 (file)
index 0000000..dfddc0a
--- /dev/null
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Link]
diff --git a/test/test-network/conf/11-test-unit-file.netdev b/test/test-network/conf/11-test-unit-file.netdev
new file mode 100644 (file)
index 0000000..86af17f
--- /dev/null
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[NetDev]
+Name=test1
+Kind=dummy
diff --git a/test/test-network/conf/11-test-unit-file.netdev.d/dropin.conf b/test/test-network/conf/11-test-unit-file.netdev.d/dropin.conf
new file mode 100644 (file)
index 0000000..6fde598
--- /dev/null
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[NetDev]
diff --git a/test/test-network/conf/11-test-unit-file.network b/test/test-network/conf/11-test-unit-file.network
new file mode 100644 (file)
index 0000000..0a4511b
--- /dev/null
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Match]
+Name=test1
+
+[Network]
+IPv6AcceptRA=no
diff --git a/test/test-network/conf/11-test-unit-file.network.d/dropin.conf b/test/test-network/conf/11-test-unit-file.network.d/dropin.conf
new file mode 100644 (file)
index 0000000..f2f6099
--- /dev/null
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Network]
index 29bd98e5c9d0883156ada54a3c11da13e1d5bd22..2762dd23747353cd4ec90e726c080dd80ecb7084 100644 (file)
@@ -2,3 +2,4 @@
 [NetDev]
 Name=vcan99
 Kind=vcan
+MTUBytes=16
diff --git a/test/test-network/conf/25-vcan98.netdev b/test/test-network/conf/25-vcan98.netdev
new file mode 100644 (file)
index 0000000..5333c82
--- /dev/null
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[NetDev]
+Name=vcan98
+Kind=vcan
diff --git a/test/test-network/conf/25-vcan98.network b/test/test-network/conf/25-vcan98.network
new file mode 100644 (file)
index 0000000..97f824d
--- /dev/null
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Match]
+Name=vcan98
+
+[Link]
+MTUBytes=16
diff --git a/test/test-network/conf/networkd-manage-foreign-nexthops-no.conf b/test/test-network/conf/networkd-manage-foreign-nexthops-no.conf
new file mode 100644 (file)
index 0000000..9bf5d7e
--- /dev/null
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Network]
+ManageForeignNextHops=no
index ea4f8d4b1571cf5f437c8f505cbbe59284871c29..adb60cd48939b93a32ca9c9beb3d014538055009 100755 (executable)
@@ -258,6 +258,8 @@ def compare_kernel_version(min_kernel_version):
     # Get only the actual kernel version without any build/distro/arch stuff
     # e.g. '5.18.5-200.fc36.x86_64' -> '5.18.5'
     kver = platform.release().split('-')[0]
+    # Get also rid of '+'
+    kver = kver.split('+')[0]
 
     return version.parse(kver) >= version.parse(min_kernel_version)
 
@@ -1236,15 +1238,20 @@ class NetworkctlTests(unittest.TestCase, Utilities):
         print(output)
         self.assertRegex(output, 'Type: loopback')
 
-    def test_udev_link_file(self):
-        copy_network_unit('11-dummy.netdev', '11-dummy.network', '25-default.link')
+    def test_unit_file(self):
+        copy_network_unit('11-test-unit-file.netdev', '11-test-unit-file.network', '11-test-unit-file.link')
         start_networkd()
         self.wait_online(['test1:degraded'])
 
         output = check_output(*networkctl_cmd, '-n', '0', 'status', 'test1', env=env)
         print(output)
-        self.assertRegex(output, r'Link File: /run/systemd/network/25-default.link')
-        self.assertRegex(output, r'Network File: /run/systemd/network/11-dummy.network')
+        self.assertIn('Link File: /run/systemd/network/11-test-unit-file.link', output)
+        self.assertIn('/run/systemd/network/11-test-unit-file.link.d/dropin.conf', output)
+        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)
 
         # 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.
@@ -1252,8 +1259,8 @@ class NetworkctlTests(unittest.TestCase, Utilities):
         check_output(*udevadm_cmd, 'trigger', '--settle', '--action=add', '/sys/class/net/lo')
         output = check_output(*networkctl_cmd, '-n', '0', 'status', 'lo', env=env)
         print(output)
-        self.assertRegex(output, r'Link File: n/a')
-        self.assertRegex(output, r'Network File: n/a')
+        self.assertIn('Link File: n/a', output)
+        self.assertIn('Network File: n/a', output)
 
     def test_delete_links(self):
         copy_network_unit('11-dummy.netdev', '11-dummy.network',
@@ -1727,10 +1734,20 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities):
 
     @expectedFailureIfModuleIsNotAvailable('vcan')
     def test_vcan(self):
-        copy_network_unit('25-vcan.netdev', '26-netdev-link-local-addressing-yes.network')
+        copy_network_unit('25-vcan.netdev', '26-netdev-link-local-addressing-yes.network',
+                          '25-vcan98.netdev', '25-vcan98.network')
         start_networkd()
 
-        self.wait_online(['vcan99:carrier'])
+        self.wait_online(['vcan99:carrier', 'vcan98:carrier'])
+
+        # https://github.com/systemd/systemd/issues/30140
+        output = check_output('ip -d link show vcan99')
+        print(output)
+        self.assertIn('mtu 16 ', output)
+
+        output = check_output('ip -d link show vcan98')
+        print(output)
+        self.assertIn('mtu 16 ', output)
 
     @expectedFailureIfModuleIsNotAvailable('vxcan')
     def test_vxcan(self):
@@ -2552,10 +2569,10 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
 
     def test_address_static(self):
         copy_network_unit('25-address-static.network', '12-dummy.netdev', copy_dropins=False)
-        start_networkd()
         self.setup_nftset('addr4', 'ipv4_addr')
         self.setup_nftset('network4', 'ipv4_addr', 'flags interval;')
         self.setup_nftset('ifindex', 'iface_index')
+        start_networkd()
 
         self.wait_online(['dummy98:routable'])
 
@@ -3273,8 +3290,6 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
         print(output)
         self.assertEqual(output, '')
 
-        self.tearDown()
-
     def test_route_static(self):
         first = True
         for manage_foreign_routes in [True, False]:
@@ -3554,6 +3569,7 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
         print(output)
         self.assertRegex(output, 'inet6 .* scope link')
 
+    @unittest.skip("Re-enable once https://github.com/systemd/systemd/issues/30056 is resolved")
     def test_sysctl(self):
         copy_networkd_conf_dropin('25-global-ipv6-privacy-extensions.conf')
         copy_network_unit('25-sysctl.network', '12-dummy.netdev', copy_dropins=False)
@@ -3832,74 +3848,86 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
         self.assertRegex(output, 'inet 10.1.2.3/16 scope global dummy98')
         self.assertNotRegex(output, 'inet 10.2.3.4/16 scope global dynamic dummy98')
 
-    @expectedFailureIfNexthopIsNotAvailable()
-    def test_nexthop(self):
-        def check_nexthop(self):
-            self.wait_online(['veth99:routable', 'veth-peer:routable', 'dummy98:routable'])
+    def check_nexthop(self, manage_foreign_nexthops):
+        self.wait_online(['veth99:routable', 'veth-peer:routable', 'dummy98:routable'])
 
-            output = check_output('ip nexthop list dev veth99')
-            print(output)
-            self.assertIn('id 1 via 192.168.5.1 dev veth99', output)
-            self.assertIn('id 2 via 2001:1234:5:8f63::2 dev veth99', output)
-            self.assertIn('id 3 dev veth99', output)
-            self.assertIn('id 4 dev veth99', output)
-            self.assertRegex(output, 'id 5 via 192.168.10.1 dev veth99 .*onlink')
-            self.assertIn('id 8 via fe80:0:222:4dff:ff:ff:ff:ff dev veth99', output)
+        output = check_output('ip nexthop list dev veth99')
+        print(output)
+        self.assertIn('id 1 via 192.168.5.1 dev veth99', output)
+        self.assertIn('id 2 via 2001:1234:5:8f63::2 dev veth99', output)
+        self.assertIn('id 3 dev veth99', output)
+        self.assertIn('id 4 dev veth99', output)
+        self.assertRegex(output, 'id 5 via 192.168.10.1 dev veth99 .*onlink')
+        self.assertIn('id 8 via fe80:0:222:4dff:ff:ff:ff:ff dev veth99', output)
+        if manage_foreign_nexthops:
             self.assertRegex(output, r'id [0-9]* via 192.168.5.2 dev veth99')
 
-            output = check_output('ip nexthop list dev dummy98')
-            print(output)
-            self.assertIn('id 20 via 192.168.20.1 dev dummy98', output)
+        output = check_output('ip nexthop list dev dummy98')
+        print(output)
+        self.assertIn('id 20 via 192.168.20.1 dev dummy98', output)
+        if manage_foreign_nexthops:
+            self.assertNotIn('id 42 via 192.168.20.2 dev dummy98', output)
+        else:
+            self.assertIn('id 42 via 192.168.20.2 dev dummy98', output)
 
-            # kernel manages blackhole nexthops on lo
-            output = check_output('ip nexthop list dev lo')
-            print(output)
-            self.assertIn('id 6 blackhole', output)
-            self.assertIn('id 7 blackhole', output)
+        # kernel manages blackhole nexthops on lo
+        output = check_output('ip nexthop list dev lo')
+        print(output)
+        self.assertIn('id 6 blackhole', output)
+        self.assertIn('id 7 blackhole', output)
 
-            # group nexthops are shown with -0 option
-            output = check_output('ip -0 nexthop list id 21')
-            print(output)
-            self.assertRegex(output, r'id 21 group (1,3/20|20/1,3)')
+        # group nexthops are shown with -0 option
+        output = check_output('ip -0 nexthop list id 21')
+        print(output)
+        self.assertRegex(output, r'id 21 group (1,3/20|20/1,3)')
 
-            output = check_output('ip route show dev veth99 10.10.10.10')
-            print(output)
-            self.assertEqual('10.10.10.10 nhid 1 via 192.168.5.1 proto static', output)
+        output = check_output('ip route show dev veth99 10.10.10.10')
+        print(output)
+        self.assertEqual('10.10.10.10 nhid 1 via 192.168.5.1 proto static', output)
 
-            output = check_output('ip route show dev veth99 10.10.10.11')
-            print(output)
-            self.assertEqual('10.10.10.11 nhid 2 via inet6 2001:1234:5:8f63::2 proto static', output)
+        output = check_output('ip route show dev veth99 10.10.10.11')
+        print(output)
+        self.assertEqual('10.10.10.11 nhid 2 via inet6 2001:1234:5:8f63::2 proto static', output)
 
-            output = check_output('ip route show dev veth99 10.10.10.12')
-            print(output)
-            self.assertEqual('10.10.10.12 nhid 5 via 192.168.10.1 proto static onlink', output)
+        output = check_output('ip route show dev veth99 10.10.10.12')
+        print(output)
+        self.assertEqual('10.10.10.12 nhid 5 via 192.168.10.1 proto static onlink', output)
 
-            output = check_output('ip -6 route show dev veth99 2001:1234:5:8f62::1')
-            print(output)
-            self.assertEqual('2001:1234:5:8f62::1 nhid 2 via 2001:1234:5:8f63::2 proto static metric 1024 pref medium', output)
+        output = check_output('ip -6 route show dev veth99 2001:1234:5:8f62::1')
+        print(output)
+        self.assertEqual('2001:1234:5:8f62::1 nhid 2 via 2001:1234:5:8f63::2 proto static metric 1024 pref medium', output)
 
-            output = check_output('ip route show 10.10.10.13')
-            print(output)
-            self.assertEqual('blackhole 10.10.10.13 nhid 6 dev lo proto static', output)
+        output = check_output('ip route show 10.10.10.13')
+        print(output)
+        self.assertEqual('blackhole 10.10.10.13 nhid 6 dev lo proto static', output)
 
-            output = check_output('ip -6 route show 2001:1234:5:8f62::2')
-            print(output)
-            self.assertEqual('blackhole 2001:1234:5:8f62::2 nhid 7 dev lo proto static metric 1024 pref medium', output)
+        output = check_output('ip -6 route show 2001:1234:5:8f62::2')
+        print(output)
+        self.assertEqual('blackhole 2001:1234:5:8f62::2 nhid 7 dev lo proto static metric 1024 pref medium', output)
 
-            output = check_output('ip route show 10.10.10.14')
-            print(output)
-            self.assertIn('10.10.10.14 nhid 21 proto static', output)
-            self.assertIn('nexthop via 192.168.20.1 dev dummy98 weight 1', output)
-            self.assertIn('nexthop via 192.168.5.1 dev veth99 weight 3', output)
+        output = check_output('ip route show 10.10.10.14')
+        print(output)
+        self.assertIn('10.10.10.14 nhid 21 proto static', output)
+        self.assertIn('nexthop via 192.168.20.1 dev dummy98 weight 1', output)
+        self.assertIn('nexthop via 192.168.5.1 dev veth99 weight 3', output)
 
-            output = check_output(*networkctl_cmd, '--json=short', 'status', env=env)
-            check_json(output)
+        output = check_output(*networkctl_cmd, '--json=short', 'status', env=env)
+        check_json(output)
+
+    def _test_nexthop(self, manage_foreign_nexthops):
+        if not manage_foreign_nexthops:
+            copy_networkd_conf_dropin('networkd-manage-foreign-nexthops-no.conf')
+
+        check_output('ip link add dummy98 type dummy')
+        check_output('ip link set dummy98 up')
+        check_output('ip address add 192.168.20.20/24 dev dummy98')
+        check_output('ip nexthop add id 42 via 192.168.20.2 dev dummy98')
 
         copy_network_unit('25-nexthop.network', '25-veth.netdev', '25-veth-peer.network',
                           '12-dummy.netdev', '25-nexthop-dummy.network')
         start_networkd()
 
-        check_nexthop(self)
+        self.check_nexthop(manage_foreign_nexthops)
 
         remove_network_unit('25-nexthop.network')
         copy_network_unit('25-nexthop-nothing.network')
@@ -3918,7 +3946,7 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
         networkctl_reconfigure('dummy98')
         networkctl_reload()
 
-        check_nexthop(self)
+        self.check_nexthop(manage_foreign_nexthops)
 
         remove_link('veth99')
         time.sleep(2)
@@ -3927,6 +3955,19 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
         print(output)
         self.assertEqual(output, '')
 
+    @expectedFailureIfNexthopIsNotAvailable()
+    def test_nexthop(self):
+        first = True
+        for manage_foreign_nexthops in [True, False]:
+            if first:
+                first = False
+            else:
+                self.tearDown()
+
+            print(f'### test_nexthop(manage_foreign_nexthops={manage_foreign_nexthops})')
+            with self.subTest(manage_foreign_nexthops=manage_foreign_nexthops):
+                self._test_nexthop(manage_foreign_nexthops)
+
 class NetworkdTCTests(unittest.TestCase, Utilities):
 
     def setUp(self):
index 9ed02248ad1def805b5a51f6385e1f4873a850c1..bdb1c8e2d362480db2a8a93ec1fd719fc6f46c06 100644 (file)
@@ -97,7 +97,7 @@ test ! -e "$root/etc/systemd/system/test1-badalias.target"
 test ! -e "$root/etc/systemd/system/test1-badalias.socket"
 test -h "$root/etc/systemd/system/test1-goodalias2.service"
 
-: '-------aliases in reeanable----------------------------------'
+: '-------aliases in reenable----------------------------------'
 ( ! "$systemctl" --root="$root" reenable test1 )
 test -h "$root/etc/systemd/system/default.target.wants/test1.service"
 test ! -e "$root/etc/systemd/system/test1-goodalias.service"
index 8f5ff5f35c8b864fbf7375af6cddc24b3e9ce13e..1d4ad7ec6a21a48761b8ab93c90d05af068d6edc 100755 (executable)
@@ -22,6 +22,9 @@ at_exit() {
         journalctl --flush
     fi
 
+    rm -f /run/systemd/journald.conf.d/99-forward-to-console.conf
+    systemctl restart systemd-journald
+
     return 0
 }
 
@@ -32,9 +35,9 @@ vcs_dump_and_check() {
     # so try it a couple of times
     for _ in {0..9}; do
         setterm --term linux --dump --file /tmp/console.dump
-        if grep -aq "Press any key to exit" /tmp/console.dump
-            grep -aq "$expected_message" /tmp/console.dump
-            grep -aq "The current boot has failed" /tmp/console.dump; then
+        if grep -aq "Press any key to exit" /tmp/console.dump &&
+           grep -aq "$expected_message" /tmp/console.dump &&
+           grep -aq "The current boot has failed" /tmp/console.dump; then
 
             return 0
         fi
@@ -49,7 +52,12 @@ vcs_dump_and_check() {
 # current boot, let's temporarily overmount /var/log/journal with a tmpfs,
 # as we're going to wipe it multiple times, but we need to keep the original
 # journal intact for the other tests to work correctly.
+#
+# Also, since we'll eventually lose the journal from this test, let's temporarily
+# forward everything to console, to make potential fails debug-able.
 trap at_exit EXIT
+mkdir -p /run/systemd/journald.conf.d/
+echo -ne '[Journal]\nForwardToConsole=yes' >/run/systemd/journald.conf.d/99-forward-to-console.conf
 mount -t tmpfs tmpfs /var/log/journal
 systemctl restart systemd-journald
 
index 5fb7dddf8915315bb2394430e8c6d334ec3b8079..c84974f1de4777f042790f12c4a2feb35b702816 100755 (executable)
@@ -32,10 +32,19 @@ proc_supports_option() {
 # the transient stuff from systemd-run. Let's just skip the following tests
 # in that case instead of complicating the test setup even more */
 if [[ -z "${COVERAGE_BUILD_DIR:-}" ]]; then
+    if ! systemd-detect-virt -cq && command -v bootctl >/dev/null; then
+        boot_path="$(bootctl --print-boot-path)"
+        esp_path="$(bootctl --print-esp-path)"
+
+        # If the mount points are handled by automount units, make sure we trigger
+        # them before proceeding further
+        ls -l "$boot_path" "$esp_path"
+    fi
+
     systemd-run --wait --pipe -p ProtectSystem=yes \
-        bash -xec "test ! -w /usr; test ! -w /boot; test -w /etc; test -w /var"
+        bash -xec "test ! -w /usr; ${boot_path:+"test ! -w $boot_path; test ! -w $esp_path;"} test -w /etc; test -w /var"
     systemd-run --wait --pipe -p ProtectSystem=full \
-        bash -xec "test ! -w /usr; test ! -w /boot; test ! -w /etc; test -w /var"
+        bash -xec "test ! -w /usr; ${boot_path:+"test ! -w $boot_path; test ! -w $esp_path;"} test ! -w /etc; test -w /var"
     systemd-run --wait --pipe -p ProtectSystem=strict \
         bash -xec "test ! -w /; test ! -w /etc; test ! -w /var; test -w /dev; test -w /proc"
     systemd-run --wait --pipe -p ProtectSystem=no \
index 333b95f9bb1daf33965ee23145422b3898953fce..61801c543af2e281b75cec49fc25a3a260fcb820 100755 (executable)
@@ -14,7 +14,7 @@ chmod 744 /tmp/badbin
 cat >/run/systemd/system/badbin_assert.service <<EOF
 [Service]
 ExecStart=/tmp/badbin
-Restart=never
+Restart=no
 EOF
 
 cat >/run/systemd/system/badbin_assert.socket <<EOF
index 9598c8ea0c219b44d0e338167eee6bfe410b27b4..5c6b4cee7908e124dcabc2d172390cc4110d5961 100755 (executable)
@@ -8,6 +8,14 @@ if systemd-detect-virt -qc; then
     exit 1
 fi
 
+# This test requires systemd to run in the initrd as well, which is not the case
+# for mkinitrd-based initrd (Ubuntu/Debian)
+if [[ "$(systemctl show -P InitRDTimestampMonotonic)" -eq 0 ]]; then
+    echo "systemd didn't run in the initrd, skipping the test"
+    touch /skipped
+    exit 0
+fi
+
 # We should've created a mount under /run in initrd (see the other half of the test)
 # that should've survived the transition from initrd to the real system
 test -d /run/initrd-mount-target
index e790b37c70d0d75bec751276ce4df2333f2fbbc4..0266f6266fb43ce007801925cce7ac371be26fcc 100755 (executable)
@@ -5,6 +5,11 @@ set -o pipefail
 
 # shellcheck source=test/units/test-control.sh
 . "$(dirname "$0")"/test-control.sh
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+maybe_mount_usr_overlay
+trap 'maybe_umount_usr_overlay' EXIT
 
 clear_unit() {
     local unit_name="${1:?}"
index 83446a5704b0eb487c09f975aa9b7c55df1785d5..74d36c405dabc0ae72f61e343fcb723f1b3b76b3 100755 (executable)
@@ -26,6 +26,19 @@ systemd-run --wait \
                  -w /sys/fs/cgroup/system.slice/test-0.service/cgroup.procs -a \
                  -w /sys/fs/cgroup/system.slice/test-0.service/cgroup.subtree_control
 
+# Test if this also works for some of the more recent attrs the kernel might or might not support
+for attr in cgroup.threads memory.oom.group memory.reclaim ; do
+
+    if grep -q "$attr" /sys/kernel/cgroup/delegate ; then
+        systemd-run --wait \
+                    --unit=test-0.service \
+                    --property="DynamicUser=1" \
+                    --property="Delegate=" \
+                    test -w /sys/fs/cgroup/system.slice/test-0.service/ -a \
+                    -w /sys/fs/cgroup/system.slice/test-0.service/"$attr"
+    fi
+done
+
 systemd-run --wait \
             --unit=test-1.service \
             --property="DynamicUser=1" \
index 0a8a920eeb430f099aab5498c0a5c563cb33ce42..eeec411e9c8f4fb49af5da00ac9045ade1b92c85 100755 (executable)
@@ -5,8 +5,6 @@ set -o pipefail
 
 # TODO:
 #   - /proc/cmdline parsing
-#   - figure out token support (apart from TPM2, as that's covered by TEST-70-TPM2)
-#       - this might help https://www.qemu.org/docs/master/system/devices/ccid.html
 #   - expect + interactive auth?
 
 # We set up an encrypted /var partition which should get mounted automatically
@@ -185,6 +183,7 @@ empty0               $IMAGE_EMPTY    -                               headless=1,
 empty1               $IMAGE_EMPTY    -                               headless=1,try-empty-password=1
 # This one expects the key to be under /{etc,run}/cryptsetup-keys.d/empty_nokey.key
 empty_nokey          $IMAGE_EMPTY    -                               headless=1
+empty_pkcs11_auto    $IMAGE_EMPTY    -                               headless=1,pkcs11-uri=auto
 
 detached             $IMAGE_DETACHED $IMAGE_DETACHED_KEYFILE         headless=1,header=$IMAGE_DETACHED_HEADER,keyfile-offset=32,keyfile-size=16
 detached_store0      $IMAGE_DETACHED $IMAGE_DETACHED_KEYFILE         headless=1,header=/header:LABEL=header_store,keyfile-offset=32,keyfile-size=16
@@ -229,6 +228,19 @@ mkdir -p /run/cryptsetup-keys.d
 cp "$IMAGE_EMPTY_KEYFILE" /run/cryptsetup-keys.d/empty_nokey.key
 cryptsetup_start_and_check empty_nokey
 
+if [[ -r /etc/softhsm2.conf ]]; then
+    # Test unlocking with a PKCS#11 token
+    export SOFTHSM2_CONF="/etc/softhsm2.conf"
+    PIN="1234" systemd-cryptenroll --pkcs11-token-uri="pkcs11:token=TestToken;object=RSATestKey" --unlock-key-file="$IMAGE_EMPTY_KEYFILE" "$IMAGE_EMPTY"
+    cryptsetup_start_and_check empty_pkcs11_auto
+    cryptsetup luksKillSlot -q "$IMAGE_EMPTY" 2
+    cryptsetup token remove --token-id 0 "$IMAGE_EMPTY"
+    PIN="1234" systemd-cryptenroll --pkcs11-token-uri="pkcs11:token=TestToken;object=ECTestKey" --unlock-key-file="$IMAGE_EMPTY_KEYFILE" "$IMAGE_EMPTY"
+    cryptsetup_start_and_check empty_pkcs11_auto
+    cryptsetup luksKillSlot -q "$IMAGE_EMPTY" 2
+    cryptsetup token remove --token-id 0 "$IMAGE_EMPTY"
+fi
+
 cryptsetup_start_and_check detached
 cryptsetup_start_and_check detached_store{0..2}
 cryptsetup_start_and_check -f detached_fail{0..4}
index 5aaac099f45b2413a9802651b0508bf9bb595f3a..9f672c738a36633aa0f14ecb9b77a289bffe1470 100755 (executable)
@@ -12,10 +12,13 @@ at_exit() {
         rm -fvr "/usr/lib/systemd/system/$UNIT_NAME" "/etc/systemd/system/$UNIT_NAME.d" "+4"
     fi
 
+    maybe_umount_usr_overlay
+
     rm -f /etc/init.d/issue-24990
     return 0
 }
 
+maybe_mount_usr_overlay
 trap at_exit EXIT
 
 # Create a simple unit file for testing
index 03aba36ab83957962eeb9eacca584af42fe2b10b..5e99302299032fc266621ac7043bbf5579048d44 100755 (executable)
@@ -3,7 +3,7 @@
 set -eux
 set -o pipefail
 
-if journalctl -b -t systemd --grep '\.device: Changed plugged -> dead'; then
+if journalctl -b -t systemd --grep '(?<!loop.|diskseq-.)\.device: Changed plugged -> dead'; then
     exit 1
 fi
 
index bcbe7a1e6a90c60889670167c96ee8073286fd8c..c7d11cffe6ecefc41088cf6852f434a5b0dc6f9d 100755 (executable)
@@ -314,6 +314,16 @@ if ! systemd-detect-virt -q -c ; then
     systemctl -P Wants show getty.target | grep -q container-getty@idontexist.service
 fi
 
+# Decrypt/encrypt via varlink
+
+echo -n '{"data":"Zm9vYmFyCg=="}' > /tmp/vlcredsdata
+
+varlinkctl call /run/systemd/io.systemd.Credentials io.systemd.Credentials.Encrypt "$(cat /tmp/vlcredsdata)" | \
+    varlinkctl call /run/systemd/io.systemd.Credentials io.systemd.Credentials.Decrypt > /tmp/vlcredsdata2
+
+cmp /tmp/vlcredsdata /tmp/vlcredsdata2
+rm /tmp/vlcredsdata /tmp/vlcredsdata2
+
 systemd-analyze log-level info
 
 touch /testok
index e800a7a12c2695d1246f7850bb1521dcef9d6009..3d0723e7dcc096776c75ccedac72708d4ab37ee7 100755 (executable)
@@ -6,6 +6,9 @@ set -o pipefail
 # shellcheck source=test/units/util.sh
 . "$(dirname "$0")"/util.sh
 
+maybe_mount_usr_overlay
+trap 'maybe_umount_usr_overlay' EXIT
+
 teardown_test_dependencies() (
     set +eux
 
diff --git a/test/units/testsuite-62-6.service b/test/units/testsuite-62-6.service
new file mode 100644 (file)
index 0000000..876d8f3
--- /dev/null
@@ -0,0 +1,10 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-62-RESTRICT-IFACES-altname
+[Service]
+ExecStart=/bin/sh -c 'ping -c 1 -W 0.2 192.168.113.1'
+ExecStart=/bin/sh -c 'ping -c 1 -W 0.2 192.168.113.5'
+ExecStart=/bin/sh -c '! ping -c 1 -W 0.2 192.168.113.9'
+RestrictNetworkInterfaces=veth0-altname-with-more-than-15-chars
+RestrictNetworkInterfaces=veth1-altname-with-more-than-15-chars
+Type=oneshot
index ed408218fabf94b842fc38ab610a778a4b857d07..0dbedc4c868408e0343535eaf17f1c34b5c1c7e4 100755 (executable)
@@ -17,6 +17,7 @@ setup() {
         ip -n "ns${i}" link set dev lo up
         ip -n "ns${i}" addr add "192.168.113."$((4*i+1))/30 dev "veth${i}_"
         ip link set dev "veth${i}" up
+        ip link property add dev "veth${i}" altname "veth${i}-altname-with-more-than-15-chars"
         ip addr add "192.168.113."$((4*i+2))/30 dev "veth${i}"
     done
 }
index 6c819dfe4a4eebd94389dc6b6e7ac4964a0efcec..078bc8b5f5301d361e8cce07c44656a5981b188b 100755 (executable)
@@ -180,6 +180,9 @@ if [[ ! -v ASAN_OPTIONS ]]; then
     # check that systemd-analyze cat-config paths work in a chroot
     mkdir -p /tmp/root
     mount --bind / /tmp/root
+    if mountpoint -q /usr; then
+        mount --bind /usr /tmp/root/usr
+    fi
     systemd-analyze cat-config systemd/system-preset >/tmp/out1
     chroot /tmp/root systemd-analyze cat-config systemd/system-preset >/tmp/out2
     diff /tmp/out{1,2}
@@ -334,6 +337,17 @@ systemd-analyze verify /tmp/hoge@test.service
 (! systemd-analyze verify /tmp/hoge@nonexist.service)
 (! systemd-analyze verify /tmp/hoge@.service)
 
+# test that all commands are verified.
+cat <<EOF >/tmp/multi-exec-start.service
+[Service]
+Type=oneshot
+ExecStart=true
+ExecStart=ls
+EOF
+systemd-analyze verify /tmp/multi-exec-start.service
+echo 'ExecStart=command-should-not-exist' >>/tmp/multi-exec-start.service
+(! systemd-analyze verify /tmp/multi-exec-start.service)
+
 # Added an additional "INVALID_ID" id to the .json to verify that nothing breaks when input is malformed
 # The PrivateNetwork id description and weight was changed to verify that 'security' is actually reading in
 # values from the .json file when required. The default weight for "PrivateNetwork" is 2500, and the new weight
index df5af4ba87352d551902e31a175583c2326e5982..3e5b78879ad46894e75b0f750e96e1e6d0abcc1f 100755 (executable)
@@ -9,6 +9,9 @@ set -o pipefail
 # shellcheck source=test/units/util.sh
 . "$(dirname "$0")"/util.sh
 
+maybe_mount_usr_overlay
+trap 'maybe_umount_usr_overlay' EXIT
+
 enable_debug() {
     mkdir -p /run/systemd/system/systemd-localed.service.d
     cat >>/run/systemd/system/systemd-localed.service.d/override.conf <<EOF
diff --git a/test/units/testsuite-74.bootctl.sh b/test/units/testsuite-74.bootctl.sh
new file mode 100755 (executable)
index 0000000..61373b5
--- /dev/null
@@ -0,0 +1,261 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+if systemd-detect-virt --quiet --container; then
+    echo "running on container, skipping."
+    exit 0
+fi
+
+if ! command -v bootctl >/dev/null; then
+    echo "bootctl not found, skipping."
+    exit 0
+fi
+
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+# shellcheck source=test/units/test-control.sh
+. "$(dirname "$0")"/test-control.sh
+
+basic_tests() {
+    bootctl "$@" --help
+    bootctl "$@" --version
+
+    bootctl "$@" install --make-entry-directory=yes
+    bootctl "$@" remove  --make-entry-directory=yes
+
+    bootctl "$@" install --all-architectures
+    bootctl "$@" remove  --all-architectures
+
+    bootctl "$@" install --make-entry-directory=yes --all-architectures
+    bootctl "$@" remove  --make-entry-directory=yes --all-architectures
+
+    bootctl "$@" install
+    (! bootctl "$@" update)
+    bootctl "$@" update --graceful
+
+    bootctl "$@" is-installed
+    bootctl "$@" is-installed --graceful
+    bootctl "$@" random-seed
+
+    bootctl "$@"
+    bootctl "$@" status
+    bootctl "$@" status --quiet
+    bootctl "$@" list
+    bootctl "$@" list --quiet
+    bootctl "$@" list --json=short
+    bootctl "$@" list --json=pretty
+
+    bootctl "$@" remove
+    (! bootctl "$@" is-installed)
+    (! bootctl "$@" is-installed --graceful)
+}
+
+testcase_bootctl_basic() {
+    assert_eq "$(bootctl --print-esp-path)" "/efi"
+    assert_eq "$(bootctl --print-boot-path)" "/boot"
+    bootctl --print-root-device
+
+    basic_tests
+}
+
+cleanup_image() (
+    set +e
+
+    if [[ -z "${IMAGE_DIR:-}" ]]; then
+        return 0
+    fi
+
+    umount "${IMAGE_DIR}/root"
+
+    if [[ -n "${LOOPDEV:-}" ]]; then
+        losetup -d "${LOOPDEV}"
+        unset LOOPDEV
+    fi
+
+    udevadm settle
+
+    rm -rf "${IMAGE_DIR}"
+    unset IMAGE_DIR
+
+    return 0
+)
+
+testcase_bootctl_image() {
+    IMAGE_DIR="$(mktemp --directory /tmp/test-bootctl.XXXXXXXXXX)"
+    trap cleanup_image RETURN
+
+    truncate -s 256m "${IMAGE_DIR}/image"
+
+    cat >"${IMAGE_DIR}/partscript" <<EOF
+label: gpt
+type=C12A7328-F81F-11D2-BA4B-00A0C93EC93B name=esp  size=64M
+type=0FC63DAF-8483-4772-8E79-3D69D8477DE4 name=root size=64M bootable
+type=BC13C2FF-59E6-4262-A352-B275FD6F7172 name=boot
+EOF
+
+    LOOPDEV="$(losetup --show -P -f "${IMAGE_DIR}/image")"
+    sfdisk "$LOOPDEV" <"${IMAGE_DIR}/partscript"
+
+    udevadm settle
+
+    mkfs.vfat -n esp  "${LOOPDEV}p1"
+    mkfs.ext4 -L root "${LOOPDEV}p2"
+    mkfs.ext4 -L boot "${LOOPDEV}p3"
+
+    mkdir -p "${IMAGE_DIR}/root"
+    mount -t ext4 "${LOOPDEV}p2" "${IMAGE_DIR}/root"
+
+    mkdir -p "${IMAGE_DIR}/root/efi"
+    mkdir -p "${IMAGE_DIR}/root/boot"
+    mkdir -p "${IMAGE_DIR}/root/etc"
+    mkdir -p "${IMAGE_DIR}/root/usr/lib"
+    if [[ -f /usr/lib/os-release ]]; then
+        cp /usr/lib/os-release "${IMAGE_DIR}/root/usr/lib/."
+        ln -s ../usr/lib/os-release "${IMAGE_DIR}/root/etc/os-release"
+    else
+        cp -a /etc/os-release "${IMAGE_DIR}/root/etc/."
+    fi
+
+    umount "${IMAGE_DIR}/root"
+
+    assert_eq "$(bootctl --image "${IMAGE_DIR}/image" --print-esp-path)" "/run/systemd/mount-rootfs/efi"
+    assert_eq "$(bootctl --image "${IMAGE_DIR}/image" --print-esp-path --esp-path=/efi)" "/run/systemd/mount-rootfs/efi"
+    assert_eq "$(bootctl --image "${IMAGE_DIR}/image" --print-boot-path)" "/run/systemd/mount-rootfs/boot"
+    assert_eq "$(bootctl --image "${IMAGE_DIR}/image" --print-boot-path --boot-path=/boot)" "/run/systemd/mount-rootfs/boot"
+
+    # FIXME: This provides spurious result.
+    bootctl --image "${IMAGE_DIR}/image" --print-root-device || :
+
+    basic_tests --image "${IMAGE_DIR}/image"
+}
+
+cleanup_raid() (
+    set +e
+
+    if [[ -z "${IMAGE_DIR:-}" ]]; then
+        return 0
+    fi
+
+    systemd-umount "${IMAGE_DIR}/root/efi"
+    systemd-umount "${IMAGE_DIR}/root/boot"
+    systemd-umount "${IMAGE_DIR}/root"
+
+    mdadm --misc --stop /dev/md/raid-esp
+    mdadm --misc --stop /dev/md/raid-root
+
+    if [[ -n "${LOOPDEV1:-}" ]]; then
+        mdadm --misc --force --zero-superblock "${LOOPDEV1}p1"
+        mdadm --misc --force --zero-superblock "${LOOPDEV1}p2"
+    fi
+
+    if [[ -n "${LOOPDEV2:-}" ]]; then
+        mdadm --misc --force --zero-superblock "${LOOPDEV2}p1"
+        mdadm --misc --force --zero-superblock "${LOOPDEV2}p2"
+    fi
+
+    udevadm settle
+
+    if [[ -n "${LOOPDEV1:-}" ]]; then
+        mdadm --misc --force --zero-superblock "${LOOPDEV1}p1"
+        mdadm --misc --force --zero-superblock "${LOOPDEV1}p2"
+        losetup -d "${LOOPDEV1}"
+        unset LOOPDEV1
+    fi
+
+    if [[ -n "${LOOPDEV2:-}" ]]; then
+        mdadm --misc --force --zero-superblock "${LOOPDEV2}p1"
+        mdadm --misc --force --zero-superblock "${LOOPDEV2}p2"
+        losetup -d "${LOOPDEV2}"
+        unset LOOPDEV2
+    fi
+
+    udevadm settle
+
+    rm -rf "${IMAGE_DIR}"
+
+    return 0
+)
+
+testcase_bootctl_raid() {
+    if ! command -v mdadm >/dev/null; then
+        echo "mdadm not found, skipping."
+        return 0
+    fi
+
+    if ! command -v mkfs.btrfs >/dev/null; then
+        echo "mkfs.btrfs not found, skipping."
+        return 0
+    fi
+
+    IMAGE_DIR="$(mktemp --directory /tmp/test-bootctl.XXXXXXXXXX)"
+    trap cleanup_raid RETURN
+
+    truncate -s 256m "${IMAGE_DIR}/image1"
+    truncate -s 256m "${IMAGE_DIR}/image2"
+
+    cat >"${IMAGE_DIR}/partscript" <<EOF
+label: gpt
+type=C12A7328-F81F-11D2-BA4B-00A0C93EC93B name=esp  size=64M
+type=0FC63DAF-8483-4772-8E79-3D69D8477DE4 name=root size=64M bootable
+type=BC13C2FF-59E6-4262-A352-B275FD6F7172 name=boot
+EOF
+
+    LOOPDEV1="$(losetup --show -P -f "${IMAGE_DIR}/image1")"
+    LOOPDEV2="$(losetup --show -P -f "${IMAGE_DIR}/image2")"
+    sfdisk "$LOOPDEV1" <"${IMAGE_DIR}/partscript"
+    sfdisk "$LOOPDEV2" <"${IMAGE_DIR}/partscript"
+
+    udevadm settle
+
+    echo y | mdadm --create /dev/md/raid-esp --name "raid-esp" "${LOOPDEV1}p1" "${LOOPDEV2}p1" -v -f --level=1 --raid-devices=2
+    mkfs.vfat /dev/md/raid-esp
+    echo y | mdadm --create /dev/md/raid-root --name "raid-root" "${LOOPDEV1}p2" "${LOOPDEV2}p2" -v -f --level=1 --raid-devices=2
+    mkfs.ext4 /dev/md/raid-root
+    mkfs.btrfs -f -M -d raid1 -m raid1 -L "raid-boot" "${LOOPDEV1}p3" "${LOOPDEV2}p3"
+
+    mkdir -p "${IMAGE_DIR}/root"
+    mount -t ext4 /dev/md/raid-root "${IMAGE_DIR}/root"
+    mkdir -p "${IMAGE_DIR}/root/efi"
+    mount -t vfat /dev/md/raid-esp "${IMAGE_DIR}/root/efi"
+    mkdir -p "${IMAGE_DIR}/root/boot"
+    mount -t btrfs "${LOOPDEV1}p3" "${IMAGE_DIR}/root/boot"
+
+    mkdir -p "${IMAGE_DIR}/root/etc"
+    mkdir -p "${IMAGE_DIR}/root/usr/lib"
+    if [[ -f /usr/lib/os-release ]]; then
+        cp /usr/lib/os-release "${IMAGE_DIR}/root/usr/lib/."
+        ln -s ../usr/lib/os-release "${IMAGE_DIR}/root/etc/os-release"
+    else
+        cp -a /etc/os-release "${IMAGE_DIR}/root/etc/."
+    fi
+
+    # find_esp() does not support md RAID partition.
+    (! bootctl --root "${IMAGE_DIR}/root" --print-esp-path)
+    (! bootctl --root "${IMAGE_DIR}/root" --print-esp-path --esp-path=/efi)
+
+    # If the verification is relaxed, it accepts md RAID partition.
+    assert_eq "$(SYSTEMD_RELAX_ESP_CHECKS=yes bootctl --root "${IMAGE_DIR}/root" --print-esp-path)" "${IMAGE_DIR}/root/efi"
+    assert_eq "$(SYSTEMD_RELAX_ESP_CHECKS=yes bootctl --root "${IMAGE_DIR}/root" --print-esp-path --esp-path=/efi)" "${IMAGE_DIR}/root/efi"
+
+    # find_xbootldr() does not support btrfs RAID, and bootctl tries to fall back to use ESP.
+    # (but as in the above, the ESP verification is also failed in this case).
+    (! bootctl --root "${IMAGE_DIR}/root" --print-boot-path)
+    (! bootctl --root "${IMAGE_DIR}/root" --print-boot-path --boot-path=/boot)
+
+    # If the verification for ESP is relaxed, bootctl falls back to use ESP.
+    assert_eq "$(SYSTEMD_RELAX_ESP_CHECKS=yes bootctl --root "${IMAGE_DIR}/root" --print-boot-path)" "${IMAGE_DIR}/root/efi"
+
+    # If the verification is relaxed, it accepts the xbootldr partition.
+    assert_eq "$(SYSTEMD_RELAX_XBOOTLDR_CHECKS=yes bootctl --root "${IMAGE_DIR}/root" --print-boot-path)" "${IMAGE_DIR}/root/boot"
+    assert_eq "$(SYSTEMD_RELAX_XBOOTLDR_CHECKS=yes bootctl --root "${IMAGE_DIR}/root" --print-boot-path --boot-path=/boot)" "${IMAGE_DIR}/root/boot"
+
+    # FIXME: This provides spurious result.
+    bootctl --root "${IMAGE_DIR}/root" --print-root-device || :
+
+    SYSTEMD_RELAX_ESP_CHECKS=yes SYSTEMD_RELAX_XBOOTLDR_CHECKS=yes basic_tests --root "${IMAGE_DIR}/root"
+}
+
+run_testcases
index b857abcf9a6ebb604ce628c6b9bed34cae0c4340..06a3c39e776a6c60971a3583ffcc619d6a05329f 100755 (executable)
@@ -28,6 +28,16 @@ Name=test
 EOF
 
 # Test files
+
+networkctl mask --runtime "donotexist.network"
+assert_eq "$(readlink /run/systemd/network/donotexist.network)" "/dev/null"
+networkctl unmask "donotexist.network" # unmask should work even without --runtime
+[[ ! -e /run/systemd/network/donotexist.network ]]
+
+touch /usr/lib/systemd/network/donotexist.network
+(! networkctl unmask "donotexist.network")
+rm /usr/lib/systemd/network/donotexist.network
+
 networkctl cat "$NETWORK_NAME" | tail -n +2 | cmp - "/usr/lib/systemd/network/$NETWORK_NAME"
 
 cat >new <<EOF
@@ -36,11 +46,20 @@ Name=test2
 EOF
 
 EDITOR='mv new' script -ec 'networkctl edit --runtime "$NETWORK_NAME"' /dev/null
+(! networkctl mask --runtime "$NETWORK_NAME")
 printf '%s\n' '[Match]' 'Name=test2' | cmp - "/run/systemd/network/$NETWORK_NAME"
 
+networkctl mask "$NETWORK_NAME"
+assert_eq "$(readlink "/etc/systemd/network/$NETWORK_NAME")" "/dev/null"
+(! networkctl edit "$NETWORK_NAME")
+(! networkctl edit --runtime "$NETWORK_NAME")
+(! networkctl cat "$NETWORK_NAME")
+networkctl unmask "$NETWORK_NAME"
+
 EDITOR='true' script -ec 'networkctl edit "$NETWORK_NAME"' /dev/null
 printf '%s\n' '[Match]' 'Name=test2' | cmp - "/etc/systemd/network/$NETWORK_NAME"
 
+(! networkctl mask "$NETWORK_NAME")
 (! EDITOR='true' script -ec 'networkctl edit --runtime "$NETWORK_NAME"' /dev/null)
 
 cat >"+4" <<EOF
index 2caca7a5a2ac00cfbe72491433a757bb33b4a706..d43d17ca6124a05a77414da48db8ac691d61721d 100755 (executable)
@@ -227,5 +227,9 @@ B=$(systemd-run -q --wait --pipe -p ProtectProc=invisible cat /proc/version)
 
 assert_eq "$A" "$B"
 
+# Check that invoking the tool under the uid0 alias name works
+uid0 ls /
+echo "$(uid0 echo foo)" = "foo"
+
 umount /proc/version
 rm "$V"
index 5dc31f8baa39b4ba682ab213539a57b0c1e97519..54234484c460bb680b834c12eecf791a98f34927 100755 (executable)
@@ -20,6 +20,12 @@ run() {
     "$@" |& tee "$RUN_OUT"
 }
 
+run_delv() {
+    # Since [0] delv no longer loads /etc/(bind/)bind.keys by default, so we
+    # have to do that explicitly for each invocation
+    run delv -a /etc/bind.keys "$@"
+}
+
 disable_ipv6() {
     sysctl -w net.ipv6.conf.all.disable_ipv6=1
 }
@@ -46,6 +52,9 @@ monitor_check_rr() (
 restart_resolved() {
     systemctl stop systemd-resolved.service
     (! systemctl is-failed systemd-resolved.service)
+    # Reset the restart counter since we call this method a bunch of times
+    # and can occasionally hit the default rate limit
+    systemctl reset-failed systemd-resolved.service
     systemctl start systemd-resolved.service
     systemctl service-log-level systemd-resolved.service debug
 }
@@ -356,15 +365,15 @@ grep -qF "unsigned.test IN MX 15 mail.unsigned.test" "$RUN_OUT"
 # Check the trust chain (with and without systemd-resolved in between
 # Issue: https://github.com/systemd/systemd/issues/22002
 # PR: https://github.com/systemd/systemd/pull/23289
-run delv @ns1.unsigned.test signed.test
+run_delv @ns1.unsigned.test signed.test
 grep -qF "; fully validated" "$RUN_OUT"
-run delv signed.test
+run_delv signed.test
 grep -qF "; fully validated" "$RUN_OUT"
 
 for addr in "${DNS_ADDRESSES[@]}"; do
-    run delv "@$addr" -t A mail.signed.test
+    run_delv "@$addr" -t A mail.signed.test
     grep -qF "; fully validated" "$RUN_OUT"
-    run delv "@$addr" -t AAAA mail.signed.test
+    run_delv "@$addr" -t AAAA mail.signed.test
     grep -qF "; fully validated" "$RUN_OUT"
 done
 run resolvectl query mail.signed.test
@@ -402,7 +411,7 @@ grep -qF "10.0.0.123" "$RUN_OUT"
 grep -qF "fd00:dead:beef:cafe::123" "$RUN_OUT"
 grep -qF "authenticated: yes" "$RUN_OUT"
 # Check OPENPGPKEY support
-run delv -t OPENPGPKEY 5a786cdc59c161cdafd818143705026636962198c66ed4c5b3da321e._openpgpkey.signed.test
+run_delv -t OPENPGPKEY 5a786cdc59c161cdafd818143705026636962198c66ed4c5b3da321e._openpgpkey.signed.test
 grep -qF "; fully validated" "$RUN_OUT"
 run resolvectl openpgp mr.smith@signed.test
 grep -qF "5a786cdc59c161cdafd818143705026636962198c66ed4c5b3da321e._openpgpkey.signed.test" "$RUN_OUT"
@@ -418,11 +427,11 @@ check_domain() {
     local addr
 
     for addr in "${DNS_ADDRESSES[@]}"; do
-        run delv "@$addr" -t "$record" "$domain"
+        run_delv "@$addr" -t "$record" "$domain"
         grep -qF "$message" "$RUN_OUT"
     done
 
-    run delv -t "$record" "$domain"
+    run_delv -t "$record" "$domain"
     grep -qF "$message" "$RUN_OUT"
 
     run resolvectl query "$domain"
@@ -458,9 +467,9 @@ grep -qE "^follow14\.final\.signed\.test\..+IN\s+NSEC\s+" "$RUN_OUT"
 # Check the trust chain (with and without systemd-resolved in between
 # Issue: https://github.com/systemd/systemd/issues/22002
 # PR: https://github.com/systemd/systemd/pull/23289
-run delv @ns1.unsigned.test sub.onlinesign.test
+run_delv @ns1.unsigned.test sub.onlinesign.test
 grep -qF "; fully validated" "$RUN_OUT"
-run delv sub.onlinesign.test
+run_delv sub.onlinesign.test
 grep -qF "; fully validated" "$RUN_OUT"
 
 run dig +short sub.onlinesign.test
@@ -474,11 +483,11 @@ run resolvectl query --legend=no -t TXT onlinesign.test
 grep -qF 'onlinesign.test IN TXT "hello from onlinesign"' "$RUN_OUT"
 
 for addr in "${DNS_ADDRESSES[@]}"; do
-    run delv "@$addr" -t A dual.onlinesign.test
+    run_delv "@$addr" -t A dual.onlinesign.test
     grep -qF "10.0.0.135" "$RUN_OUT"
-    run delv "@$addr" -t AAAA dual.onlinesign.test
+    run_delv "@$addr" -t AAAA dual.onlinesign.test
     grep -qF "fd00:dead:beef:cafe::135" "$RUN_OUT"
-    run delv "@$addr" -t ANY ipv6.onlinesign.test
+    run_delv "@$addr" -t ANY ipv6.onlinesign.test
     grep -qF "fd00:dead:beef:cafe::136" "$RUN_OUT"
 done
 run resolvectl query dual.onlinesign.test
index b5ed73237c8babc45bb90e2ccd624ee97f5a2091..a18c8980cf0ee7185077523c4e42758aaa8264e2 100755 (executable)
@@ -216,3 +216,19 @@ kernel_supports_lsm() {
 
     return 1
 }
+
+MOUNTED_USR_OVERLAY=false
+
+maybe_mount_usr_overlay() {
+    if [[ ! -w /usr ]]; then
+        mkdir -p /tmp/usr-overlay/{upperdir,workdir}
+        mount -t overlay -o lowerdir=/usr,upperdir=/tmp/usr-overlay/upperdir,workdir=/tmp/usr-overlay/workdir overlay /usr
+       MOUNTED_USR_OVERLAY=true
+    fi
+}
+
+maybe_umount_usr_overlay() {
+    if "$MOUNTED_USR_OVERLAY"; then
+        umount -l /usr
+    fi
+}
index e7bfb7f838f95246c8a81a6075d4721ce829b8ca..9d3604951d56bf6a1b6d1e450520bdb25759ae6c 100644 (file)
@@ -280,6 +280,11 @@ units = [
           'file' : 'systemd-coredump@.service.in',
           'conditions' : ['ENABLE_COREDUMP'],
         },
+        {
+          'file' : 'systemd-creds.socket',
+          'symlinks' : ['sockets.target.wants/'],
+        },
+        { 'file' : 'systemd-creds@.service' },
         { 'file' : 'systemd-exit.service' },
         {
           'file' : 'systemd-firstboot.service',
@@ -303,6 +308,10 @@ units = [
           'file' : 'systemd-homed-activate.service',
           'conditions' : ['ENABLE_HOMED'],
         },
+        {
+          'file' : 'systemd-homed-firstboot.service',
+          'conditions' : ['ENABLE_HOMED'],
+        },
         {
           'file' : 'systemd-homed.service.in',
           'conditions' : ['ENABLE_HOMED'],
diff --git a/units/systemd-creds.socket b/units/systemd-creds.socket
new file mode 100644 (file)
index 0000000..794fac1
--- /dev/null
@@ -0,0 +1,23 @@
+#  SPDX-License-Identifier: LGPL-2.1-or-later
+#
+#  This file is part of systemd.
+#
+#  systemd is free software; you can redistribute it and/or modify it
+#  under the terms of the GNU Lesser General Public License as published by
+#  the Free Software Foundation; either version 2.1 of the License, or
+#  (at your option) any later version.
+
+[Unit]
+Description=Credential Encryption/Decryption (Varlink)
+Documentation=man:systemd-creds(1)
+DefaultDependencies=no
+Before=sockets.target
+
+[Socket]
+ListenStream=/run/systemd/io.systemd.Credentials
+FileDescriptorName=varlink
+SocketMode=0600
+Accept=yes
+
+[Install]
+WantedBy=sockets.target
diff --git a/units/systemd-creds@.service b/units/systemd-creds@.service
new file mode 100644 (file)
index 0000000..37cdd31
--- /dev/null
@@ -0,0 +1,19 @@
+#  SPDX-License-Identifier: LGPL-2.1-or-later
+#
+#  This file is part of systemd.
+#
+#  systemd is free software; you can redistribute it and/or modify it
+#  under the terms of the GNU Lesser General Public License as published by
+#  the Free Software Foundation; either version 2.1 of the License, or
+#  (at your option) any later version.
+
+[Unit]
+Description=Credential Encryption/Decryption (Varlink)
+Documentation=man:systemd-creds(1)
+DefaultDependencies=no
+Conflicts=shutdown.target initrd-switch-root.target
+Before=shutdown.target initrd-switch-root.target
+
+[Service]
+Environment=LISTEN_FDNAMES=varlink
+ExecStart=-systemd-creds
diff --git a/units/systemd-homed-firstboot.service b/units/systemd-homed-firstboot.service
new file mode 100644 (file)
index 0000000..3615940
--- /dev/null
@@ -0,0 +1,28 @@
+#  SPDX-License-Identifier: LGPL-2.1-or-later
+#
+#  This file is part of systemd.
+#
+#  systemd is free software; you can redistribute it and/or modify it
+#  under the terms of the GNU Lesser General Public License as published by
+#  the Free Software Foundation; either version 2.1 of the License, or
+#  (at your option) any later version.
+
+[Unit]
+Description=First Boot Home Area Wizard
+Documentation=man:homectl(1)
+ConditionFirstBoot=yes
+After=home.mount systemd-homed.service
+Before=systemd-user-sessions.service first-boot-complete.target
+
+[Service]
+Type=oneshot
+RemainAfterExit=yes
+ExecStart=homectl firstboot --prompt-new-user
+StandardOutput=tty
+StandardInput=tty
+StandardError=tty
+ImportCredential=home.*
+
+[Install]
+WantedBy=systemd-homed.service
+Also=systemd-homed.service
index e629048b97a78318bac93d159a2b8b8f5fa64c03..bfc3df1021e77b0674a50f0602f7313aa6725895 100644 (file)
@@ -39,4 +39,4 @@ TimeoutStopSec=3min
 [Install]
 WantedBy=multi-user.target
 Alias=dbus-org.freedesktop.home1.service
-Also=systemd-homed-activate.service systemd-userdbd.service
+Also=systemd-homed-activate.service systemd-userdbd.service systemd-homed-firstboot.service