]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
Merge pull request #30720 from yuwata/dhcp-server-address-verification
authorLuca Boccassi <bluca@debian.org>
Wed, 10 Jan 2024 19:29:25 +0000 (19:29 +0000)
committerGitHub <noreply@github.com>
Wed, 10 Jan 2024 19:29:25 +0000 (19:29 +0000)
dhcp-server: several fixlets for address verification

552 files changed:
.github/workflows/build_test.sh
.github/workflows/mkosi.yml
TODO
coccinelle/mfree.cocci
coccinelle/mfree_return.cocci [deleted file]
docs/ENVIRONMENT.md
hwdb.d/60-sensor.hwdb
man/journalctl.xml
man/loginctl.xml
man/logind.conf.xml
man/org.freedesktop.hostname1.xml
man/org.freedesktop.systemd1.xml
man/rules/meson.build
man/systemd-analyze.xml
man/systemd-creds.xml
man/systemd-cryptenroll.xml
man/systemd-detect-virt.xml
man/systemd-dissect.xml
man/systemd-network-generator.service.xml
man/systemd-nspawn.xml
man/systemd-tmpfiles.xml
man/systemd-vpick.xml [new file with mode: 0644]
man/systemd.exec.xml
man/systemd.link.xml
man/systemd.mount.xml
man/systemd.netdev.xml
man/systemd.resource-control.xml
man/systemd.swap.xml
man/systemd.system-credentials.xml
man/systemd.unit.xml
man/systemd.v.xml [new file with mode: 0644]
man/varlinkctl.xml
meson.build
mkosi.images/system/mkosi.conf.d/10-ubuntu.conf
shell-completion/bash/journalctl
shell-completion/bash/systemd-analyze
shell-completion/zsh/_journalctl
src/analyze/analyze-pcrs.c
src/analyze/analyze.c
src/basic/env-util.c
src/basic/env-util.h
src/basic/fd-util.c
src/basic/fd-util.h
src/basic/fileio.c
src/basic/fileio.h
src/basic/hexdecoct.c
src/basic/hexdecoct.h
src/basic/iovec-util.h
src/basic/meson.build
src/basic/missing_socket.h
src/basic/missing_wait.h [new file with mode: 0644]
src/basic/parse-util.c
src/basic/parse-util.h
src/basic/pidref.c
src/basic/pidref.h
src/basic/process-util.c
src/basic/process-util.h
src/basic/sigbus.c
src/basic/socket-util.c
src/basic/socket-util.h
src/basic/stat-util.c
src/basic/stat-util.h
src/basic/string-util.c
src/basic/string-util.h
src/basic/strv.c
src/basic/strv.h
src/basic/uid-classification.c [moved from src/basic/uid-alloc-range.c with 99% similarity]
src/basic/uid-classification.h [moved from src/basic/uid-alloc-range.h with 100% similarity]
src/basic/uid-range.c
src/basic/uid-range.h
src/basic/virt.c
src/basic/virt.h
src/boot/measure.c
src/core/cgroup.c
src/core/cgroup.h
src/core/dbus-manager.c
src/core/dbus-mount.c
src/core/dbus-unit.c
src/core/dbus.c
src/core/dynamic-user.c
src/core/exec-credential.c
src/core/exec-invoke.c
src/core/execute-serialize.c
src/core/import-creds.c
src/core/load-fragment-gperf.gperf.in
src/core/load-fragment.c
src/core/load-fragment.h
src/core/main.c
src/core/manager.c
src/core/namespace.c
src/core/unit-printf.c
src/coredump/coredump.c
src/coredump/coredumpctl.c
src/creds/creds.c
src/creds/io.systemd.credentials.policy [new file with mode: 0644]
src/creds/meson.build
src/cryptenroll/cryptenroll-pkcs11.c
src/cryptenroll/cryptenroll-tpm2.c
src/cryptsetup/cryptsetup-pkcs11.c
src/cryptsetup/cryptsetup-tokens/cryptsetup-token-systemd-fido2.c
src/cryptsetup/cryptsetup-tokens/cryptsetup-token-systemd-pkcs11.c
src/cryptsetup/cryptsetup-tokens/cryptsetup-token-systemd-tpm2.c
src/cryptsetup/cryptsetup-tokens/luks2-fido2.c
src/cryptsetup/cryptsetup-tokens/luks2-pkcs11.c
src/cryptsetup/cryptsetup-tokens/luks2-tpm2.c
src/cryptsetup/cryptsetup-tokens/luks2-tpm2.h
src/cryptsetup/cryptsetup-tpm2.c
src/cryptsetup/cryptsetup-tpm2.h
src/cryptsetup/cryptsetup.c
src/dissect/dissect.c
src/firstboot/firstboot.c
src/fsck/fsck.c
src/fundamental/string-util-fundamental.c
src/fundamental/string-util-fundamental.h
src/hibernate-resume/hibernate-resume-config.c
src/home/homectl.c
src/home/homed-home.c
src/home/homed-manager-bus.c
src/home/homed-manager.c
src/home/homework-fscrypt.c
src/home/org.freedesktop.home1.conf
src/home/pam_systemd_home.c
src/hostname/hostnamectl.c
src/hostname/hostnamed.c
src/import/importd.c
src/import/pull.c
src/journal-remote/journal-remote.c
src/journal/cat.c
src/journal/journalctl.c
src/journal/journald-server.c
src/kernel-install/kernel-install.c
src/libsystemd-network/sd-dhcp-client-id.c
src/libsystemd-network/sd-dhcp-lease.c
src/libsystemd/sd-bus/bus-socket.c
src/libsystemd/sd-bus/sd-bus.c
src/libsystemd/sd-device/device-monitor.c
src/libsystemd/sd-device/device-util.h
src/libsystemd/sd-event/sd-event.c
src/libsystemd/sd-id128/id128-util.c
src/libsystemd/sd-journal/journal-file.c
src/libsystemd/sd-journal/journal-internal.h
src/libsystemd/sd-journal/sd-journal.c
src/locale/localed-util.c
src/locale/localed.c
src/login/logind-action.c
src/login/logind-session-dbus.c
src/login/logind-session-device.c
src/login/logind-session-device.h
src/login/logind-session.c
src/login/logind-session.h
src/login/logind-user.c
src/login/logind.c
src/login/pam_systemd.c
src/machine/machined.c
src/network/generator/main.c
src/network/meson.build
src/network/netdev/macsec.c
src/network/netdev/netdev.c
src/network/netdev/vxlan.c
src/network/netdev/wireguard.c
src/network/networkctl.c
src/network/networkd-dhcp4.c
src/network/networkd-json.c
src/network/networkd-link.c
src/network/networkd-manager.c
src/network/networkd-manager.h
src/network/networkd-ndisc.c
src/network/networkd-network-gperf.gperf
src/network/networkd-network.c
src/network/networkd-nexthop.c
src/network/networkd-nexthop.h
src/network/networkd-queue.c
src/network/networkd-route-metric.c [new file with mode: 0644]
src/network/networkd-route-metric.h [new file with mode: 0644]
src/network/networkd-route-nexthop.c [new file with mode: 0644]
src/network/networkd-route-nexthop.h [new file with mode: 0644]
src/network/networkd-route.c
src/network/networkd-route.h
src/network/networkd-routing-policy-rule.c
src/nspawn/nspawn-bind-user.c
src/nspawn/nspawn.c
src/nspawn/nspawn.h
src/nss-resolve/nss-resolve.c
src/oom/oomd-manager.c
src/partition/repart.c
src/pcrlock/pcrlock.c
src/portable/portabled.c
src/resolve/meson.build
src/resolve/resolvectl.c
src/resolve/resolved-bus.c
src/resolve/resolved-conf.c
src/resolve/resolved-dns-dnssec.c
src/resolve/resolved-dns-dnssec.h
src/resolve/resolved-dns-packet.c
src/resolve/resolved-dns-packet.h
src/resolve/resolved-dns-query.c
src/resolve/resolved-dns-query.h
src/resolve/resolved-dns-transaction.c
src/resolve/resolved-dns-transaction.h
src/resolve/resolved-dns-trust-anchor.c
src/resolve/resolved-manager.c
src/resolve/resolved-manager.h
src/resolve/resolved-varlink.c
src/resolve/test-resolved-dummy-server.c [new file with mode: 0644]
src/run/run.c
src/shared/bus-polkit.c
src/shared/bus-polkit.h
src/shared/bus-print-properties.c
src/shared/bus-unit-util.c
src/shared/bus-util.c
src/shared/condition.c
src/shared/conf-parser.c
src/shared/creds-util.c
src/shared/creds-util.h
src/shared/cryptsetup-fido2.c
src/shared/dev-setup.c
src/shared/discover-image.c
src/shared/dissect-image.c
src/shared/find-esp.c
src/shared/group-record.c
src/shared/json.c
src/shared/json.h
src/shared/logs-show.c
src/shared/meson.build
src/shared/mount-util.c
src/shared/pam-util.c
src/shared/pam-util.h
src/shared/parse-helpers.c
src/shared/pkcs11-util.c
src/shared/pkcs11-util.h
src/shared/tests.h
src/shared/tpm2-util.c
src/shared/tpm2-util.h
src/shared/user-record.c
src/shared/userdb.c
src/shared/varlink-io.systemd.Hostname.c [new file with mode: 0644]
src/shared/varlink-io.systemd.Hostname.h [new file with mode: 0644]
src/shared/varlink-io.systemd.Resolve.Monitor.c
src/shared/varlink-io.systemd.Resolve.c
src/shared/varlink.c
src/shared/varlink.h
src/shared/vpick.c [new file with mode: 0644]
src/shared/vpick.h [new file with mode: 0644]
src/shared/watchdog.c
src/systemctl/fuzz-systemctl-parse-argv.c
src/sysupdate/sysupdate-pattern.c
src/sysupdate/sysupdate-resource.c
src/sysusers/sysusers.c
src/test/meson.build
src/test/test-condition.c
src/test/test-creds.c
src/test/test-dev-setup.c
src/test/test-engine.c
src/test/test-execute.c
src/test/test-hexdecoct.c
src/test/test-iovec-util.c [new file with mode: 0644]
src/test/test-json.c
src/test/test-parse-helpers.c
src/test/test-process-util.c
src/test/test-stat-util.c
src/test/test-string-util.c
src/test/test-strv.c
src/test/test-tpm2.c
src/test/test-uid-classification.c [moved from src/test/test-uid-alloc-range.c with 99% similarity]
src/test/test-uid-range.c
src/test/test-vpick.c [new file with mode: 0644]
src/timedate/timedated.c
src/timesync/timesyncd-manager.c
src/tmpfiles/tmpfiles.c
src/tpm2-setup/tpm2-generator.c
src/udev/net/link-config-gperf.gperf
src/udev/net/link-config.c
src/udev/net/link-config.h
src/udev/udev-builtin-net_setup_link.c
src/udev/udev-builtin-path_id.c
src/udev/udev-builtin.c
src/udev/udev-builtin.h
src/udev/udev-manager.c
src/udev/udev-rules.c
src/udev/udev-spawn.c
src/udev/udevadm-test-builtin.c
src/udev/udevadm-test.c
src/userdb/userdbctl.c
src/userdb/userdbd-manager.c
src/veritysetup/veritysetup-generator.c
src/veritysetup/veritysetup.c
src/vpick/meson.build [new file with mode: 0644]
src/vpick/vpick-tool.c [new file with mode: 0644]
test/TEST-24-CRYPTSETUP/test.sh
test/TEST-74-AUX-UTILS/test.sh
test/knot-data/knot.conf
test/test-execute/exec-ambientcapabilities-dynuser.service
test/test-execute/exec-ambientcapabilities-merge-nfsnobody.service
test/test-execute/exec-ambientcapabilities-merge-nobody.service
test/test-execute/exec-ambientcapabilities-merge.service
test/test-execute/exec-ambientcapabilities-nfsnobody.service
test/test-execute/exec-ambientcapabilities-nobody.service
test/test-execute/exec-ambientcapabilities.service
test/test-execute/exec-bindpaths.service
test/test-execute/exec-capabilityboundingset-invert.service
test/test-execute/exec-capabilityboundingset-merge.service
test/test-execute/exec-capabilityboundingset-reset.service
test/test-execute/exec-capabilityboundingset-simple.service
test/test-execute/exec-condition-failed.service
test/test-execute/exec-condition-skip.service
test/test-execute/exec-cpuaffinity1.service
test/test-execute/exec-cpuaffinity2.service
test/test-execute/exec-cpuaffinity3.service
test/test-execute/exec-dynamicuser-fixeduser-adm.service
test/test-execute/exec-dynamicuser-fixeduser-games.service
test/test-execute/exec-dynamicuser-fixeduser-one-supplementarygroup.service
test/test-execute/exec-dynamicuser-fixeduser.service
test/test-execute/exec-dynamicuser-runtimedirectory1.service
test/test-execute/exec-dynamicuser-runtimedirectory2.service
test/test-execute/exec-dynamicuser-runtimedirectory3.service
test/test-execute/exec-dynamicuser-statedir-migrate-step1.service
test/test-execute/exec-dynamicuser-statedir-migrate-step2.service
test/test-execute/exec-dynamicuser-supplementarygroups.service
test/test-execute/exec-environment-empty.service
test/test-execute/exec-environment-multiple.service
test/test-execute/exec-environment-no-substitute.service
test/test-execute/exec-environment.service
test/test-execute/exec-environmentfile.service
test/test-execute/exec-execsearchpath-environment-path-set.service
test/test-execute/exec-execsearchpath-environment.service
test/test-execute/exec-execsearchpath-environmentfile-set.service
test/test-execute/exec-execsearchpath-environmentfile.service
test/test-execute/exec-execsearchpath-passenvironment-set.service
test/test-execute/exec-execsearchpath-passenvironment.service
test/test-execute/exec-execsearchpath-unit-specifier.service
test/test-execute/exec-group-nfsnobody.service
test/test-execute/exec-group-nobody.service
test/test-execute/exec-group-nogroup.service
test/test-execute/exec-group.service
test/test-execute/exec-ignoresigpipe-no.service
test/test-execute/exec-ignoresigpipe-yes.service
test/test-execute/exec-inaccessiblepaths-mount-propagation.service
test/test-execute/exec-inaccessiblepaths-sys.service
test/test-execute/exec-ioschedulingclass-best-effort.service
test/test-execute/exec-ioschedulingclass-idle.service
test/test-execute/exec-ioschedulingclass-none.service
test/test-execute/exec-ioschedulingclass-realtime.service
test/test-execute/exec-load-credential.service
test/test-execute/exec-networknamespacepath-privatemounts-no.service
test/test-execute/exec-networknamespacepath-privatemounts-yes.service
test/test-execute/exec-noexecpaths-simple.service
test/test-execute/exec-oomscoreadjust-negative.service
test/test-execute/exec-oomscoreadjust-positive.service
test/test-execute/exec-passenvironment-absent.service
test/test-execute/exec-passenvironment-empty.service
test/test-execute/exec-passenvironment-repeated.service
test/test-execute/exec-passenvironment.service
test/test-execute/exec-personality-aarch64.service
test/test-execute/exec-personality-loongarch64.service
test/test-execute/exec-personality-ppc64.service
test/test-execute/exec-personality-ppc64le.service
test/test-execute/exec-personality-s390.service
test/test-execute/exec-personality-x86-64.service
test/test-execute/exec-personality-x86.service
test/test-execute/exec-privatedevices-bind.service
test/test-execute/exec-privatedevices-disabled-by-prefix.service
test/test-execute/exec-privatedevices-no-capability-mknod.service
test/test-execute/exec-privatedevices-no-capability-sys-rawio.service
test/test-execute/exec-privatedevices-no.service
test/test-execute/exec-privatedevices-yes-capability-mknod.service
test/test-execute/exec-privatedevices-yes-capability-sys-rawio.service
test/test-execute/exec-privatedevices-yes-with-group.service
test/test-execute/exec-privatedevices-yes.service
test/test-execute/exec-privatenetwork-yes-privatemounts-no.service
test/test-execute/exec-privatenetwork-yes-privatemounts-yes.service
test/test-execute/exec-privatetmp-disabled-by-prefix.service
test/test-execute/exec-privatetmp-no.service
test/test-execute/exec-privatetmp-yes.service
test/test-execute/exec-protecthome-tmpfs-vs-protectsystem-strict.service
test/test-execute/exec-protectkernellogs-no-capabilities.service
test/test-execute/exec-protectkernellogs-yes-capabilities.service
test/test-execute/exec-protectkernelmodules-no-capabilities.service
test/test-execute/exec-protectkernelmodules-yes-capabilities.service
test/test-execute/exec-protectkernelmodules-yes-mount-propagation.service
test/test-execute/exec-readonlypaths-mount-propagation.service
test/test-execute/exec-readonlypaths-simple.service
test/test-execute/exec-readonlypaths-with-bindpaths.service
test/test-execute/exec-readonlypaths.service
test/test-execute/exec-readwritepaths-mount-propagation.service
test/test-execute/exec-runtimedirectory-mode.service
test/test-execute/exec-runtimedirectory-owner-nfsnobody.service
test/test-execute/exec-runtimedirectory-owner-nobody.service
test/test-execute/exec-runtimedirectory-owner-nogroup.service
test/test-execute/exec-runtimedirectory-owner.service
test/test-execute/exec-runtimedirectory.service
test/test-execute/exec-set-credential.service
test/test-execute/exec-specifier-interpolation.service
test/test-execute/exec-standardinput-data.service
test/test-execute/exec-supplementarygroups-multiple-groups-default-group-user.service
test/test-execute/exec-supplementarygroups-multiple-groups-withgid.service
test/test-execute/exec-supplementarygroups-multiple-groups-withuid.service
test/test-execute/exec-supplementarygroups-single-group-user.service
test/test-execute/exec-supplementarygroups-single-group.service
test/test-execute/exec-supplementarygroups.service
test/test-execute/exec-systemcallerrornumber-name.service
test/test-execute/exec-systemcallerrornumber-number.service
test/test-execute/exec-systemcallfilter-failing.service
test/test-execute/exec-systemcallfilter-failing2.service
test/test-execute/exec-systemcallfilter-failing3.service
test/test-execute/exec-systemcallfilter-nonewprivileges-bounding1.service
test/test-execute/exec-systemcallfilter-nonewprivileges-bounding2.service
test/test-execute/exec-systemcallfilter-nonewprivileges-protectclock.service
test/test-execute/exec-systemcallfilter-nonewprivileges.service
test/test-execute/exec-systemcallfilter-not-failing.service
test/test-execute/exec-systemcallfilter-not-failing2.service
test/test-execute/exec-systemcallfilter-not-failing3.service
test/test-execute/exec-systemcallfilter-override-error-action.service
test/test-execute/exec-systemcallfilter-override-error-action2.service
test/test-execute/exec-systemcallfilter-system-user-nfsnobody.service
test/test-execute/exec-systemcallfilter-system-user-nobody.service
test/test-execute/exec-systemcallfilter-system-user.service
test/test-execute/exec-systemcallfilter-with-errno-in-allow-list.service
test/test-execute/exec-systemcallfilter-with-errno-multi.service
test/test-execute/exec-systemcallfilter-with-errno-name.service
test/test-execute/exec-systemcallfilter-with-errno-number.service
test/test-execute/exec-temporaryfilesystem-options.service
test/test-execute/exec-temporaryfilesystem-ro.service
test/test-execute/exec-temporaryfilesystem-usr.service
test/test-execute/exec-umask-0177.service
test/test-execute/exec-umask-default.service
test/test-execute/exec-umask-namespace.service
test/test-execute/exec-unsetenvironment.service
test/test-execute/exec-user-nfsnobody.service
test/test-execute/exec-user-nobody.service
test/test-execute/exec-user.service
test/test-execute/exec-workingdirectory-trailing-dot.service
test/test-execute/exec-workingdirectory.service
test/test-functions
test/test-network/conf/25-neighbor-dummy.network [moved from test/test-network/conf/25-neighbor-ip-dummy.network with 100% similarity]
test/test-network/conf/25-neighbor-dummy.network.d/10-step1.conf [moved from test/test-network/conf/25-neighbor-section.network with 90% similarity]
test/test-network/conf/25-neighbor-dummy.network.d/10-step2.conf [moved from test/test-network/conf/25-neighbor-section.network.d/override.conf with 99% similarity]
test/test-network/conf/25-neighbor-dummy.network.d/10-step3.conf [new file with mode: 0644]
test/test-network/conf/25-nexthop-1.network [moved from test/test-network/conf/25-nexthop.network with 97% similarity]
test/test-network/conf/25-nexthop-2.network [new file with mode: 0644]
test/test-network/conf/25-nexthop-dummy-1.network [moved from test/test-network/conf/25-nexthop-dummy.network with 100% similarity]
test/test-network/conf/25-nexthop-dummy-2.network [new file with mode: 0644]
test/test-network/conf/25-wireguard-endpoint-peer0-cred.txt [new file with mode: 0644]
test/test-network/conf/25-wireguard-no-peer-private-key-cred.txt [new file with mode: 0644]
test/test-network/conf/25-wireguard-no-peer.netdev
test/test-network/conf/25-wireguard-preshared-key-peer2-cred.txt [new file with mode: 0644]
test/test-network/conf/25-wireguard.netdev
test/test-network/conf/25-wireguard.netdev.d/peer2.conf
test/test-network/conf/26-bridge-mac-master.network [new file with mode: 0644]
test/test-network/conf/26-bridge-mac-slave.network [moved from test/test-network/conf/25-neighbor-next.network with 57% similarity]
test/test-network/conf/26-bridge-mac.link [new file with mode: 0644]
test/test-network/conf/26-bridge-mac.netdev [new file with mode: 0644]
test/test-network/systemd-networkd-tests.py
test/testsuite-04.units/verbose-success.service [new file with mode: 0644]
test/testsuite-23.units/testsuite-23-binds-to.service
test/testsuite-23.units/testsuite-23-bound-by.service
test/testsuite-23.units/testsuite-23-fail.service
test/testsuite-23.units/testsuite-23-joins-namespace-of-1.service
test/testsuite-23.units/testsuite-23-joins-namespace-of-4.service
test/testsuite-23.units/testsuite-23-joins-namespace-of-6.service
test/testsuite-23.units/testsuite-23-joins-namespace-of-8.service
test/testsuite-23.units/testsuite-23-namespaced.service
test/testsuite-23.units/testsuite-23-non-namespaced.service
test/testsuite-23.units/testsuite-23-prop-stop-one.service
test/testsuite-23.units/testsuite-23-prop-stop-two.service
test/testsuite-23.units/testsuite-23-retry-fail.service
test/testsuite-23.units/testsuite-23-retry-upheld.service
test/testsuite-23.units/testsuite-23-retry-uphold.service
test/testsuite-23.units/testsuite-23-success.service
test/testsuite-23.units/testsuite-23-upheldby-install.service
test/testsuite-23.units/testsuite-23-uphold.service
test/units/a-conj.service
test/units/a.service
test/units/b.service
test/units/c.service
test/units/d.service
test/units/daughter.service
test/units/dml-discard-empty.service
test/units/dml-discard-set-ml.service
test/units/dml-override-empty.service
test/units/dml-passthrough-empty.service
test/units/dml-passthrough-set-dml.service
test/units/dml-passthrough-set-ml.service
test/units/e.service
test/units/f.service
test/units/g.service
test/units/grandchild.service
test/units/h.service
test/units/i.service
test/units/loopy.service
test/units/loopy2.service
test/units/loopy3.service
test/units/loopy4.service
test/units/nomemleaf.service
test/units/sched_idle_bad.service
test/units/sched_idle_ok.service
test/units/sched_rr_bad.service
test/units/sched_rr_change.service
test/units/sched_rr_ok.service
test/units/son.service
test/units/testsuite-03.sh
test/units/testsuite-04.journal.sh
test/units/testsuite-05.effective-limit.sh [new file with mode: 0755]
test/units/testsuite-05.rlimit.sh [new file with mode: 0755]
test/units/testsuite-05.service
test/units/testsuite-05.sh
test/units/testsuite-07.exec-context.sh
test/units/testsuite-07.exec-deserialization.sh
test/units/testsuite-07.issue-1981.sh
test/units/testsuite-07.issue-3171.sh
test/units/testsuite-07.poll-limit.sh
test/units/testsuite-17.03.sh
test/units/testsuite-17.link-property.sh [new file with mode: 0755]
test/units/testsuite-22.18.sh [new file with mode: 0755]
test/units/testsuite-23.ExecReload.sh
test/units/testsuite-23.clean-unit.sh
test/units/testsuite-23.start-stop-no-reload.sh
test/units/testsuite-24.sh
test/units/testsuite-35.sh
test/units/testsuite-36.sh
test/units/testsuite-38-sleep.service
test/units/testsuite-44.sh
test/units/testsuite-45.sh
test/units/testsuite-50.sh
test/units/testsuite-58.sh
test/units/testsuite-59.sh
test/units/testsuite-62-1.service
test/units/testsuite-62-2.service
test/units/testsuite-62-3.service
test/units/testsuite-62-4.service
test/units/testsuite-62-5.service
test/units/testsuite-65.sh
test/units/testsuite-66-deviceisolation.service
test/units/testsuite-69.service
test/units/testsuite-71.sh
test/units/testsuite-74.delta.sh
test/units/testsuite-74.network-generator.sh [new file with mode: 0755]
test/units/testsuite-74.varlinkctl.sh
test/units/testsuite-74.vpick.sh [new file with mode: 0755]
test/units/testsuite-75.sh
test/units/unit-with-multiple-dashes.service
test/units/util.sh
tools/update-man-rules.py
units/meson.build
units/systemd-creds.socket
units/systemd-hostnamed.service.in
units/systemd-hostnamed.socket [new file with mode: 0644]
units/systemd-importd.service.in
units/systemd-localed.service.in
units/systemd-logind.service.in
units/systemd-network-generator.service.in
units/systemd-networkd.service.in
units/systemd-timedated.service.in

index c55004676146582b29f0e1bb206f60053522db01..80f01f09dd02e6b19e42d6aa700d8fc9d04be0e1 100755 (executable)
@@ -10,9 +10,9 @@ success() { echo >&2 -e "\033[32;1m$1\033[0m"; }
 ARGS=(
     "--optimization=0 -Dopenssl=disabled -Dcryptolib=gcrypt -Ddns-over-tls=gnutls -Dtpm=true -Dtpm2=enabled"
     "--optimization=s -Dutmp=false"
+    "--optimization=2 -Dc_args=-Wmaybe-uninitialized -Ddns-over-tls=openssl"
     "--optimization=3 -Db_lto=true -Ddns-over-tls=false"
     "--optimization=3 -Db_lto=false -Dtpm2=disabled -Dlibfido2=disabled -Dp11kit=disabled"
-    "--optimization=3 -Ddns-over-tls=openssl"
     "--optimization=3 -Dfexecve=true -Dstandalone-binaries=true -Dstatic-libsystemd=true -Dstatic-libudev=true"
     "-Db_ndebug=true"
 )
@@ -131,6 +131,11 @@ ninja --version
 for args in "${ARGS[@]}"; do
     SECONDS=0
 
+    if [[ "$COMPILER" == clang && "$args" =~ Wmaybe-uninitialized ]]; then
+        # -Wmaybe-uninitialized is not implemented in clang
+        continue
+    fi
+
     info "Checking build with $args"
     # shellcheck disable=SC2086
     if ! AR="$AR" \
index 8b32ec82e3251fb7d9d65c1874cc99eb250c110a..f6eddebffb55511e5e624de8d4173dc780940452 100644 (file)
@@ -76,21 +76,31 @@ jobs:
 
     steps:
     - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11
-    - uses: systemd/mkosi@bbe715f42911f9660712377a5b39335b9391ae22
+    - uses: systemd/mkosi@dbce89aabda438ba58080366631b2c242e365f21
 
     - name: Configure
       run: |
-        tee mkosi.local.conf <<EOF
+        tee mkosi.local.conf <<EOF
         [Distribution]
         Distribution=${{ matrix.distro }}
         Release=${{ matrix.release }}
-        EOF
 
-        tee mkosi.conf.d/99-ci.conf <<- EOF
         [Content]
         Environment=CI_BUILD=1
                     SLOW_TESTS=true
 
+        [Host]
+        ToolsTree=default
+        ToolsTreeDistribution=fedora
+        QemuVsock=yes
+        # Sometimes we run on a host with /dev/kvm, but it is broken, so explicitly disable it
+        QemuKvm=no
+        Ephemeral=yes
+        EOF
+
+        # These should override the options from mkosi.conf so we put them in a dropin that's ordered later
+        # instead.
+        tee mkosi.conf.d/99-ci.conf <<EOF
         [Host]
         KernelCommandLineExtra=systemd.unit=mkosi-check-and-shutdown.service
                                systemd.journald.max_level_console=debug
@@ -98,17 +108,13 @@ jobs:
                                udev.log_level=info
                                # Root device can take a long time to appear, so let's bump the timeout.
                                systemd.default_device_timeout_sec=180
-        QemuVsock=yes
-        # Sometimes we run on a host with /dev/kvm, but it is broken, so explicitly disable it
-        QemuKvm=no
-        Ephemeral=yes
         EOF
 
         # For erofs, we have to install linux-modules-extra-azure, but that doesn't match the running kernel
         # version, so we can't load the erofs module. squashfs is a builtin module so we use that instead.
 
         mkdir -p mkosi.images/system/mkosi.repart/10-usr.conf.d
-        tee mkosi.images/system/mkosi.repart/10-usr.conf.d/squashfs.conf <<EOF
+        tee mkosi.images/system/mkosi.repart/10-usr.conf.d/squashfs.conf <<EOF
         [Partition]
         Format=squashfs
         EOF
@@ -117,7 +123,7 @@ jobs:
         # eventually times out. Override it to just shutdown immediately.
         mkdir -p mkosi.images/initrd/mkosi.extra/usr/lib/systemd/system/emergency.service.d/
         mkdir -p mkosi.images/system/mkosi.extra/usr/lib/systemd/system/emergency.service.d/
-        tee mkosi.images/initrd/mkosi.extra/usr/lib/systemd/system/emergency.service.d/poweroff.conf <<EOF
+        tee mkosi.images/initrd/mkosi.extra/usr/lib/systemd/system/emergency.service.d/poweroff.conf <<EOF
         [Unit]
         FailureAction=exit
         [Service]
diff --git a/TODO b/TODO
index c4e09cbc44738c7574908b28852efe031d043f03..9b6a52f5cfb809ca8c9859e944a30c9afdad5eb0 100644 (file)
--- a/TODO
+++ b/TODO
@@ -132,6 +132,11 @@ Deprecations and removals:
 
 Features:
 
+* extend the smbios11 logic for passing credentials so that instead of passing
+  the credential data literally it can also just reference an AF_VSOCK CID/port
+  to read them from. This way the data doesn't remain in the SMBIOS blob during
+  runtime, but only in the credentials fs.
+
 * add a new ExecStart= flag that inserts the configured user's shell as first
   word in the command line. (maybe use character '.'). Usecase: tool such as
   uid0 can use that to spawn the target user's default shell.
@@ -147,6 +152,12 @@ Features:
 * use udev rule networkd ownership property to take ownership of network
   interfaces nspawn creates
 
+* support encrypted credentials in user context too. This is complicated by the
+  fact that the user does not have access to the TPM nor the system
+  credential. Implementation idea: extend the systemd-creds Varlink interface
+  to allow this: user must supply some per-user secret, that we'll include in
+  the encryption key.
+
 * 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
@@ -306,7 +317,6 @@ Features:
   - coredumpcl
   - systemd-bless-boot
   - systemd-measure
-  - systemd-creds (allowing clients to encrypt credentials locally)
   - systemd-cryptenroll (to allow UIs to enroll FIDO2 keys and such)
   - systemd-dissect
   - systemd-sysupdate
@@ -314,9 +324,6 @@ Features:
   - systemd-pcrlock (to allow fwupd to relax policy)
   - kernel-install
 
-* Varlink: add glue code to allow varlink clients to be authenticated via
-  Polkit by passing client pidfd over.
-
 * in the service manager, pick up ERRNO= + BUSERROR= + VARLINKERROR= error
   identifiers, and store them along with the exit status of a server and report
   via "systemctl status".
@@ -367,7 +374,6 @@ Features:
   - sd_bus_creds
   - unit_attach_pid_to_cgroup_via_bus()
   - cg_attach() – requires new kernel feature
-  - varlink_get_peer_pid()
 
 * ddi must be listed as block device fstype
 
@@ -473,6 +479,17 @@ Features:
   line, and then generate a mount unit for it using a udev generated symlink
   based on lo_file_name.
 
+* teach systemd-nspawn the boot assessment logic: hook up vpick's try counters
+  with success notifications from nspawn payloads. When this is enabled,
+  automatically support reverting back to older OS version images if newer ones
+  fail to boot.
+
+* implement new "systemd-fsrebind" tool that works like gpt-auto-generator but
+  looks at a root dir and then applies vpick on various dirs/images to pick a
+  root tree, a /usr/ tree, a /home/, a /srv/, a /var/ tree and so on. Dirs
+  could also be btrfs subvols (combine with btrfs auto-snapshort approach for
+  creating versions like these automatically).
+
 * remove tomoyo support, it's obsolete and unmaintained apparently
 
 * In .socket units, add ConnectStream=, ConnectDatagram=,
@@ -704,17 +721,6 @@ Features:
 * automatic boot assessment: add one more default success check that just waits
   for a bit after boot, and blesses the boot if the system stayed up that long.
 
-* implement concept of "versioned" resources inside a dir, and write a spec for
-  it. Make all tools in systemd, in particular
-  RootImage=/RootDirectory=/--image=/--directory= implement this. Idea:
-  directories ending in ".v/" indicate a directory with versioned resources in
-  them. Versioned resources inside a .v dir are always named in the pattern
-  <prefix>_<version>[+<tries-left>[-<tries-done>]].<suffix>
-
-* add support for using this .v/ logic on the root fs itself: in the initrd,
-  after mounting the rootfs, look for root-<arch>.v/ in the root fs, and then
-  apply the logic, moving the switch root logic there.
-
 * systemd-repart: add support for generating ISO9660 images
 
 * systemd-repart: in addition to the existing "factory reset" mode (which
@@ -823,10 +829,6 @@ Features:
   would just use the same public key specified with --public-key= (or the one
   automatically derived from --private-key=).
 
-* push people to use ".sysext.raw" as suffix for sysext DDIs (DDI =
-  discoverable disk images, i.e. the new name for gpt disk images following the
-  discoverable disk spec). [Also: just ".sysext/" for directory-based sysext]
-
 * Add "purpose" flag to partition flags in discoverable partition spec that
   indicate if partition is intended for sysext, for portable service, for
   booting and so on. Then, when dissecting DDI allow specifying a purpose to
@@ -920,8 +922,6 @@ Features:
   should probably also one you can use to get a remote attestation quote.
 
 * Process credentials in:
-  • networkd/udevd: add a way to define additional .link, .network, .netdev files
-    via the credentials logic.
   • crypttab-generator: allow defining additional crypttab-like volumes via
     credentials (similar: verity-generator, integrity-generator). Use
     fstab-generator logic as inspiration.
@@ -1170,26 +1170,6 @@ Features:
   passwords, not just the first. i.e. if there are multiple defined, prefer
   unlocked over locked and prefer non-empty over empty.
 
-* maybe add a tool inspired by the GPT auto discovery spec that runs in the
-  initrd and rearranges the rootfs hierarchy via bind mounts, if
-  enabled. Specifically in some top-level dir /@auto/ it will look for
-  dirs/symlinks/subvolumes that are named after their purpose, and optionally
-  encode a version as well as assessment counters, and then mount them into the
-  file system tree to boot into, similar to how we do that for the gpt auto
-  logic. Maybe then bind mount the original root into /.superior or something
-  like that (so that update tools can look there). Further discussion in this
-  thread:
-  https://lists.freedesktop.org/archives/systemd-devel/2021-November/047059.html
-  The GPT dissection logic should automatically enable this tool whenever we
-  detect a specially marked root fs (i.e introduce a new generic root gpt type
-  for this, that is arch independent). The also implement this in the image
-  dissection logic, so that nspawn/RootImage= and so on grok it. Maybe make
-  generic enough so that it can also work for ostrees arrangements.
-
-* if a path ending in ".auto.d/" is set for RootDirectory=/RootImage= then do a
-  strverscmp() of everything inside that dir and use that. i.e. implement very
-  simple version control. Also use this in systemd-nspawn --image= and so on.
-
 * homed: while a home dir is not activated generate slightly different NSS
   records for it, that reports the home dir as "/" and the shell as some binary
   provided by us. Then, when an SSH login happens and SSH permits it our binary
@@ -1340,8 +1320,9 @@ Features:
   - acquire + decrypt creds from pkcs11?
   - make systemd-cryptsetup acquire pw via creds logic
   - make PAMName= acquire pw via creds logic
-  - make macsec/wireguard code in networkd read key via creds logic
-  - make gatwayd/remote read key via creds logic
+  - make macsec code in networkd read key via creds logic (copy logic from
+    wireguard)
+  - make gatewayd/remote read key via creds logic
   - add sd_notify() command for flushing out creds not needed anymore
   - make user manager instances create and use a user-specific key (the one in
     /var/lib is root-only) and add --user switch to systemd-creds to use it
@@ -1848,8 +1829,6 @@ Features:
 
 * man: the documentation of Restart= currently is very misleading and suggests the tools from ExecStartPre= might get restarted.
 
-* load .d/*.conf dropins for device units
-
 * There's currently no way to cancel fsck (used to be possible via C-c or c on the console)
 
 * add option to sockets to avoid activation. Instead just drop packets/connections, see http://cyberelk.net/tim/2012/02/15/portreserve-systemd-solution/
@@ -2344,14 +2323,10 @@ Features:
 * systemctl:
   - add systemctl switch to dump transaction without executing it
   - Add a verbose mode to "systemctl start" and friends that explains what is being done or not done
-  - "systemctl disable" on a static unit prints no message and does
-    nothing. "systemctl enable" does nothing, and gives a bad message
-    about it. Should fix both to print nice actionable messages.
   - print nice message from systemctl --failed if there are no entries shown, and hook that into ExecStartPre of rescue.service/emergency.service
   - add new command to systemctl: "systemctl system-reexec" which reexecs as many daemons as virtually possible
   - systemctl enable: fail if target to alias into does not exist? maybe show how many units are enabled afterwards?
   - systemctl: "Journal has been rotated since unit was started." message is misleading
-  - systemctl status output should include list of triggering units and their status
 
 * introduce an option (or replacement) for "systemctl show" that outputs all
   properties as JSON, similar to busctl's new JSON output. In contrast to that
index 191cd626f52a49709111347d4e039810d532f2cb..1062d0ca53707a082654167509b53a5fef4b5603 100644 (file)
@@ -1,7 +1,36 @@
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
+@@
+/* Avoid running this transformation on the mfree function itself */
+position p : script:python() { p[0].current_element != "mfree" };
+expression e;
+@@
+- free@p(e);
+- return NULL;
++ return mfree(e);
+
 @@
 expression p;
 @@
 - free(p);
 - p = NULL;
 + p = mfree(p);
+
+@@
+expression p;
+@@
+- if (p)
+-          free(p);
++ free(p);
+
+@@
+expression p;
+@@
+- if (p)
+-          mfree(p);
++ free(p);
+
+@@
+expression p;
+@@
+- mfree(p);
++ free(p);
diff --git a/coccinelle/mfree_return.cocci b/coccinelle/mfree_return.cocci
deleted file mode 100644 (file)
index c2c4cb3..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-@@
-/* Avoid running this transformation on the mfree function itself */
-position p : script:python() { p[0].current_element != "mfree" };
-expression e;
-@@
-- free@p(e);
-- return NULL;
-+ return mfree(e);
index 819d5367738bf974fe7d9aa944536bfdeebf5b44..c96e6db85eaaf551db46bb0ae002c41855c20f0c 100644 (file)
@@ -249,6 +249,13 @@ All tools:
   devices sysfs path are actually backed by sysfs. Relaxing this verification
   is useful for testing purposes.
 
+* `$SYSTEMD_UDEV_EXTRA_TIMEOUT_SEC=` — Specifies an extra timespan that the
+  udev manager process waits for a worker process kills slow programs specified
+  by IMPORT{program}=, PROGRAM=, or RUN=, and finalizes the processing event.
+  If the worker process cannot finalize the event within the specified timespan,
+  the worker process is killed by the manager process. Defaults to 10 seconds,
+  maximum allowed is 5 hours.
+
 `udevadm` and `systemd-hwdb`:
 
 * `SYSTEMD_HWDB_UPDATE_BYPASS=` — If set to "1", execution of hwdb updates is skipped
@@ -603,3 +610,9 @@ SYSTEMD_HOME_DEBUG_SUFFIX=foo \
   latter two via the environment variable unless `systemd-storagetm` is invoked
   to expose a single device only, since those identifiers better should be kept
   unique.
+
+Tools using the Varlink protocol (such as `varlinkctl`) or sd-bus (such as
+`busctl`):
+
+* `$SYSTEMD_SSH` – the ssh binary to invoke when the `ssh:` transport is
+  used. May be a filename (which is searched for in `$PATH`) or absolute path.
index 1086940b78250225fc23a64adcc3a59942d7ddd6..0e39a6a5663b1de024cb2a369412b0761b55472f 100644 (file)
@@ -1021,6 +1021,10 @@ sensor:modalias:acpi:KIOX000A*:dmi:bvnAmericanMegatrendsInc.:bvr5.011:bd11/03/20
 sensor:modalias:acpi:KIOX000A*:dmi:*:svnTECLAST:pnX98PlusII:*
  ACCEL_MOUNT_MATRIX=1, 0, 0; 0, -1, 0; 0, 0, 1
 
+# Teclast X98 Pro
+sensor:modalias:acpi:BMA250E*:dmi:*:svnTECLAST:pnX98Pro:*
+ ACCEL_MOUNT_MATRIX=0, -1, 0; -1, 0, 0; 0, 0, 1
+
 #########################################
 # Thundersoft
 #########################################
index ac900ff1a6686bb4056356b1014bcd5c8a62874a..ea517f4ac4e08ecf3ae14f834078396af9dd8dce 100644 (file)
         <xi:include href="version-info.xml" xpointer="v217"/></listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><option>-T</option></term>
+        <term><option>--exclude-identifier=<replaceable>SYSLOG_IDENTIFIER</replaceable></option></term>
+
+        <listitem><para>Exclude messages for the specified syslog identifier
+        <replaceable>SYSLOG_IDENTIFIER</replaceable>.</para>
+
+        <para>This parameter can be specified multiple times.</para>
+
+        <xi:include href="version-info.xml" xpointer="v256"/></listitem>
+      </varlistentry>
+
       <varlistentry>
         <term><option>-p</option></term>
         <term><option>--priority=</option></term>
index c8abf0972a6dfc4f44db82c44562c18542a6200b..8a093c6e2982d71dd4d474973242904a8adeced6 100644 (file)
       <varlistentry>
         <term><command>show-session</command> <optional><replaceable>ID</replaceable>…</optional></term>
 
-        <listitem><para>Show properties of one or more sessions or the
-        manager itself. If no argument is specified, properties of the
-        manager will be shown. If a session ID is specified,
-        properties of the session are shown. By default, empty
-        properties are suppressed. Use <option>--all</option> to show
-        those too. To select specific properties to show, use
-        <option>--property=</option>. This command is intended to be
-        used whenever computer-parsable output is required. Use
-        <command>session-status</command> if you are looking for
-        formatted human-readable output.</para>
+        <listitem><para>Show properties of one or more sessions or the manager itself. If no argument is
+        specified, properties of the manager will be shown. If a session ID is specified, properties of
+        the session are shown. Specially, if the given ID is <literal>self</literal>, the session to which
+        the <command>loginctl</command> process belongs is used. If <literal>auto</literal>, the current
+        session is used as with <literal>self</literal> if exists, and falls back to the current user's
+        graphical session. By default, empty properties are suppressed. Use <option>--all</option> to show
+        those too. To select specific properties to show, use <option>--property=</option>. This command
+        is intended to be used whenever computer-parsable output is required. Use <command>session-status</command>
+        if you are looking for formatted human-readable output.</para>
 
         <xi:include href="version-info.xml" xpointer="v233"/></listitem>
       </varlistentry>
       <varlistentry>
         <term><option>--kill-whom=</option></term>
 
-        <listitem><para>When used with
-        <command>kill-session</command>, choose which processes to
-        kill. Must be one of <option>leader</option>, or
-        <option>all</option> to select whether to kill only the leader
-        process of the session or all processes of the session. If
-        omitted, defaults to <option>all</option>.</para>
+        <listitem><para>When used with <command>kill-session</command>, choose which processes to kill.
+        Takes one of <literal>leader</literal> or <literal>all</literal>, to select whether to kill only
+        the leader process of the session or all processes of the session. If omitted, defaults to
+        <option>all</option>.</para>
 
         <xi:include href="version-info.xml" xpointer="v252"/></listitem>
       </varlistentry>
index ff19ba91fa01b7af3468f6b35d39b988d085050e..d74c9b410fd3973aa4911780cd534b79ac1afcfc 100644 (file)
         <term><varname>StopIdleSessionSec=</varname></term>
 
         <listitem><para>Specifies a timeout in seconds, or a time span value after which
-        <filename>systemd-logind</filename> checks the idle state of all sessions. Every session that is idle for
-        longer then the timeout will be stopped. Defaults to <literal>infinity</literal>
-        (<filename>systemd-logind</filename> is not checking the idle state of sessions). For details about the syntax
-        of time spans, see
+        <filename>systemd-logind</filename> checks the idle state of all sessions. Every session that is idle
+        for longer than the timeout will be stopped. Note that this option doesn't apply to
+        <literal>greeter</literal> or <literal>lock-screen</literal> sessions. Defaults to
+        <literal>infinity</literal> (<filename>systemd-logind</filename> is not checking the idle state
+        of sessions). For details about the syntax of time spans, see
         <citerefentry><refentrytitle>systemd.time</refentrytitle><manvolnum>7</manvolnum></citerefentry>.
         </para>
 
index 577e64dcd7edd5565b37f7d40e5c2e36ca7242d5..61d9831ca6b8a5aaaccaa8a6c7b5fe4165aa3149 100644 (file)
@@ -99,6 +99,8 @@ node /org/freedesktop/hostname1 {
       readonly ay MachineID = [...];
       @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
       readonly ay BootID = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly u VSockCID = ...;
   };
   interface org.freedesktop.DBus.Peer { ... };
   interface org.freedesktop.DBus.Introspectable { ... };
@@ -120,10 +122,6 @@ node /org/freedesktop/hostname1 {
 
     <!--property FirmwareDate is not documented!-->
 
-    <!--property MachineID is not documented!-->
-
-    <!--property BootID is not documented!-->
-
     <!--Autogenerated cross-references for systemd.directives, do not edit-->
 
     <variablelist class="dbus-interface" generated="True" extra-ref="org.freedesktop.hostname1"/>
@@ -196,6 +194,8 @@ node /org/freedesktop/hostname1 {
 
     <variablelist class="dbus-property" generated="True" extra-ref="BootID"/>
 
+    <variablelist class="dbus-property" generated="True" extra-ref="VSockCID"/>
+
     <!--End of Autogenerated section-->
 
     <para>Whenever the hostname or other metadata is changed via the daemon,
@@ -287,6 +287,18 @@ node /org/freedesktop/hostname1 {
     purpose of those properties is to allow remote clients to access this information over D-Bus. Local
     clients can access the information directly.</para>
 
+    <para><varname>MachineID</varname> expose the 128bit machine ID, see
+    <citerefentry><refentrytitle>machine-id</refentrytitle><manvolnum>5</manvolnum></citerefentry> for
+    details.</para>
+
+    <para><varname>BootID</varname> expose the 128bit boot ID, as per
+    <filename>/proc/sys/kernel/random/boot_id</filename>.</para>
+
+    <para><varname>VSockCID</varname> exposes the system's local <constant>AF_VSOCK</constant> CID (Context
+    Identifier, i.e. address) for the system, if one is available in the virtual machine environment. Set to
+    <constant>UINT32_MAX</constant> otherwise. See <citerefentry project="man-pages"><refentrytitle>vsock</refentrytitle><manvolnum>7</manvolnum></citerefentry> for
+    details.</para>
+
     <refsect2>
       <title>Methods</title>
 
@@ -440,8 +452,8 @@ node /org/freedesktop/hostname1 {
       <para><varname>OperatingSystemSupportEnd</varname>,
       <varname>FirmwareVendor</varname>, and
       <varname>FirmwareDate</varname> were added in version 253.</para>
-      <para><varname>MachineID</varname>, and
-      <varname>BootID</varname> were added in version 256.</para>
+      <para><varname>MachineID</varname>, <varname>BootID</varname> and
+      <varname>VSockCID</varname> were added in version 256.</para>
     </refsect2>
   </refsect1>
 </refentry>
index 1bdff502ca3ae971dc7a4364a527b0d289a04f83..878bd0d99d95a428529138b70b7a006b886ff0cc 100644 (file)
@@ -2806,6 +2806,10 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
       @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
       readonly t MemoryAvailable = ...;
       @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t EffectiveMemoryMax = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t EffectiveMemoryHigh = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
       readonly t CPUUsageNSec = ...;
       @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
       readonly ay EffectiveCPUs = [...];
@@ -2814,6 +2818,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
       @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
       readonly t TasksCurrent = ...;
       @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t EffectiveTasksMax = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
       readonly t IPIngressBytes = ...;
       @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
       readonly t IPIngressPackets = ...;
@@ -3439,6 +3445,10 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
 
     <!--property MemoryZSwapCurrent is not documented!-->
 
+    <!--property EffectiveMemoryMax is not documented!-->
+
+    <!--property EffectiveMemoryHigh is not documented!-->
+
     <!--property CPUUsageNSec is not documented!-->
 
     <!--property EffectiveCPUs is not documented!-->
@@ -3447,6 +3457,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
 
     <!--property TasksCurrent is not documented!-->
 
+    <!--property EffectiveTasksMax is not documented!-->
+
     <!--property IPIngressBytes is not documented!-->
 
     <!--property IPIngressPackets is not documented!-->
@@ -4081,6 +4093,10 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
 
     <variablelist class="dbus-property" generated="True" extra-ref="MemoryAvailable"/>
 
+    <variablelist class="dbus-property" generated="True" extra-ref="EffectiveMemoryMax"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="EffectiveMemoryHigh"/>
+
     <variablelist class="dbus-property" generated="True" extra-ref="CPUUsageNSec"/>
 
     <variablelist class="dbus-property" generated="True" extra-ref="EffectiveCPUs"/>
@@ -4089,6 +4105,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
 
     <variablelist class="dbus-property" generated="True" extra-ref="TasksCurrent"/>
 
+    <variablelist class="dbus-property" generated="True" extra-ref="EffectiveTasksMax"/>
+
     <variablelist class="dbus-property" generated="True" extra-ref="IPIngressBytes"/>
 
     <variablelist class="dbus-property" generated="True" extra-ref="IPIngressPackets"/>
@@ -4885,6 +4903,10 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket {
       @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
       readonly t MemoryAvailable = ...;
       @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t EffectiveMemoryMax = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t EffectiveMemoryHigh = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
       readonly t CPUUsageNSec = ...;
       @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
       readonly ay EffectiveCPUs = [...];
@@ -4893,6 +4915,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket {
       @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
       readonly t TasksCurrent = ...;
       @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t EffectiveTasksMax = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
       readonly t IPIngressBytes = ...;
       @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
       readonly t IPIngressPackets = ...;
@@ -5528,6 +5552,10 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket {
 
     <!--property MemoryZSwapCurrent is not documented!-->
 
+    <!--property EffectiveMemoryMax is not documented!-->
+
+    <!--property EffectiveMemoryHigh is not documented!-->
+
     <!--property CPUUsageNSec is not documented!-->
 
     <!--property EffectiveCPUs is not documented!-->
@@ -5536,6 +5564,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket {
 
     <!--property TasksCurrent is not documented!-->
 
+    <!--property EffectiveTasksMax is not documented!-->
+
     <!--property IPIngressBytes is not documented!-->
 
     <!--property IPIngressPackets is not documented!-->
@@ -6152,6 +6182,10 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket {
 
     <variablelist class="dbus-property" generated="True" extra-ref="MemoryAvailable"/>
 
+    <variablelist class="dbus-property" generated="True" extra-ref="EffectiveMemoryMax"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="EffectiveMemoryHigh"/>
+
     <variablelist class="dbus-property" generated="True" extra-ref="CPUUsageNSec"/>
 
     <variablelist class="dbus-property" generated="True" extra-ref="EffectiveCPUs"/>
@@ -6160,6 +6194,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket {
 
     <variablelist class="dbus-property" generated="True" extra-ref="TasksCurrent"/>
 
+    <variablelist class="dbus-property" generated="True" extra-ref="EffectiveTasksMax"/>
+
     <variablelist class="dbus-property" generated="True" extra-ref="IPIngressBytes"/>
 
     <variablelist class="dbus-property" generated="True" extra-ref="IPIngressPackets"/>
@@ -6830,6 +6866,10 @@ node /org/freedesktop/systemd1/unit/home_2emount {
       @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
       readonly t MemoryAvailable = ...;
       @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t EffectiveMemoryMax = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t EffectiveMemoryHigh = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
       readonly t CPUUsageNSec = ...;
       @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
       readonly ay EffectiveCPUs = [...];
@@ -6838,6 +6878,8 @@ node /org/freedesktop/systemd1/unit/home_2emount {
       @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
       readonly t TasksCurrent = ...;
       @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t EffectiveTasksMax = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
       readonly t IPIngressBytes = ...;
       @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
       readonly t IPIngressPackets = ...;
@@ -7401,6 +7443,10 @@ node /org/freedesktop/systemd1/unit/home_2emount {
 
     <!--property MemoryZSwapCurrent is not documented!-->
 
+    <!--property EffectiveMemoryMax is not documented!-->
+
+    <!--property EffectiveMemoryHigh is not documented!-->
+
     <!--property CPUUsageNSec is not documented!-->
 
     <!--property EffectiveCPUs is not documented!-->
@@ -7409,6 +7455,8 @@ node /org/freedesktop/systemd1/unit/home_2emount {
 
     <!--property TasksCurrent is not documented!-->
 
+    <!--property EffectiveTasksMax is not documented!-->
+
     <!--property IPIngressBytes is not documented!-->
 
     <!--property IPIngressPackets is not documented!-->
@@ -7939,6 +7987,10 @@ node /org/freedesktop/systemd1/unit/home_2emount {
 
     <variablelist class="dbus-property" generated="True" extra-ref="MemoryAvailable"/>
 
+    <variablelist class="dbus-property" generated="True" extra-ref="EffectiveMemoryMax"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="EffectiveMemoryHigh"/>
+
     <variablelist class="dbus-property" generated="True" extra-ref="CPUUsageNSec"/>
 
     <variablelist class="dbus-property" generated="True" extra-ref="EffectiveCPUs"/>
@@ -7947,6 +7999,8 @@ node /org/freedesktop/systemd1/unit/home_2emount {
 
     <variablelist class="dbus-property" generated="True" extra-ref="TasksCurrent"/>
 
+    <variablelist class="dbus-property" generated="True" extra-ref="EffectiveTasksMax"/>
+
     <variablelist class="dbus-property" generated="True" extra-ref="IPIngressBytes"/>
 
     <variablelist class="dbus-property" generated="True" extra-ref="IPIngressPackets"/>
@@ -8740,6 +8794,10 @@ node /org/freedesktop/systemd1/unit/dev_2dsda3_2eswap {
       @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
       readonly t MemoryAvailable = ...;
       @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t EffectiveMemoryMax = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t EffectiveMemoryHigh = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
       readonly t CPUUsageNSec = ...;
       @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
       readonly ay EffectiveCPUs = [...];
@@ -8748,6 +8806,8 @@ node /org/freedesktop/systemd1/unit/dev_2dsda3_2eswap {
       @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
       readonly t TasksCurrent = ...;
       @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t EffectiveTasksMax = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
       readonly t IPIngressBytes = ...;
       @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
       readonly t IPIngressPackets = ...;
@@ -9297,6 +9357,10 @@ node /org/freedesktop/systemd1/unit/dev_2dsda3_2eswap {
 
     <!--property MemoryZSwapCurrent is not documented!-->
 
+    <!--property EffectiveMemoryMax is not documented!-->
+
+    <!--property EffectiveMemoryHigh is not documented!-->
+
     <!--property CPUUsageNSec is not documented!-->
 
     <!--property EffectiveCPUs is not documented!-->
@@ -9305,6 +9369,8 @@ node /org/freedesktop/systemd1/unit/dev_2dsda3_2eswap {
 
     <!--property TasksCurrent is not documented!-->
 
+    <!--property EffectiveTasksMax is not documented!-->
+
     <!--property IPIngressBytes is not documented!-->
 
     <!--property IPIngressPackets is not documented!-->
@@ -9821,6 +9887,10 @@ node /org/freedesktop/systemd1/unit/dev_2dsda3_2eswap {
 
     <variablelist class="dbus-property" generated="True" extra-ref="MemoryAvailable"/>
 
+    <variablelist class="dbus-property" generated="True" extra-ref="EffectiveMemoryMax"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="EffectiveMemoryHigh"/>
+
     <variablelist class="dbus-property" generated="True" extra-ref="CPUUsageNSec"/>
 
     <variablelist class="dbus-property" generated="True" extra-ref="EffectiveCPUs"/>
@@ -9829,6 +9899,8 @@ node /org/freedesktop/systemd1/unit/dev_2dsda3_2eswap {
 
     <variablelist class="dbus-property" generated="True" extra-ref="TasksCurrent"/>
 
+    <variablelist class="dbus-property" generated="True" extra-ref="EffectiveTasksMax"/>
+
     <variablelist class="dbus-property" generated="True" extra-ref="IPIngressBytes"/>
 
     <variablelist class="dbus-property" generated="True" extra-ref="IPIngressPackets"/>
@@ -10481,6 +10553,10 @@ node /org/freedesktop/systemd1/unit/system_2eslice {
       @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
       readonly t MemoryAvailable = ...;
       @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t EffectiveMemoryMax = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t EffectiveMemoryHigh = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
       readonly t CPUUsageNSec = ...;
       @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
       readonly ay EffectiveCPUs = [...];
@@ -10489,6 +10565,8 @@ node /org/freedesktop/systemd1/unit/system_2eslice {
       @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
       readonly t TasksCurrent = ...;
       @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t EffectiveTasksMax = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
       readonly t IPIngressBytes = ...;
       @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
       readonly t IPIngressPackets = ...;
@@ -10664,6 +10742,10 @@ node /org/freedesktop/systemd1/unit/system_2eslice {
 
     <!--property MemoryZSwapCurrent is not documented!-->
 
+    <!--property EffectiveMemoryMax is not documented!-->
+
+    <!--property EffectiveMemoryHigh is not documented!-->
+
     <!--property CPUUsageNSec is not documented!-->
 
     <!--property EffectiveCPUs is not documented!-->
@@ -10672,6 +10754,8 @@ node /org/freedesktop/systemd1/unit/system_2eslice {
 
     <!--property TasksCurrent is not documented!-->
 
+    <!--property EffectiveTasksMax is not documented!-->
+
     <!--property IPIngressBytes is not documented!-->
 
     <!--property IPIngressPackets is not documented!-->
@@ -10852,6 +10936,10 @@ node /org/freedesktop/systemd1/unit/system_2eslice {
 
     <variablelist class="dbus-property" generated="True" extra-ref="MemoryAvailable"/>
 
+    <variablelist class="dbus-property" generated="True" extra-ref="EffectiveMemoryMax"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="EffectiveMemoryHigh"/>
+
     <variablelist class="dbus-property" generated="True" extra-ref="CPUUsageNSec"/>
 
     <variablelist class="dbus-property" generated="True" extra-ref="EffectiveCPUs"/>
@@ -10860,6 +10948,8 @@ node /org/freedesktop/systemd1/unit/system_2eslice {
 
     <variablelist class="dbus-property" generated="True" extra-ref="TasksCurrent"/>
 
+    <variablelist class="dbus-property" generated="True" extra-ref="EffectiveTasksMax"/>
+
     <variablelist class="dbus-property" generated="True" extra-ref="IPIngressBytes"/>
 
     <variablelist class="dbus-property" generated="True" extra-ref="IPIngressPackets"/>
@@ -11066,6 +11156,10 @@ node /org/freedesktop/systemd1/unit/session_2d1_2escope {
       @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
       readonly t MemoryAvailable = ...;
       @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t EffectiveMemoryMax = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t EffectiveMemoryHigh = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
       readonly t CPUUsageNSec = ...;
       @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
       readonly ay EffectiveCPUs = [...];
@@ -11074,6 +11168,8 @@ node /org/freedesktop/systemd1/unit/session_2d1_2escope {
       @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
       readonly t TasksCurrent = ...;
       @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t EffectiveTasksMax = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
       readonly t IPIngressBytes = ...;
       @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
       readonly t IPIngressPackets = ...;
@@ -11269,6 +11365,10 @@ node /org/freedesktop/systemd1/unit/session_2d1_2escope {
 
     <!--property MemoryZSwapCurrent is not documented!-->
 
+    <!--property EffectiveMemoryMax is not documented!-->
+
+    <!--property EffectiveMemoryHigh is not documented!-->
+
     <!--property CPUUsageNSec is not documented!-->
 
     <!--property EffectiveCPUs is not documented!-->
@@ -11277,6 +11377,8 @@ node /org/freedesktop/systemd1/unit/session_2d1_2escope {
 
     <!--property TasksCurrent is not documented!-->
 
+    <!--property EffectiveTasksMax is not documented!-->
+
     <!--property IPIngressBytes is not documented!-->
 
     <!--property IPIngressPackets is not documented!-->
@@ -11487,6 +11589,10 @@ node /org/freedesktop/systemd1/unit/session_2d1_2escope {
 
     <variablelist class="dbus-property" generated="True" extra-ref="MemoryAvailable"/>
 
+    <variablelist class="dbus-property" generated="True" extra-ref="EffectiveMemoryMax"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="EffectiveMemoryHigh"/>
+
     <variablelist class="dbus-property" generated="True" extra-ref="CPUUsageNSec"/>
 
     <variablelist class="dbus-property" generated="True" extra-ref="EffectiveCPUs"/>
@@ -11495,6 +11601,8 @@ node /org/freedesktop/systemd1/unit/session_2d1_2escope {
 
     <variablelist class="dbus-property" generated="True" extra-ref="TasksCurrent"/>
 
+    <variablelist class="dbus-property" generated="True" extra-ref="EffectiveTasksMax"/>
+
     <variablelist class="dbus-property" generated="True" extra-ref="IPIngressBytes"/>
 
     <variablelist class="dbus-property" generated="True" extra-ref="IPIngressPackets"/>
@@ -11888,6 +11996,9 @@ $ gdbus introspect --system --dest org.freedesktop.systemd1 \
       <varname>MemorySwapCurrent</varname>,
       <varname>MemorySwapPeak</varname>, and
       <varname>MemoryZSwapCurrent</varname> were added in version 255.</para>
+      <para><varname>EffectiveMemoryHigh</varname>,
+      <varname>EffectiveMemoryMax</varname>,
+      <varname>EffectiveTasksMax</varname> were added in version 256.</para>
     </refsect2>
     <refsect2>
       <title>Socket Unit Objects</title>
@@ -11919,6 +12030,9 @@ $ gdbus introspect --system --dest org.freedesktop.systemd1 \
       <varname>MemorySwapCurrent</varname>,
       <varname>MemorySwapPeak</varname>, and
       <varname>MemoryZSwapCurrent</varname> were added in version 255.</para>
+      <para><varname>EffectiveMemoryHigh</varname>,
+      <varname>EffectiveMemoryMax</varname>,
+      <varname>EffectiveTasksMax</varname> were added in version 256.</para>
     </refsect2>
     <refsect2>
       <title>Mount Unit Objects</title>
@@ -11948,6 +12062,9 @@ $ gdbus introspect --system --dest org.freedesktop.systemd1 \
       <varname>MemorySwapCurrent</varname>,
       <varname>MemorySwapPeak</varname>, and
       <varname>MemoryZSwapCurrent</varname> were added in version 255.</para>
+      <para><varname>EffectiveMemoryHigh</varname>,
+      <varname>EffectiveMemoryMax</varname>,
+      <varname>EffectiveTasksMax</varname> were added in version 256.</para>
     </refsect2>
     <refsect2>
       <title>Swap Unit Objects</title>
@@ -11977,6 +12094,9 @@ $ gdbus introspect --system --dest org.freedesktop.systemd1 \
       <varname>MemorySwapCurrent</varname>,
       <varname>MemorySwapPeak</varname>, and
       <varname>MemoryZSwapCurrent</varname> were added in version 255.</para>
+      <para><varname>EffectiveMemoryHigh</varname>,
+      <varname>EffectiveMemoryMax</varname>,
+      <varname>EffectiveTasksMax</varname> were added in version 256.</para>
     </refsect2>
     <refsect2>
       <title>Slice Unit Objects</title>
@@ -11997,6 +12117,9 @@ $ gdbus introspect --system --dest org.freedesktop.systemd1 \
       <varname>MemorySwapCurrent</varname>,
       <varname>MemorySwapPeak</varname>, and
       <varname>MemoryZSwapCurrent</varname> were added in version 255.</para>
+      <para><varname>EffectiveMemoryHigh</varname>,
+      <varname>EffectiveMemoryMax</varname>,
+      <varname>EffectiveTasksMax</varname> were added in version 256.</para>
     </refsect2>
     <refsect2>
       <title>Scope Unit Objects</title>
@@ -12018,6 +12141,9 @@ $ gdbus introspect --system --dest org.freedesktop.systemd1 \
       <varname>MemorySwapCurrent</varname>,
       <varname>MemorySwapPeak</varname>, and
       <varname>MemoryZSwapCurrent</varname> were added in version 255.</para>
+      <para><varname>EffectiveMemoryHigh</varname>,
+      <varname>EffectiveMemoryMax</varname>,
+      <varname>EffectiveTasksMax</varname> were added in version 256.</para>
     </refsect2>
     <refsect2>
       <title>Job Objects</title>
index c99f79eba8afcfedca4c8dae07d80b4bd2f6dbe7..3592b862f718d1633d7bbc3800aa285358e08469 100644 (file)
@@ -1131,6 +1131,7 @@ manpages = [
   'HAVE_LIBCRYPTSETUP'],
  ['systemd-vmspawn', '1', [], 'ENABLE_VMSPAWN'],
  ['systemd-volatile-root.service', '8', ['systemd-volatile-root'], ''],
+ ['systemd-vpick', '1', [], ''],
  ['systemd-xdg-autostart-generator', '8', [], 'ENABLE_XDG_AUTOSTART'],
  ['systemd', '1', ['init'], ''],
  ['systemd.automount', '5', [], ''],
@@ -1165,6 +1166,7 @@ manpages = [
  ['systemd.time', '7', [], ''],
  ['systemd.timer', '5', [], ''],
  ['systemd.unit', '5', [], ''],
+ ['systemd.v', '7', [], ''],
  ['sysupdate.d', '5', [], 'ENABLE_SYSUPDATE'],
  ['sysusers.d', '5', [], 'ENABLE_SYSUSERS'],
  ['telinit', '8', [], 'HAVE_SYSV_COMPAT'],
index 41c0e45df63198ee5fd9aa4ec2b11bcc94ab9997..0b5e8ba974fba209a8a9d31862ecb8a54c251ddc 100644 (file)
       <command>systemd-analyze</command>
       <arg choice="opt" rep="repeat">OPTIONS</arg>
       <arg choice="plain">verify</arg>
-      <arg choice="opt" rep="repeat"><replaceable>FILE</replaceable></arg>
+      <arg choice="plain" rep="repeat"><replaceable>FILE</replaceable></arg>
     </cmdsynopsis>
     <cmdsynopsis>
       <command>systemd-analyze</command>
       <arg choice="opt" rep="repeat">OPTIONS</arg>
       <arg choice="plain">security</arg>
-      <arg choice="plain" rep="repeat"><replaceable>UNIT</replaceable></arg>
+      <arg choice="opt" rep="repeat"><replaceable>UNIT</replaceable></arg>
     </cmdsynopsis>
     <cmdsynopsis>
       <command>systemd-analyze</command>
       <command>systemd-analyze</command>
       <arg choice="opt" rep="repeat">OPTIONS</arg>
       <arg choice="plain">fdstore</arg>
-      <arg choice="opt" rep="repeat"><replaceable>UNIT</replaceable></arg>
+      <arg choice="plain" rep="repeat"><replaceable>UNIT</replaceable></arg>
     </cmdsynopsis>
     <cmdsynopsis>
       <command>systemd-analyze</command>
@@ -839,7 +839,7 @@ alias.service:7: Unknown key name 'MysteryKey' in section 'Service', ignoring.
     </refsect2>
 
     <refsect2>
-      <title><command>systemd-analyze fdstore <optional><replaceable>UNIT</replaceable>...</optional></command></title>
+      <title><command>systemd-analyze fdstore <replaceable>UNIT</replaceable>...</command></title>
 
       <para>Lists the current contents of the specified service unit's file descriptor store. This shows
       names, inode types, device numbers, inode numbers, paths and open modes of the open file
@@ -868,7 +868,7 @@ stored sock 0:8   4213190 -      socket:[4213190] ro
     </refsect2>
 
     <refsect2>
-      <title><command>systemd-analyze image-policy <optional><replaceable>POLICY</replaceable>…</optional></command></title>
+      <title><command>systemd-analyze image-policy <replaceable>POLICY</replaceable>…</command></title>
 
       <para>This command analyzes the specified image policy string, as per
       <citerefentry><refentrytitle>systemd.image-policy</refentrytitle><manvolnum>7</manvolnum></citerefentry>. The
@@ -1513,6 +1513,7 @@ x86-64      native</programlisting>
       <xi:include href="user-system-options.xml" xpointer="machine" />
 
       <varlistentry>
+        <term><option>-q</option></term>
         <term><option>--quiet</option></term>
 
         <listitem><para>Suppress hints and other non-essential output.</para>
index 60f38f6bd997263bf93a9f3299a43734017ba8e5..5f52540e84e8728a0dd73ed470e2f0b1186f1e67 100644 (file)
 
         <listitem><para>When specified with the <command>encrypt</command> command controls the
         encryption/signature key to use. Takes one of <literal>host</literal>, <literal>tpm2</literal>,
-        <literal>host+tpm2</literal>, <literal>tpm2-absent</literal>, <literal>auto</literal>,
+        <literal>host+tpm2</literal>, <literal>null</literal>, <literal>auto</literal>,
         <literal>auto-initrd</literal>. See above for details on the three key types. If set to
         <literal>auto</literal> (which is the default) the TPM2 key is used if a TPM2 device is found and not
         running in a container. The host key is used if <filename>/var/lib/systemd/</filename> is on
         chip and the OS installation, and both need to be available to decrypt the credential again. If
         <literal>auto</literal> is selected but neither TPM2 is available (or running in container) nor
         <filename>/var/lib/systemd/</filename> is on persistent media, encryption will fail. If set to
-        <literal>tpm2-absent</literal> a fixed zero length key is used (thus, in this mode no confidentiality
+        <literal>null</literal> a fixed zero length key is used (thus, in this mode no confidentiality
         nor authenticity are provided!). This logic is useful to cover for systems that lack a TPM2 chip but
         where credentials shall be generated. Note that decryption of such credentials is refused on systems
         that have a TPM2 chip and where UEFI SecureBoot is enabled (this is done so that such a locked down
         system cannot be tricked into loading a credential generated this way that lacks authentication
         information). If set to <literal>auto-initrd</literal> a TPM2 key is used if a TPM2 is found. If not
-        a fixed zero length key is used, equivalent to <literal>tpm2-absent</literal> mode. This option is
+        a fixed zero length key is used, equivalent to <literal>null</literal> mode. This option is
         particularly useful to generate credentials files that are encrypted/authenticated against TPM2 where
         available but still work on systems lacking support for this.</para>
 
index a308a2ebdc7ae96f2bb54e8d099f0cf949a1b26a..041337ab8af92a1a89a7344f3a921af4db5e030a 100644 (file)
       <varlistentry>
         <term><option>--pkcs11-token-uri=</option><replaceable>URI</replaceable></term>
 
-        <listitem><para>Enroll a PKCS#11 security token or smartcard (e.g. a YubiKey). Expects a PKCS#11
-        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.</para>
+        <listitem><para>Enroll a PKCS#11 security token or smartcard (e.g. a YubiKey). Expects a PKCS#11 URI
+        that allows to find an X.509 certificate on the token. The URI must also be suitable to find
+        a related private key after changing the type of object in it. Alternatively the special value
+        <literal>auto</literal> may be specified, in order to automatically determine the suitable URI if
+        a single security token containing a single key pair is plugged in. The special value
+        <literal>list</literal> may be used to 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
index 9993ac77828784f217b515264936632f4324ac4f..2239294145aa2d89780ca679af3ac80ae045fb48 100644 (file)
 
           <row>
             <entry><varname>apple</varname></entry>
-            <entry><ulink url="https://developer.apple.com/documentation/virtualization">Apple Virtualization.framework</ulink></entry>
+            <entry><ulink url="https://developer.apple.com/documentation/virtualization">Apple virtualization framework</ulink></entry>
           </row>
 
           <row>
             <entry><ulink url="https://www.lockheedmartin.com/en-us/products/Hardened-Security-for-Intel-Processors.html">LMHS SRE hypervisor</ulink></entry>
           </row>
 
+          <row>
+            <entry><varname>google</varname></entry>
+            <entry><ulink url="https://cloud.google.com/compute">Google Compute Engine</ulink></entry>
+          </row>
+
           <row>
             <entry valign="top" morerows="9">Container</entry>
             <entry><varname>openvz</varname></entry>
index b238a9f7d06ca89338a9e9c9524c3b976aeb96bf..85166f23d352ce6aace565e40b2fbc8f92b0d5cf 100644 (file)
     mounted directly by <command>mount</command> and <citerefentry
     project='man-pages'><refentrytitle>fstab</refentrytitle><manvolnum>5</manvolnum></citerefentry>. For
     details see below.</para>
+
+    <para>In place of the image path a <literal>.v/</literal> versioned directory may be specified, see
+    <citerefentry><refentrytitle>systemd.v</refentrytitle><manvolnum>7</manvolnum></citerefentry> for
+    details.</para>
   </refsect1>
 
   <refsect1>
       <member><citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
       <member><citerefentry><refentrytitle>systemd-nspawn</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
       <member><citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry></member>
+      <member><citerefentry><refentrytitle>systemd.v</refentrytitle><manvolnum>7</manvolnum></citerefentry></member>
       <member><ulink url="https://uapi-group.org/specifications/specs/discoverable_partitions_specification">Discoverable Partitions Specification</ulink></member>
       <member><citerefentry project='man-pages'><refentrytitle>mount</refentrytitle><manvolnum>8</manvolnum></citerefentry></member>
       <member><citerefentry project='man-pages'><refentrytitle>umount</refentrytitle><manvolnum>8</manvolnum></citerefentry></member>
index 5682339560af6cd26ad48a14ae637334f5751f7b..1d498cbf1dd1e80013340d035ca6f9baea45466f 100644 (file)
     for option syntax and details.</para>
   </refsect1>
 
+  <refsect1>
+    <title>Credentials</title>
+
+    <para><command>systemd-network-generator</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>5</manvolnum></citerefentry> for
+    details). The following credentials are used when passed in:</para>
+
+    <variablelist class='system-credentials'>
+      <varlistentry>
+        <term><varname>network.netdev.*</varname></term>
+        <term><varname>network.link.*</varname></term>
+        <term><varname>network.network.*</varname></term>
+
+        <listitem><para>These credentials should contain valid
+        <citerefentry><refentrytitle>systemd.netdev</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
+        <citerefentry><refentrytitle>systemd.link</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
+        <citerefentry><refentrytitle>systemd.network</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+        configuration data. From each matching credential a separate file is created. Example: a passed
+        credential <filename>network.link.50-foobar</filename> will be copied into a configuration file
+        <filename>50-foobar.link</filename>.</para>
+
+        <para>Note that the resulting files are created world-readable, it's hence recommended to not include
+        secrets in these credentials, but supply them via separate credentials directly to
+        <filename>systemd-networkd.service</filename>.</para>
+
+        <xi:include href="version-info.xml" xpointer="v256"/></listitem>
+      </varlistentry>
+    </variablelist>
+
+    <para>Note that by default the <filename>systemd-network-generator.service</filename> unit file is set up
+    to inherit the these credentials from the service manager.</para>
+  </refsect1>
+
   <refsect1>
     <title>See Also</title>
     <para><simplelist type="inline">
index e721f0e5ab4acbf3bec7dfe324464db7807eb583..1a9fb09aaeae288588d2e6f8e0a5f44c3da6114c 100644 (file)
         <term><option>-D</option></term>
         <term><option>--directory=</option></term>
 
-        <listitem><para>Directory to use as file system root for the
-        container.</para>
+        <listitem><para>Directory to use as file system root for the container.</para>
 
-        <para>If neither <option>--directory=</option>, nor
-        <option>--image=</option> is specified the directory is
-        determined by searching for a directory named the same as the
-        machine name specified with <option>--machine=</option>. See
+        <para>If neither <option>--directory=</option>, nor <option>--image=</option> is specified the
+        directory is determined by searching for a directory named the same as the machine name specified
+        with <option>--machine=</option>. See
         <citerefentry><refentrytitle>machinectl</refentrytitle><manvolnum>1</manvolnum></citerefentry>
         section "Files and Directories" for the precise search path.</para>
 
-        <para>If neither <option>--directory=</option>,
-        <option>--image=</option>, nor <option>--machine=</option>
-        are specified, the current directory will
-        be used. May not be specified together with
-        <option>--image=</option>.</para></listitem>
+        <para>In place of the directory path a <literal>.v/</literal> versioned directory may be specified, see
+        <citerefentry><refentrytitle>systemd.v</refentrytitle><manvolnum>7</manvolnum></citerefentry> for
+        details.</para>
+
+        <para>If neither <option>--directory=</option>, <option>--image=</option>, nor
+        <option>--machine=</option> are specified, the current directory will be used. May not be specified
+        together with <option>--image=</option>.</para></listitem>
       </varlistentry>
 
       <varlistentry>
         <para>Any other partitions, such as foreign partitions or swap partitions are not mounted. May not be specified
         together with <option>--directory=</option>, <option>--template=</option>.</para>
 
+        <para>In place of the image path a <literal>.v/</literal> versioned directory may be specified, see
+        <citerefentry><refentrytitle>systemd.v</refentrytitle><manvolnum>7</manvolnum></citerefentry> for
+        details.</para>
+
         <xi:include href="version-info.xml" xpointer="v211"/></listitem>
       </varlistentry>
 
index 8486e75c87f4c74bc0fe1608d6ea67c2b369944d..6bf6694d2d7a2a9fa098f852afffee9b6b7abe78 100644 (file)
         </para></listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><option>--purge</option></term>
+        <listitem><para>If this option is passed, all files and directories created by a
+        <filename>tmpfiles.d/</filename> entry will be deleted.</para>
+
+        <xi:include href="version-info.xml" xpointer="v256"/></listitem>
+      </varlistentry>
+
       <varlistentry>
         <term><option>--user</option></term>
         <listitem><para>Execute "user" configuration, i.e. <filename>tmpfiles.d</filename>
diff --git a/man/systemd-vpick.xml b/man/systemd-vpick.xml
new file mode 100644 (file)
index 0000000..95f946a
--- /dev/null
@@ -0,0 +1,198 @@
+<?xml version='1.0'?> <!--*-nxml-*-->
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
+  "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+<!-- SPDX-License-Identifier: LGPL-2.1-or-later -->
+
+<refentry id="systemd-vpick"
+    xmlns:xi="http://www.w3.org/2001/XInclude">
+
+  <refentryinfo>
+    <title>systemd-vpick</title>
+    <productname>systemd</productname>
+  </refentryinfo>
+
+  <refmeta>
+    <refentrytitle>systemd-vpick</refentrytitle>
+    <manvolnum>1</manvolnum>
+  </refmeta>
+
+  <refnamediv>
+    <refname>systemd-vpick</refname>
+    <refpurpose>Resolve paths to <literal>.v/</literal> versioned directories</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv>
+    <cmdsynopsis>
+      <command>systemd-vpick <arg choice="opt" rep="repeat">OPTIONS</arg> <arg choice="opt" rep="repeat">PATH</arg></command>
+    </cmdsynopsis>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>Description</title>
+
+    <para><command>systemd-vpick</command> resolves a file system path referencing a <literal>.v/</literal>
+    versioned directory to a path to the newest (by version) file contained therein. This tool provides a
+    command line interface for the
+    <citerefentry><refentrytitle>systemd.v</refentrytitle><manvolnum>7</manvolnum></citerefentry>
+    logic.</para>
+
+    <para>The tool expects a path to a <literal>.v/</literal> directory as argument (either directly, or with
+    a triple underscore pattern as final component). It then determines the newest file contained in that
+    directory, and writes its path to standard output.</para>
+
+    <para>Unless the triple underscore pattern is passed as last component of the path, it is typically
+    necessary to at least specify the <option>--suffix=</option> switch to configure the file suffix to look
+    for.</para>
+
+    <para>If the specified path does not reference a <literal>.v/</literal> path (i.e. neither the final
+    component ends in <literal>.v</literal>, nor the penultimate does or the final one does contain a triple
+    underscore) it specified path is written unmodified to standard output.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>Options</title>
+
+    <para>The following options are understood:</para>
+
+    <variablelist>
+      <varlistentry>
+        <term><option>--basename=</option></term>
+        <term><option>-B</option></term>
+
+        <listitem><para>Overrides the "basename" of the files to look for, i.e. the part to the left of the
+        variable part of the filenames. Normally this is derived automatically from the filename of the
+        <literal>.v</literal> component of the specified path, or from the triple underscore pattern in the
+        last component of the specified path.</para>
+
+        <xi:include href="version-info.xml" xpointer="v256"/></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>-V</option></term>
+
+        <listitem><para>Explicitly configures the version to select. If specified, a filename with the
+        specified version string will be looked for, instead of the newest version
+        available.</para>
+
+        <xi:include href="version-info.xml" xpointer="v256"/></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>-A</option></term>
+
+        <listitem><para>Explicitly configures the architecture to select. If specified, a filename with the
+        specified architecture identifier will be looked for. If not specified only filenames with a locally
+        supported architecture are considered, or those without any architecture identifier.</para>
+
+        <xi:include href="version-info.xml" xpointer="v256"/></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>--suffix=</option></term>
+        <term><option>-S</option></term>
+
+        <listitem><para>Configures the suffix of the filenames to consider. For the <literal>.v/</literal>
+        logic it is necessary to specify the suffix to look for, and the <literal>.v/</literal> component
+        must also carry the suffix immediately before <literal>.v</literal> in its name.</para>
+
+        <xi:include href="version-info.xml" xpointer="v256"/></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>--type=</option></term>
+        <term><option>-t</option></term>
+
+        <listitem><para>Configures the inode type to look for in the <literal>.v/</literal> directory. Takes
+        one of <literal>reg</literal>, <literal>dir</literal>, <literal>sock</literal>,
+        <literal>fifo</literal>, <literal>blk</literal>, <literal>chr</literal>, <literal>lnk</literal> as
+        argument, each identifying an inode type. See <citerefentry
+        project='man-pages'><refentrytitle>inode</refentrytitle><manvolnum>7</manvolnum></citerefentry> for
+        details about inode types. If this option is used inodes not matching the specified type are filtered
+        and not taken into consideration.</para>
+
+        <xi:include href="version-info.xml" xpointer="v256"/></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>--print=</option></term>
+        <term><option>-p</option></term>
+
+        <listitem><para>Configures what precisely to write to standard output. If not specified prints the
+        full, resolved path of the newest matching file in the <literal>.v/</literal> directory. This switch can be set to one of the following:</para>
+
+        <itemizedlist>
+          <listitem><para>If set to <literal>filename</literal>, will print only the filename instead of the full path of the resolved file.</para></listitem>
+          <listitem><para>If set to <literal>version</literal>, will print only the version of the resolved file.</para></listitem>
+          <listitem><para>If set to <literal>type</literal>, will print only the inode type of the resolved
+          file (i.e. a string such as <literal>reg</literal> for regular files, or <literal>dir</literal> for
+          directories).</para></listitem>
+          <listitem><para>If set to <literal>arch</literal>, will print only the architecture of the resolved
+          file.</para></listitem>
+          <listitem><para>If set to <literal>tries</literal>, will print only the tries left/tries done of the
+          resolved file.</para></listitem>
+          <listitem><para>If set to <literal>all</literal>, will print all of the above in a simple tabular
+          output.</para></listitem>
+        </itemizedlist>
+
+        <xi:include href="version-info.xml" xpointer="v256"/></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>--resolve=</option></term>
+
+        <listitem><para>Takes a boolean argument. If true the path to the versioned file is fully
+        canonicalized (i.e. symlinks resolved, and redundant path components removed) before it is shown. If
+        false (the default) this is not done, and the path is shown without canonicalization.</para>
+
+        <xi:include href="version-info.xml" xpointer="v256"/></listitem>
+      </varlistentry>
+
+      <xi:include href="standard-options.xml" xpointer="help" />
+      <xi:include href="standard-options.xml" xpointer="version" />
+    </variablelist>
+  </refsect1>
+
+  <refsect1>
+    <title>Examples</title>
+
+    <para>Use a command like the following to automatically pick the newest raw disk image from a
+    <literal>.v/</literal> directory:</para>
+
+    <programlisting>$ systemd-vpick --suffix=.raw --type=reg /var/lib/machines/quux.raw.v/</programlisting>
+
+    <para>This will enumerate all regular files matching
+    <filename>/var/lib/machines/quux.raw.v/quux*.raw</filename>, filter and sort them according to the rules
+    described in
+    <citerefentry><refentrytitle>systemd.v</refentrytitle><manvolnum>7</manvolnum></citerefentry>, and then
+    write the path to the newest (by version) file to standard output.</para>
+
+    <para>Use a command like the following to automatically pick the newest OS directory tree from a
+    <literal>.v/</literal> directory:</para>
+
+    <programlisting>$ systemd-vpick --type=dir /var/lib/machines/waldo.v/</programlisting>
+
+    <para>This will enumerate all directory inodes matching
+    <filename>/var/lib/machines/waldo.v/waldo*</filename>, filter and sort them according to the rules
+    described in
+    <citerefentry><refentrytitle>systemd.v</refentrytitle><manvolnum>7</manvolnum></citerefentry>, and then
+    write the path to the newest (by version) directory to standard output.</para>
+
+    <para>For further examples see
+    <citerefentry><refentrytitle>systemd.v</refentrytitle><manvolnum>7</manvolnum></citerefentry>.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>Exit status</title>
+
+    <para>On success, 0 is returned, a non-zero failure code
+    otherwise.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>See Also</title>
+    <para><simplelist type="inline">
+      <member><citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
+      <member><citerefentry><refentrytitle>systemd.v</refentrytitle><manvolnum>7</manvolnum></citerefentry></member>
+    </simplelist></para>
+  </refsect1>
+</refentry>
index e2b291008452eab5140f1fe6888780aef16b0920..42e6ff8fd751433b2754d92e42d93cce66e1c6a5 100644 (file)
           <programlisting>BindReadOnlyPaths=/dev/log /run/systemd/journal/socket /run/systemd/journal/stdout</programlisting>
         </example>
 
+        <para>In place of the directory path a <literal>.v/</literal> versioned directory may be specified,
+        see <citerefentry><refentrytitle>systemd.v</refentrytitle><manvolnum>7</manvolnum></citerefentry> for
+        details.</para>
+
         <xi:include href="system-or-user-ns.xml" xpointer="singular"/></listitem>
       </varlistentry>
 
         <citerefentry><refentrytitle>systemd-soft-reboot.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>),
         in case the service is configured to survive it.</para>
 
+        <para>In place of the image path a <literal>.v/</literal> versioned directory may be specified, see
+        <citerefentry><refentrytitle>systemd.v</refentrytitle><manvolnum>7</manvolnum></citerefentry> for
+        details.</para>
+
         <xi:include href="system-only.xml" xpointer="singular"/>
 
         <xi:include href="version-info.xml" xpointer="v233"/></listitem>
index 4d99cd88f6becd8a41dedf65191efb3604bed0b0..8869d9589f0fbec6663df7dd0ad176384b0bccaf 100644 (file)
         <listitem>
           <para>A description of the device.</para>
 
-        <xi:include href="version-info.xml" xpointer="v211"/>
+          <xi:include href="version-info.xml" xpointer="v211"/>
+        </listitem>
+      </varlistentry>
+      <varlistentry>
+        <term><varname>Property=</varname></term>
+        <listitem>
+          <para>Set specified udev properties. This takes space separated list of key-value pairs
+          concatenated with equal sign (<literal>=</literal>). Example:
+          <programlisting>Property=HOGE=foo BAR=baz</programlisting>
+          This option supports simple specifier expansion, see the Specifiers section below.
+          This option can be specified multiple times. If an empty string is assigned, then the all previous
+          assignments are cleared.</para>
+
+          <para>This setting is useful to configure the <literal>ID_NET_MANAGED_BY=</literal> property which
+          declares which network management service shall manage the interface, which is respected by
+          systemd-networkd and others. Use
+          <programlisting>Property=ID_NET_MANAGED_BY=io.systemd.Network</programlisting>
+          to declare explicitly that <command>systemd-networkd</command> shall manage the interface, or set
+          the property to something else to declare explicitly it shall not do so. See
+          <citerefentry><refentrytitle>systemd.network</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+          for details how this property is used to match interface names.</para>
+
+          <xi:include href="version-info.xml" xpointer="v256"/>
+        </listitem>
+      </varlistentry>
+      <varlistentry>
+        <term><varname>ImportProperty=</varname></term>
+        <listitem>
+          <para>Import specified udev properties from the saved database. This takes space separated list of
+          property names. Example: <programlisting>ImportProperty=HOGE BAR</programlisting>
+          This option supports simple specifier expansion, see the Specifiers section below.
+          This option can be specified multiple times. If an empty string is assigned, then the all previous
+          assignments are cleared.</para>
+          <para>If the same property is also set in <varname>Property=</varname> in the above, then the
+          imported property value will be overridden by the value specified in <varname>Property=</varname>.
+          </para>
+
+          <xi:include href="version-info.xml" xpointer="v256"/>
+        </listitem>
+      </varlistentry>
+      <varlistentry>
+        <term><varname>UnsetProperty=</varname></term>
+        <listitem>
+          <para>Unset specified udev properties. This takes space separated list of
+          property names. Example: <programlisting>ImportProperty=HOGE BAR</programlisting>
+          This option supports simple specifier expansion, see the Specifiers section below.
+          This option can be specified multiple times. If an empty string is assigned, then the all previous
+          assignments are cleared.</para>
+          <para>This setting is applied after <varname>ImportProperty=</varname> and
+          <varname>Property=</varname> are applied. Hence, if the same property is specified in
+          <varname>ImportProperty=</varname> or <varname>Property=</varname>, then the imported or specified
+          property value will be ignored, and the property will be unset.</para>
+
+          <xi:include href="version-info.xml" xpointer="v256"/>
         </listitem>
       </varlistentry>
       <varlistentry>
         <listitem>
           <para>The <varname>ifalias</varname> interface property is set to this value.</para>
 
-        <xi:include href="version-info.xml" xpointer="v211"/>
+          <xi:include href="version-info.xml" xpointer="v211"/>
         </listitem>
       </varlistentry>
       <varlistentry>
     </variablelist>
   </refsect1>
 
+  <refsect1>
+    <title>Specifiers</title>
+
+    <para>Some settings resolve specifiers which may be used to write generic unit files referring to runtime
+    or unit parameters that are replaced when the unit files are loaded. Specifiers must be known and
+    resolvable for the setting to be valid. The following specifiers are understood:</para>
+
+    <table class='specifiers'>
+      <title>Specifiers available in unit files</title>
+      <tgroup cols='3' align='left' colsep='1' rowsep='1'>
+        <colspec colname="spec" />
+        <colspec colname="mean" />
+        <colspec colname="detail" />
+        <thead>
+          <row>
+            <entry>Specifier</entry>
+            <entry>Meaning</entry>
+            <entry>Details</entry>
+          </row>
+        </thead>
+        <tbody>
+          <xi:include href="standard-specifiers.xml" xpointer="a"/>
+          <xi:include href="standard-specifiers.xml" xpointer="A"/>
+          <xi:include href="standard-specifiers.xml" xpointer="b"/>
+          <xi:include href="standard-specifiers.xml" xpointer="B"/>
+          <xi:include href="standard-specifiers.xml" xpointer="H"/>
+          <xi:include href="standard-specifiers.xml" xpointer="l"/>
+          <xi:include href="standard-specifiers.xml" xpointer="m"/>
+          <xi:include href="standard-specifiers.xml" xpointer="M"/>
+          <xi:include href="standard-specifiers.xml" xpointer="o"/>
+          <xi:include href="standard-specifiers.xml" xpointer="q"/>
+          <xi:include href="standard-specifiers.xml" xpointer="T"/>
+          <xi:include href="standard-specifiers.xml" xpointer="v"/>
+          <xi:include href="standard-specifiers.xml" xpointer="V"/>
+          <xi:include href="standard-specifiers.xml" xpointer="w"/>
+          <xi:include href="standard-specifiers.xml" xpointer="W"/>
+        </tbody>
+      </tgroup>
+    </table>
+  </refsect1>
+
   <refsect1>
     <title>Examples</title>
 
index 85eb79426987d96211047e7c38b8620a251061d2..307acc4ce9e7936db450ce4feffd3071aee8306d 100644 (file)
 
       <varlistentry>
         <term><varname>What=</varname></term>
-        <listitem><para>Takes an absolute path of a device node, file or other resource to mount. See
-        <citerefentry
+        <listitem><para>Takes an absolute path or a fstab-style identifier of a device node, file or
+        other resource to mount. See <citerefentry
         project='man-pages'><refentrytitle>mount</refentrytitle><manvolnum>8</manvolnum></citerefentry> for
         details. If this refers to a device node, a dependency on the respective device unit is automatically
         created. (See
index cd77e725bc49b266aeacb60fdb1408cc55968c0e..bf3b5c21daef51a7eb175f402c45a2b7aa672c0e 100644 (file)
       <varlistentry>
         <term><varname>PrivateKey=</varname></term>
         <listitem>
-          <para>The Base64 encoded private key for the interface. It can be
-          generated using the <command>wg genkey</command> command
+          <para>The Base64 encoded private key for the interface. It can be generated using
+          the <command>wg genkey</command> command
           (see <citerefentry project="wireguard"><refentrytitle>wg</refentrytitle><manvolnum>8</manvolnum></citerefentry>).
-          This option or <varname>PrivateKeyFile=</varname> is mandatory to use WireGuard.
-          Note that because this information is secret, you may want to set
-          the permissions of the .netdev file to be owned by <literal>root:systemd-network</literal>
-          with a <literal>0640</literal> file mode.</para>
+          Specially, if the specified key is prefixed with <literal>@</literal>, it is interpreted as
+          the name of the credential from which the actual key shall be read. <command>systemd-networkd.service</command>
+          automatically imports credentials matching <literal>network.wireguard.*</literal>. For more details
+          on credentials, refer to
+          <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
+          A private key is mandatory to use WireGuard. If not set, the credential
+          <literal>network.wireguard.private.<replaceable>netdev</replaceable></literal> is used if exists.
+          I.e. for <filename>50-foobar.netdev</filename>, <literal>network.wireguard.private.50-foobar</literal>
+          is tried.</para>
+
+          <para>Note that because this information is secret, it's strongly recommended to use an (encrypted)
+          credential. Alternatively, you may want to set the permissions of the .netdev file to be owned
+          by <literal>root:systemd-network</literal> with a <literal>0640</literal> file mode.</para>
 
           <xi:include href="version-info.xml" xpointer="v237"/>
         </listitem>
         <listitem>
           <para>Sets a Base64 encoded public key calculated by <command>wg pubkey</command>
           (see <citerefentry project="wireguard"><refentrytitle>wg</refentrytitle><manvolnum>8</manvolnum></citerefentry>)
-          from a private key, and usually transmitted out of band to the
-          author of the configuration file. This option is mandatory for this
-          section.</para>
+          from a private key, and usually transmitted out of band to the author of the configuration file.
+          This option honors the <literal>@</literal> prefix in the same way as the <option>PrivateKey=</option>
+          setting of the <option>[WireGuard]</option> section. This option is mandatory for this section.</para>
 
         <xi:include href="version-info.xml" xpointer="v237"/>
         </listitem>
       <varlistentry>
         <term><varname>PresharedKey=</varname></term>
         <listitem>
-          <para>Optional preshared key for the interface. It can be generated
-          by the <command>wg genpsk</command> command. This option adds an
-          additional layer of symmetric-key cryptography to be mixed into the
-          already existing public-key cryptography, for post-quantum
-          resistance.
-          Note that because this information is secret, you may want to set
-          the permissions of the .netdev file to be owned by <literal>root:systemd-network</literal>
-          with a <literal>0640</literal> file mode.</para>
+          <para>Optional preshared key for the interface. It can be generated by the <command>wg genpsk</command>
+          command. This option adds an additional layer of symmetric-key cryptography to be mixed into the
+          already existing public-key cryptography, for post-quantum resistance.
+          This option honors the <literal>@</literal> prefix in the same way as the <option>PrivateKey=</option>
+          setting of the <option>[WireGuard]</option> section.</para>
+
+          <para>Note that because this information is secret, it's strongly recommended to use an (encrypted)
+          credential. Alternatively, you may want to set the permissions of the .netdev file to be owned
+          by <literal>root:systemd-network</literal> with a <literal>0640</literal> file mode.</para>
 
           <xi:include href="version-info.xml" xpointer="v237"/>
         </listitem>
       <varlistentry>
         <term><varname>Endpoint=</varname></term>
         <listitem>
-          <para>Sets an endpoint IP address or hostname, followed by a colon, and then
-          a port number. IPv6 address must be in the square brackets. For example,
-          <literal>111.222.333.444:51820</literal> for IPv4 and <literal>[1111:2222::3333]:51820</literal>
-          for IPv6 address. This endpoint will be updated automatically once to
-          the most recent source IP address and port of correctly
+          <para>Sets an endpoint IP address or hostname, followed by a colon, and then a port number.
+          IPv6 address must be in the square brackets. For example, <literal>111.222.333.444:51820</literal>
+          for IPv4 and <literal>[1111:2222::3333]:51820</literal> for IPv6 address. This endpoint will be
+          updated automatically once to the most recent source IP address and port of correctly
           authenticated packets from the peer at configuration time.</para>
 
+          <para>This option honors the <literal>@</literal> prefix in the same way as the <option>PrivateKey=</option>
+          setting of the <option>[WireGuard]</option> section.</para>
+
           <xi:include href="version-info.xml" xpointer="v237"/>
         </listitem>
       </varlistentry>
index 2c5785cca9bf9f9eaeb46ab477edd95f416939a0..2eb6797f4c9d05d70bb6962f916fc73e145bcf2e 100644 (file)
@@ -409,7 +409,9 @@ CPUWeight=20   DisableControllers=cpu              /          \
           system. If assigned the
           special value <literal>infinity</literal>, no memory throttling is applied. This controls the
           <literal>memory.high</literal> control group attribute. For details about this control group attribute, see
-          <ulink url="https://docs.kernel.org/admin-guide/cgroup-v2.html#memory-interface-files">Memory Interface Files</ulink>.</para>
+          <ulink url="https://docs.kernel.org/admin-guide/cgroup-v2.html#memory-interface-files">Memory Interface Files</ulink>.
+          The effective configuration is reported as <varname>EffectiveMemoryHigh=</varname>
+          (see also <varname>EffectiveMemoryMax=</varname>).</para>
 
           <para>While <varname>StartupMemoryHigh=</varname> applies to the startup and shutdown phases of the system,
           <varname>MemoryHigh=</varname> applies to normal runtime of the system, and if the former is not set also to
@@ -437,7 +439,9 @@ CPUWeight=20   DisableControllers=cpu              /          \
           percentage value may be specified, which is taken relative to the installed physical memory on the system. If
           assigned the special value <literal>infinity</literal>, no memory limit is applied. This controls the
           <literal>memory.max</literal> control group attribute. For details about this control group attribute, see
-          <ulink url="https://docs.kernel.org/admin-guide/cgroup-v2.html#memory-interface-files">Memory Interface Files</ulink>.</para>
+          <ulink url="https://docs.kernel.org/admin-guide/cgroup-v2.html#memory-interface-files">Memory Interface Files</ulink>.
+          The effective configuration is reported as <varname>EffectiveMemoryMax=</varname> (the value is
+          the most stringent limit of the unit and parent slices and it is capped by physical memory).</para>
 
           <para>While <varname>StartupMemoryMax=</varname> applies to the startup and shutdown phases of the system,
           <varname>MemoryMax=</varname> applies to normal runtime of the system, and if the former is not set also to
@@ -563,7 +567,8 @@ CPUWeight=20   DisableControllers=cpu              /          \
           limit is applied. This controls the <literal>pids.max</literal> control group attribute. For
           details about this control group attribute, the
           <ulink url="https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v2.html#pid">pids controller
-          </ulink>.</para>
+          </ulink>.
+          The effective configuration is reported as <varname>EffectiveTasksMax=</varname>.</para>
 
           <para>The system default for this setting may be controlled with
           <varname>DefaultTasksMax=</varname> in
index 8b35a9469a65da233b966e6ba1b9941d72950d28..2b3305ccdeb4af322034c487cfa25ae7d8e0deaa 100644 (file)
 
       <varlistentry>
         <term><varname>What=</varname></term>
-        <listitem><para>Takes an absolute path of a device node or file to use for paging. See <citerefentry
+        <listitem><para>Takes an absolute path or a fstab-style identifier of a device node or file to use
+        for paging. See <citerefentry
         project='man-pages'><refentrytitle>swapon</refentrytitle><manvolnum>8</manvolnum></citerefentry> for
         details. If this refers to a device node, a dependency on the respective device unit is automatically
         created. (See
index ab6cab2e06a95c6e6253635b6e13eb3ff4c279fc..eb4c94c47f1a3ad276ea9fb3497d128c278081c6 100644 (file)
         </listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><varname>network.netdev.*</varname></term>
+        <term><varname>network.link.*</varname></term>
+        <term><varname>network.network.*</varname></term>
+        <listitem>
+          <para>Configures network devices. Read by
+          <citerefentry><refentrytitle>systemd-network-generator.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>. These
+          credentials directly translate to a matching <filename>*.netdev</filename>,
+          <filename>*.link</filename> or <filename>*.network</filename> file. Example: the contents of a
+          credential <filename>network.link.50-foobar</filename> will be copied into a file
+          <filename>50-foobar.link</filename>. See
+          <citerefentry><refentrytitle>systemd.netdev</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
+          <citerefentry><refentrytitle>systemd.link</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
+          <citerefentry><refentrytitle>systemd.network</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+          for details.</para>
+
+          <para>Note that the resulting files are created world-readable, it's hence recommended to not include
+          secrets in these credentials, but supply them via separate credentials directly to
+          <filename>systemd-networkd.service</filename>, e.g. <varname>network.wireguard.*</varname>
+          as described below.</para>
+
+          <xi:include href="version-info.xml" xpointer="v256"/>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>network.wireguard.*</varname></term>
+        <listitem>
+          <para>Configures secrets for WireGuard netdevs. Read by
+          <citerefentry><refentrytitle>systemd-networkd.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>.
+          For more information, refer to the <option>[WireGuard]</option> section of
+          <citerefentry><refentrytitle>systemd.netdev</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
+          </para>
+
+          <xi:include href="version-info.xml" xpointer="v256"/>
+        </listitem>
+      </varlistentry>
+
       <varlistentry>
         <term><varname>passwd.hashed-password.root</varname></term>
         <term><varname>passwd.plaintext-password.root</varname></term>
index c447cd063887e68643cf8be53ea19c853739f858..f7a6af70597048ad1a8abdc0c9b51b707141d0b0 100644 (file)
             <entry>Credentials directory</entry>
             <entry>This is the value of the <literal>$CREDENTIALS_DIRECTORY</literal> environment variable if available. See section "Credentials" in <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry> for more information.</entry>
           </row>
+          <row>
+            <entry><literal>%D</literal></entry>
+            <entry>Shared data directory</entry>
+            <entry>This is either <filename>/usr/share/</filename> (for the system manager) or the path <literal>$XDG_DATA_HOME</literal> resolves to (for user managers).</entry>
+          </row>
           <row>
             <entry><literal>%E</literal></entry>
             <entry>Configuration directory root</entry>
diff --git a/man/systemd.v.xml b/man/systemd.v.xml
new file mode 100644 (file)
index 0000000..b29d32f
--- /dev/null
@@ -0,0 +1,163 @@
+<?xml version='1.0'?> <!--*-nxml-*-->
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
+  "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+<!-- SPDX-License-Identifier: LGPL-2.1-or-later -->
+
+<refentry id="systemd.v">
+
+  <refentryinfo>
+    <title>systemd.v</title>
+    <productname>systemd</productname>
+  </refentryinfo>
+
+  <refmeta>
+    <refentrytitle>systemd.v</refentrytitle>
+    <manvolnum>7</manvolnum>
+  </refmeta>
+
+  <refnamediv>
+    <refname>systemd.v</refname>
+    <refpurpose>Directory with Versioned Resources</refpurpose>
+  </refnamediv>
+
+  <refsect1>
+    <title>Description</title>
+
+    <para>In various places systemd components accept paths whose trailing components have the
+    <literal>.v/</literal> suffix, pointing to a directory. These components will then automatically look for
+    suitable files inside the directory, do a version comparison and open the newest file found (by
+    version). Specifically, two expressions are supported:</para>
+
+    <itemizedlist>
+
+      <listitem><para>When looking for files with a suffix <replaceable>.SUFFIX</replaceable>, and a path
+      <filename>…<replaceable>PATH</replaceable>/<replaceable>NAME</replaceable><replaceable>.SUFFIX</replaceable>.v/</filename>
+      is specified, then all files
+      <filename>…<replaceable>PATH</replaceable>/<replaceable>NAME</replaceable><replaceable>.SUFFIX</replaceable>.v/<replaceable>NAME</replaceable>_*<replaceable>.SUFFIX</replaceable></filename>
+      are enumerated, filtered, sorted and the newest file used. The primary sorting key is the
+      <emphasis>variable part</emphasis>, here indicated by the wildcard
+      <literal>*</literal>.</para></listitem>
+
+      <listitem><para>When a path
+      <filename>…<replaceable>PATH</replaceable>.v/<replaceable>NAME</replaceable>___<replaceable>.SUFFIX</replaceable></filename>
+      is specified (i.e. the penultimate component of the path ends in <literal>.v</literal> and the final
+      component contains a triple underscore), then all files
+      <filename>…<replaceable>PATH</replaceable>.v/<replaceable>NAME</replaceable>_*<replaceable>.SUFFIX</replaceable></filename>
+      are enumerated, filtered, sorted and the newest file used (again, by the <emphasis>variable
+      part</emphasis>, here indicated by the wildcard <literal>*</literal>).</para></listitem>
+    </itemizedlist>
+
+    <para>To illustrate this in an example, consider a directory <filename>/var/lib/machines/mymachine.raw.v/</filename>, which is populated with three files:</para>
+
+    <itemizedlist>
+      <listitem><para><filename>mymachine_7.5.13.raw</filename></para></listitem>
+      <listitem><para><filename>mymachine_7.5.14.raw</filename></para></listitem>
+      <listitem><para><filename>mymachine_7.6.0.raw</filename></para></listitem>
+    </itemizedlist>
+
+    <para>Invoke a tool such as <citerefentry><refentrytitle>systemd-nspawn</refentrytitle><manvolnum>1</manvolnum></citerefentry> with a command line like the following:</para>
+
+    <programlisting># systemd-nspawn --image=/var/lib/machines/mymachine.raw.v --boot</programlisting>
+
+    <para>Then this would automatically be resolved to the equivalent of:</para>
+
+    <programlisting># systemd-nspawn --image=/var/lib/machines/mymachine.raw.v/mymachine_7.6.0.raw --boot</programlisting>
+
+    <para>Much of systemd's functionality that expects a path to a disk image or OS directory hierarchy
+    support the <literal>.v/</literal> versioned directory mechanism, for example
+    <citerefentry><refentrytitle>systemd-nspawn</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+    <citerefentry><refentrytitle>systemd-dissect</refentrytitle><manvolnum>1</manvolnum></citerefentry> or
+    the <varname>RootDirectory=</varname>/<varname>RootImage=</varname> settings of service files (see
+    <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry>).</para>
+
+    <para>Use the
+    <citerefentry><refentrytitle>systemd-vpick</refentrytitle><manvolnum>1</manvolnum></citerefentry> tool to
+    resolve <literal>.v/</literal> paths from the command line, for example for usage in shell
+    scripts.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>Filtering and Sorting</title>
+
+    <para>The variable part of the filenames in the <literal>.v/</literal> directories are filtered and
+    compared primarily with a version comparison, implementing <ulink
+    url="https://uapi-group.org/specifications/specs/version_format_specification/">Version Format
+    Specification</ulink>. However, additional rules apply:</para>
+
+    <itemizedlist>
+      <listitem><para>If the variable part is suffixed by one or two integer values ("tries left" and "tries
+      done") in the formats <filename>+<replaceable>LEFT</replaceable></filename> or
+      <filename>+<replaceable>LEFT</replaceable>-<replaceable>DONE</replaceable></filename>, then these
+      indicate usage attempt counters. The idea is that each time before a file is attempted to be used, its
+      "tries left" counter is decreased, and the "tries done" counter increased (simply by renaming the
+      file). When the file is successfully used (which for example could mean for an OS image: successfully
+      booted) the counters are removed from the file name, indicating that the file has been validated to
+      work correctly. This mechanism mirrors the boot assessment counters defined by <ulink
+      url="https://systemd.io/AUTOMATIC_BOOT_ASSESSMENT/">Automatic Boot Assessment</ulink>. Any filenames
+      with no boot counters or with a non-zero "tries left" counter are sorted before filenames with a zero
+      "tries left" counter.</para></listitem>
+
+      <listitem><para>Preceding the use counters (if they are specified), an optional CPU architecture
+      identifier may be specified in the filename (separated from the version with an underscore), as defined
+      in the architecture vocabulary of the <varname>ConditionArchitecture=</varname> unit file setting, as
+      documented in
+      <citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry>. Files
+      whose name indicates an architecture not supported locally are filtered and not considered for the
+      version comparison.</para></listitem>
+
+      <listitem><para>The rest of the variable part is the version string.</para></listitem>
+    </itemizedlist>
+
+    <para>Or in other words, the files in the <literal>.v/</literal> directories should follow one of these
+    naming structures:</para>
+
+    <itemizedlist>
+      <listitem><para><filename><replaceable>NAME</replaceable>_<replaceable>VERSION</replaceable><replaceable>.SUFFIX</replaceable></filename></para></listitem>
+      <listitem><para><filename><replaceable>NAME</replaceable>_<replaceable>VERSION</replaceable>_<replaceable>ARCHITECTURE</replaceable><replaceable>.SUFFIX</replaceable></filename></para></listitem>
+      <listitem><para><filename><replaceable>NAME</replaceable>_<replaceable>VERSION</replaceable>+<replaceable>LEFT</replaceable><replaceable>.SUFFIX</replaceable></filename></para></listitem>
+      <listitem><para><filename><replaceable>NAME</replaceable>_<replaceable>VERSION</replaceable>+<replaceable>LEFT</replaceable>-<replaceable>DONE</replaceable><replaceable>.SUFFIX</replaceable></filename></para></listitem>
+      <listitem><para><filename><replaceable>NAME</replaceable>_<replaceable>VERSION</replaceable>_<replaceable>ARCHITECTURE</replaceable>+<replaceable>LEFT</replaceable><replaceable>.SUFFIX</replaceable></filename></para></listitem>
+      <listitem><para><filename><replaceable>NAME</replaceable>_<replaceable>VERSION</replaceable>_<replaceable>ARCHITECTURE</replaceable>+<replaceable>LEFT</replaceable>-<replaceable>DONE</replaceable><replaceable>.SUFFIX</replaceable></filename></para></listitem>
+    </itemizedlist>
+  </refsect1>
+
+  <refsect1>
+    <title>Example</title>
+
+    <para>Here's a more comprehensive example, further extending the one described above. Consider a
+    directory <filename>/var/lib/machines/mymachine.raw.v/</filename>, which is populated with the following
+    files:</para>
+
+    <itemizedlist>
+      <listitem><para><filename>mymachine_7.5.13.raw</filename></para></listitem>
+      <listitem><para><filename>mymachine_7.5.14_x86-64.raw</filename></para></listitem>
+      <listitem><para><filename>mymachine_7.6.0_arm64.raw</filename></para></listitem>
+      <listitem><para><filename>mymachine_7.7.0_x86-64+0-5.raw</filename></para></listitem>
+    </itemizedlist>
+
+    <para>Now invoke the following command on an x86-64 machine:</para>
+
+    <programlisting>$ systemd-vpick --suffix=.raw /var/lib/machines/mymachine.raw.v/</programlisting>
+
+    <para>This would resolve the specified path to
+    <filename>/var/lib/machines/mymachine.raw.v/mymachine_7.5.14_x86-64.raw</filename>. Explanation: even
+    though <filename>mymachine_7.7.0_x86-64+0-5.raw</filename> has the newest version, it is not preferred
+    because its tries left counter is zero. And even though <filename>mymachine_7.6.0_arm64.raw</filename>
+    has the second newest version it is also not considered, in this case because we operate on an x86_64
+    system and the image is intended for arm64 CPUs. Finally, the <filename>mymachine_7.5.13.raw</filename>
+    image is not considered because it is older than <filename>mymachine_7.5.14_x86-64.raw</filename>.</para>
+  </refsect1>
+
+  <refsect1>
+      <title>See Also</title>
+      <para><simplelist type="inline">
+        <member><citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
+        <member><citerefentry><refentrytitle>systemd-vpick</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
+        <member><citerefentry><refentrytitle>systemd-nspawn</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
+        <member><citerefentry><refentrytitle>systemd-dissect</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
+        <member><citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry></member>
+        <member><citerefentry><refentrytitle>systemd-sysupdate</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
+      </simplelist></para>
+  </refsect1>
+
+</refentry>
index 77acaab0f5b008281210135671109ac351f76f37..eff49af349b16eaefac5a89fec17775ead50358c 100644 (file)
 
     <itemizedlist>
       <listitem><para>A Varlink service reference starting with the <literal>unix:</literal> string, followed
-      by an absolute <constant>AF_UNIX</constant> path, or by <literal>@</literal> and an arbitrary string
+      by an absolute <constant>AF_UNIX</constant> socket path, or by <literal>@</literal> and an arbitrary string
       (the latter for referencing sockets in the abstract namespace).</para></listitem>
 
       <listitem><para>A Varlink service reference starting with the <literal>exec:</literal> string, followed
       by an absolute path of a binary to execute.</para></listitem>
+
+      <listitem><para>A Varlink service reference starting with the <literal>ssh:</literal> string, followed
+      by an SSH host specification, followed by <literal>:</literal>, followed by an absolute
+      <constant>AF_UNIX</constant> socket path. (This requires OpenSSH 9.4 or newer on the server side,
+      abstract namespace sockets are not supported.)</para></listitem>
     </itemizedlist>
 
     <para>For convenience these two simpler (redundant) service address syntaxes are also supported:</para>
index d1433e3dbc750f0ae043acef7a845e71483622b7..d2d255391d523f009976acbb287a4e0f71ae4fd1 100644 (file)
@@ -2230,6 +2230,7 @@ subdir('src/vconsole')
 subdir('src/veritysetup')
 subdir('src/vmspawn')
 subdir('src/volatile-root')
+subdir('src/vpick')
 subdir('src/xdg-autostart-generator')
 
 subdir('src/systemd')
index f58ee7e02e299378ece07b40f845247949784312..b099e79132e7d10e0924309f91fc6cf805e18fc6 100644 (file)
@@ -9,3 +9,6 @@ Packages=
         linux-image-generic
         linux-tools-common
         linux-tools-generic
+# "orphan_file" is enabled by default in recent versions of mkfs.ext4 but not supported by the Jammy kernel
+# so we explicitly disable it.
+Environment=SYSTEMD_REPART_MKFS_OPTIONS_EXT4="-O ^orphan_file"
index 067b05396dada6fc4eb8b49970ecbc57a3a1ed72..c7f6a05a6efa8c7c1ded1d56213a6c132789f8e2 100644 (file)
@@ -28,7 +28,7 @@ __contains_word () {
 __get_machines() {
     local a b
     { machinectl list --full --max-addresses=0 --no-legend --no-pager 2>/dev/null; echo ".host"; } | \
-       { while read a b; do echo " $a"; done; } | \
+        { while read a b; do echo " $a"; done; } | \
         sort -u
 }
 
@@ -45,10 +45,12 @@ _journalctl() {
                       --version --list-catalog --update-catalog --list-boots
                       --show-cursor --dmesg -k --pager-end -e -r --reverse
                       --utc -x --catalog --no-full --force --dump-catalog
-                      --flush --rotate --sync --no-hostname -N --fields'
-        [ARG]='-b --boot -D --directory --file -F --field -t --identifier --facility
-                      -M --machine -o --output -u --unit --user-unit -p --priority
-                      --root --case-sensitive'
+                      --flush --rotate --sync --no-hostname -N --fields
+                      --list-namespaces'
+        [ARG]='-b --boot -D --directory --file -F --field -t --identifier
+                      -T --exclude-identifier --facility -M --machine -o --output
+                      -u --unit --user-unit -p --priority --root --case-sensitive
+                      --namespace'
         [ARGUNKNOWN]='-c --cursor --interval -n --lines -S --since -U --until
                       --after-cursor --cursor-file --verify-key -g --grep
                       --vacuum-size --vacuum-time --vacuum-files --output-fields'
@@ -107,12 +109,15 @@ _journalctl() {
             --user-unit)
                 comps=$(journalctl -F '_SYSTEMD_USER_UNIT' 2>/dev/null)
                 ;;
-            --identifier|-t)
+            --identifier|-t|--exclude-identifier|-T)
                 comps=$(journalctl -F 'SYSLOG_IDENTIFIER' 2>/dev/null)
                 ;;
             --case-sensitive)
                 comps='yes no'
                 ;;
+            --namespace)
+                comps=$(journalctl --list-namespaces --output=cat 2>/dev/null)
+                ;;
             *)
                 return 0
                 ;;
index d27a6422207bea994e66a59c29b0b5ccc8aff14f..00cb478688d2ffcba22a853a9dae223118432b4f 100644 (file)
@@ -61,7 +61,7 @@ _systemd_analyze() {
 
     local -A OPTS=(
         [STANDALONE]='-h --help --version --system --user --global --order --require --no-pager
-                             --man=no --generators=yes --quiet'
+                             --man=no --generators=yes -q --quiet'
         [ARG]='-H --host -M --machine --fuzz --from-pattern --to-pattern --root'
     )
 
index 5dba1e7dfe1745d8ea8a98d89bf55809d0077b36..9c132b8731cf2d3884ce2d61be85cfd788468e47 100644 (file)
@@ -63,6 +63,13 @@ _journalctl_facilities() {
   _describe 'possible values' _journalctl_facilities
 }
 
+(( $+functions[_journalctl_namespaces] )) ||
+_journalctl_namespaces() {
+  local -a _journalctl_namespaces
+  _journalctl_namespaces=( ${(f)"$(_call_program namespaces "$service --list-namespaces --output=cat" 2>/dev/null)"} )
+  _describe 'possible values' _journalctl_namespaces
+}
+
 # Build arguments for "journalctl" to be used in completion.
 # Use both --user and --system modes, they are not exclusive.
 local -a _modes; _modes=(--user --system)
@@ -131,6 +138,8 @@ _arguments -s \
     '--header[Show journal header information]' \
     '--interval=[Time interval for changing the FSS sealing key]:time interval' \
     '--list-catalog[List messages in catalog]' \
+    '--list-namespaces[List available journal namespaces]' \
+    '--namespace[Show journal data from specified namespace]:namespace:_journalctl_namespaces' \
     '--new-id128[Generate a new 128 Bit ID]' \
     '--rotate[Request immediate rotation of the journal files]' \
     '--setup-keys[Generate a new FSS key pair]' \
index ed907f78d10d58f8635b1483af668f0c7a9d5495..43e415fc6df6e074020025ba99c881a434cf88f3 100644 (file)
@@ -48,7 +48,7 @@ static int get_current_pcr(const char *alg, uint32_t pcr, void **ret, size_t *re
         if (r < 0)
                 return log_error_errno(r, "Failed to read '%s': %m", p);
 
-        r = unhexmem(s, ss, &buf, &bufsize);
+        r = unhexmem_full(s, ss, /* secure = */ false, &buf, &bufsize);
         if (r < 0)
                 return log_error_errno(r, "Failed to decode hex PCR data '%s': %m", s);
 
index 6edc412e751b75e6f47da06f7d39206b6d4242b8..14458260c6aa420a40edc3d9805eff7388f8b5e3 100644 (file)
@@ -218,7 +218,7 @@ static int help(int argc, char *argv[], void *userdata) {
                "  dot [UNIT...]              Output dependency graph in %s format\n"
                "  dump [PATTERN...]          Output state serialization of service\n"
                "                             manager\n"
-               "  cat-config                 Show configuration file and drop-ins\n"
+               "  cat-config NAME|PATH...    Show configuration file and drop-ins\n"
                "  unit-files                 List files and symlinks for units\n"
                "  unit-paths                 List load directories for units\n"
                "  exit-status [STATUS...]    List exit status definitions\n"
@@ -238,6 +238,7 @@ static int help(int argc, char *argv[], void *userdata) {
                "  inspect-elf FILE...        Parse and print ELF package metadata\n"
                "  malloc [D-BUS SERVICE...]  Dump malloc stats of a D-Bus service\n"
                "  fdstore SERVICE...         Show file descriptor store contents of service\n"
+               "  image-policy POLICY...     Analyze image policy string\n"
                "  pcrs [PCR...]              Show TPM2 PCRs and their names\n"
                "  srk > FILE                 Write TPM2 SRK to stdout\n"
                "\nOptions:\n"
@@ -362,7 +363,7 @@ static int parse_argv(int argc, char *argv[]) {
         assert(argc >= 0);
         assert(argv);
 
-        while ((c = getopt_long(argc, argv, "hH:M:U:", options, NULL)) >= 0)
+        while ((c = getopt_long(argc, argv, "hH:M:U:q", options, NULL)) >= 0)
                 switch (c) {
 
                 case 'h':
index 23a128bfd136b02ffc3630c20f10ed63f3c7e4fc..22eb3308e48c9cd438f531e45acd1b4b7f3c00ee 100644 (file)
@@ -1012,8 +1012,8 @@ int putenv_dup(const char *assignment, bool override) {
 }
 
 int setenv_systemd_exec_pid(bool update_only) {
-        char str[DECIMAL_STR_MAX(pid_t)];
         const char *e;
+        int r;
 
         /* Update $SYSTEMD_EXEC_PID=pid except when '*' is set for the variable. */
 
@@ -1024,10 +1024,9 @@ int setenv_systemd_exec_pid(bool update_only) {
         if (streq_ptr(e, "*"))
                 return 0;
 
-        xsprintf(str, PID_FMT, getpid_cached());
-
-        if (setenv("SYSTEMD_EXEC_PID", str, 1) < 0)
-                return -errno;
+        r = setenvf("SYSTEMD_EXEC_PID", /* overwrite= */ 1, PID_FMT, getpid_cached());
+        if (r < 0)
+                return r;
 
         return 1;
 }
@@ -1122,3 +1121,25 @@ int set_full_environment(char **env) {
 
         return 0;
 }
+
+int setenvf(const char *name, bool overwrite, const char *valuef, ...) {
+        _cleanup_free_ char *value = NULL;
+        va_list ap;
+        int r;
+
+        assert(name);
+
+        if (!valuef)
+                return RET_NERRNO(unsetenv(name));
+
+        va_start(ap, valuef);
+        DISABLE_WARNING_FORMAT_NONLITERAL;
+        r = vasprintf(&value, valuef, ap);
+        REENABLE_WARNING;
+        va_end(ap);
+
+        if (r < 0)
+                return -ENOMEM;
+
+        return RET_NERRNO(setenv(name, value, overwrite));
+}
index 8e77cc71d6b8aac27ac256babc69977524d4ec92..332efcf1b742af94945885cfe29f18ac94895259 100644 (file)
@@ -80,3 +80,5 @@ int getenv_path_list(const char *name, char ***ret_paths);
 int getenv_steal_erase(const char *name, char **ret);
 
 int set_full_environment(char **env);
+
+int setenvf(const char *name, bool overwrite, const char *valuef, ...) _printf_(3,4);
index 1a279690d245c678b6e11612adb697e52584b6b5..38866ebb78253b409e06d82385dce9df719b3612 100644 (file)
@@ -170,6 +170,19 @@ int fd_nonblock(int fd, bool nonblock) {
         return RET_NERRNO(fcntl(fd, F_SETFL, nflags));
 }
 
+int stdio_disable_nonblock(void) {
+        int ret = 0;
+
+        /* stdin/stdout/stderr really should have O_NONBLOCK, which would confuse apps if left on, as
+         * write()s might unexpectedly fail with EAGAIN. */
+
+        RET_GATHER(ret, fd_nonblock(STDIN_FILENO, false));
+        RET_GATHER(ret, fd_nonblock(STDOUT_FILENO, false));
+        RET_GATHER(ret, fd_nonblock(STDERR_FILENO, false));
+
+        return ret;
+}
+
 int fd_cloexec(int fd, bool cloexec) {
         int flags, nflags;
 
index 4bdd61fe54350dce2408cd1e004241811b5d58de..6a1143b4f301ee8bf0237fb99caa6f209edcb41c 100644 (file)
@@ -62,6 +62,8 @@ DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(DIR*, closedir, NULL);
 #define _cleanup_close_pair_ _cleanup_(close_pairp)
 
 int fd_nonblock(int fd, bool nonblock);
+int stdio_disable_nonblock(void);
+
 int fd_cloexec(int fd, bool cloexec);
 int fd_cloexec_many(const int fds[], size_t n_fds, bool cloexec);
 
index 752a65646f596a56906292181c55bf769560999a..f19326b7110f5cb2edc78db31b1d61526d87742b 100644 (file)
@@ -200,6 +200,19 @@ int write_string_stream_ts(
         return 0;
 }
 
+static mode_t write_string_file_flags_to_mode(WriteStringFileFlags flags) {
+
+        /* We support three different modes, that are the ones that really make sense for text files like this:
+         *
+         *     → 0600 (i.e. root-only)
+         *     → 0444 (i.e. read-only)
+         *     → 0644 (i.e. writable for root, readable for everyone else)
+         */
+
+        return FLAGS_SET(flags, WRITE_STRING_FILE_MODE_0600) ? 0600 :
+                FLAGS_SET(flags, WRITE_STRING_FILE_MODE_0444) ? 0444 : 0644;
+}
+
 static int write_string_file_atomic_at(
                 int dir_fd,
                 const char *fn,
@@ -225,7 +238,7 @@ static int write_string_file_atomic_at(
         if (r < 0)
                 goto fail;
 
-        r = fchmod_umask(fileno(f), FLAGS_SET(flags, WRITE_STRING_FILE_MODE_0600) ? 0600 : 0644);
+        r = fchmod_umask(fileno(f), write_string_file_flags_to_mode(flags));
         if (r < 0)
                 goto fail;
 
@@ -288,7 +301,7 @@ int write_string_file_ts_at(
                     (FLAGS_SET(flags, WRITE_STRING_FILE_CREATE) ? O_CREAT : 0) |
                     (FLAGS_SET(flags, WRITE_STRING_FILE_TRUNCATE) ? O_TRUNC : 0) |
                     (FLAGS_SET(flags, WRITE_STRING_FILE_SUPPRESS_REDUNDANT_VIRTUAL) ? O_RDWR : O_WRONLY),
-                    (FLAGS_SET(flags, WRITE_STRING_FILE_MODE_0600) ? 0600 : 0666));
+                    write_string_file_flags_to_mode(flags));
         if (fd < 0) {
                 r = -errno;
                 goto fail;
index e0e0a45b2b53dcee76ca8878f654202dfa800012..5b247bc10116296859cd98e22317fa44efaa6a66 100644 (file)
@@ -26,7 +26,8 @@ typedef enum {
         WRITE_STRING_FILE_NOFOLLOW                   = 1 << 8,
         WRITE_STRING_FILE_MKDIR_0755                 = 1 << 9,
         WRITE_STRING_FILE_MODE_0600                  = 1 << 10,
-        WRITE_STRING_FILE_SUPPRESS_REDUNDANT_VIRTUAL = 1 << 11,
+        WRITE_STRING_FILE_MODE_0444                  = 1 << 11,
+        WRITE_STRING_FILE_SUPPRESS_REDUNDANT_VIRTUAL = 1 << 12,
 
         /* And before you wonder, why write_string_file_atomic_label_ts() is a separate function instead of just one
            more flag here: it's about linking: we don't want to pull -lselinux into all users of write_string_file()
index ea683eb42734eb1fc6f1252d42c8451567e5e2dc..4cb67d94a48c3dd80500e83ce7bc7e0582fdd37e 100644 (file)
@@ -114,7 +114,7 @@ int unhexmem_full(
                 const char *p,
                 size_t l,
                 bool secure,
-                void **ret,
+                void **ret_data,
                 size_t *ret_len) {
 
         _cleanup_free_ uint8_t *buf = NULL;
@@ -155,8 +155,8 @@ int unhexmem_full(
 
         if (ret_len)
                 *ret_len = (size_t) (z - buf);
-        if (ret)
-                *ret = TAKE_PTR(buf);
+        if (ret_data)
+                *ret_data = TAKE_PTR(buf);
 
         return 0;
 }
@@ -766,7 +766,7 @@ int unbase64mem_full(
                 const char *p,
                 size_t l,
                 bool secure,
-                void **ret,
+                void **ret_data,
                 size_t *ret_size) {
 
         _cleanup_free_ uint8_t *buf = NULL;
@@ -854,8 +854,8 @@ int unbase64mem_full(
 
         if (ret_size)
                 *ret_size = (size_t) (z - buf);
-        if (ret)
-                *ret = TAKE_PTR(buf);
+        if (ret_data)
+                *ret_data = TAKE_PTR(buf);
 
         return 0;
 }
index 319b21a17c658d704ac36da111895fa2d01503d5..0a10af3e16ab96b8b5822cdbc1f05c93b7ff54a6 100644 (file)
@@ -18,9 +18,9 @@ char hexchar(int x) _const_;
 int unhexchar(char c) _const_;
 
 char *hexmem(const void *p, size_t l);
-int unhexmem_full(const char *p, size_t l, bool secure, void **mem, size_t *len);
-static inline int unhexmem(const char *p, size_t l, void **mem, size_t *len) {
-        return unhexmem_full(p, l, false, mem, len);
+int unhexmem_full(const char *p, size_t l, bool secure, void **ret_data, size_t *ret_size);
+static inline int unhexmem(const char *p, void **ret_data, size_t *ret_size) {
+        return unhexmem_full(p, SIZE_MAX, false, ret_data, ret_size);
 }
 
 char base32hexchar(int x) _const_;
@@ -45,9 +45,9 @@ ssize_t base64_append(
                 size_t l,
                 size_t margin,
                 size_t width);
-int unbase64mem_full(const char *p, size_t l, bool secure, void **mem, size_t *len);
-static inline int unbase64mem(const char *p, size_t l, void **mem, size_t *len) {
-        return unbase64mem_full(p, l, false, mem, len);
+int unbase64mem_full(const char *p, size_t l, bool secure, void **ret_data, size_t *ret_size);
+static inline int unbase64mem(const char *p, void **ret_data, size_t *ret_size) {
+        return unbase64mem_full(p, SIZE_MAX, false, ret_data, ret_size);
 }
 
 void hexdump(FILE *f, const void *p, size_t s);
index 3f5cdd02e103b4620881a4a93d60bcaf2ecd301b..bc7a67054adae996a3a17bbad483f3bd3c53f199 100644 (file)
@@ -12,12 +12,28 @@ size_t iovec_total_size(const struct iovec *iovec, size_t n);
 
 bool iovec_increment(struct iovec *iovec, size_t n, size_t k);
 
-#define IOVEC_MAKE(base, len) (struct iovec) { .iov_base = (base), .iov_len = (len) }
-#define IOVEC_MAKE_STRING(string)                       \
-        ({                                              \
-                const char *_s = (string);              \
-                IOVEC_MAKE((char*) _s, strlen(_s));     \
-        })
+/* This accepts both const and non-const pointers */
+#define IOVEC_MAKE(base, len)                                           \
+        (struct iovec) {                                                \
+                .iov_base = (void*) (base),                             \
+                .iov_len = (len),                                       \
+        }
+
+static inline struct iovec* iovec_make_string(struct iovec *iovec, const char *s) {
+        assert(iovec);
+        /* We don't use strlen_ptr() here, because we don't want to include string-util.h for now */
+        *iovec = IOVEC_MAKE(s, s ? strlen(s) : 0);
+        return iovec;
+}
+
+#define IOVEC_MAKE_STRING(s) \
+        *iovec_make_string(&(struct iovec) {}, s)
+
+#define CONST_IOVEC_MAKE_STRING(s)              \
+        (const struct iovec) {                  \
+                .iov_base = (char*) s,          \
+                .iov_len = STRLEN(s),           \
+        }
 
 static inline void iovec_done(struct iovec *iovec) {
         /* A _cleanup_() helper that frees the iov_base in the iovec */
@@ -35,10 +51,43 @@ static inline void iovec_done_erase(struct iovec *iovec) {
 }
 
 static inline bool iovec_is_set(const struct iovec *iovec) {
+        /* Checks if the iovec points to a non-empty chunk of memory */
         return iovec && iovec->iov_len > 0 && iovec->iov_base;
 }
 
+static inline bool iovec_is_valid(const struct iovec *iovec) {
+        /* Checks if the iovec is either NULL, empty or points to a valid bit of memory */
+        return !iovec || (iovec->iov_base || iovec->iov_len == 0);
+}
+
 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_iovec);
+
+static inline int iovec_memcmp(const struct iovec *a, const struct iovec *b) {
+
+        if (a == b)
+                return 0;
+
+        return memcmp_nn(a ? a->iov_base : NULL,
+                         a ? a->iov_len : 0,
+                         b ? b->iov_base : NULL,
+                         b ? b->iov_len : 0);
+}
+
+static inline struct iovec *iovec_memdup(const struct iovec *source, struct iovec *ret) {
+        assert(ret);
+
+        if (!iovec_is_set(source))
+                *ret = (struct iovec) {};
+        else {
+                void *p = memdup(source->iov_base, source->iov_len);
+                if (!p)
+                        return NULL;
+
+                *ret = IOVEC_MAKE(p, source->iov_len);
+        }
+
+        return ret;
+}
index d7450d8b445ec37d0d05459d80be2747a58fb309..949ca4d81f971a41019890a8b48140a766cc5c0c 100644 (file)
@@ -96,7 +96,7 @@ basic_sources = files(
         'terminal-util.c',
         'time-util.c',
         'tmpfile-util.c',
-        'uid-alloc-range.c',
+        'uid-classification.c',
         'uid-range.c',
         'unit-def.c',
         'unit-file.c',
index 30ac297e171716519b748de8f9a752a967785223..47cc7626aa357a94638ca17cd77f373580f166da 100644 (file)
@@ -6,7 +6,6 @@
 #if HAVE_LINUX_VM_SOCKETS_H
 #include <linux/vm_sockets.h>
 #else
-#define VMADDR_CID_ANY -1U
 struct sockaddr_vm {
         unsigned short svm_family;
         unsigned short svm_reserved1;
@@ -20,6 +19,26 @@ struct sockaddr_vm {
 };
 #endif /* !HAVE_LINUX_VM_SOCKETS_H */
 
+#ifndef VMADDR_CID_ANY
+#define VMADDR_CID_ANY -1U
+#endif
+
+#ifndef VMADDR_CID_HYPERVISOR
+#define VMADDR_CID_HYPERVISOR 0U
+#endif
+
+#ifndef VMADDR_CID_LOCAL
+#define VMADDR_CID_LOCAL 1U
+#endif
+
+#ifndef VMADDR_CID_HOST
+#define VMADDR_CID_HOST 2U
+#endif
+
+#ifndef VMADDR_PORT_ANY
+#define VMADDR_PORT_ANY -1U
+#endif
+
 #ifndef AF_VSOCK
 #define AF_VSOCK 40
 #endif
@@ -32,6 +51,10 @@ struct sockaddr_vm {
 #define SO_PEERGROUPS 59
 #endif
 
+#ifndef SO_PEERPIDFD
+#define SO_PEERPIDFD 77
+#endif
+
 #ifndef SO_BINDTOIFINDEX
 #define SO_BINDTOIFINDEX 62
 #endif
diff --git a/src/basic/missing_wait.h b/src/basic/missing_wait.h
new file mode 100644 (file)
index 0000000..a24779d
--- /dev/null
@@ -0,0 +1,8 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include <sys/wait.h>
+
+#ifndef P_PIDFD
+#define P_PIDFD 3
+#endif
index 0430e33e40df55e22c3773dda826252657430530..5971173915a30506cd671cc83a8f887b925d5c04 100644 (file)
@@ -691,7 +691,7 @@ int parse_ip_port(const char *s, uint16_t *ret) {
         return 0;
 }
 
-int parse_ip_port_range(const char *s, uint16_t *low, uint16_t *high) {
+int parse_ip_port_range(const char *s, uint16_t *low, uint16_t *high, bool allow_zero) {
         unsigned l, h;
         int r;
 
@@ -699,7 +699,10 @@ int parse_ip_port_range(const char *s, uint16_t *low, uint16_t *high) {
         if (r < 0)
                 return r;
 
-        if (l <= 0 || l > 65535 || h <= 0 || h > 65535)
+        if (l > 65535 || h > 65535)
+                return -EINVAL;
+
+        if (!allow_zero && (l == 0 || h == 0))
                 return -EINVAL;
 
         if (h < l)
index 1845f0a876f1aa00ef92aff9eac5b2b13e198d4c..c12988ef2049794b7ac69c095495b34a081ba3a5 100644 (file)
@@ -139,7 +139,7 @@ int parse_fractional_part_u(const char **s, size_t digits, unsigned *res);
 int parse_nice(const char *p, int *ret);
 
 int parse_ip_port(const char *s, uint16_t *ret);
-int parse_ip_port_range(const char *s, uint16_t *low, uint16_t *high);
+int parse_ip_port_range(const char *s, uint16_t *low, uint16_t *high, bool allow_zero);
 
 int parse_ip_prefix_length(const char *s, int *ret);
 
index cf1c165b605b5ffba9943d61c4a9f6e9b9f47793..972853bbd6b99c7ed6cd76c8ccf50ac49e5ee0d4 100644 (file)
@@ -3,6 +3,7 @@
 #include "errno-util.h"
 #include "fd-util.h"
 #include "missing_syscall.h"
+#include "missing_wait.h"
 #include "parse-util.h"
 #include "pidref.h"
 #include "process-util.h"
@@ -302,6 +303,44 @@ bool pidref_is_self(const PidRef *pidref) {
         return pidref->pid == getpid_cached();
 }
 
+int pidref_wait(const PidRef *pidref, siginfo_t *ret, int options) {
+        int r;
+
+        if (!pidref_is_set(pidref))
+                return -ESRCH;
+
+        if (pidref->pid == 1 || pidref->pid == getpid_cached())
+                return -ECHILD;
+
+        siginfo_t si = {};
+
+        if (pidref->fd >= 0) {
+                r = RET_NERRNO(waitid(P_PIDFD, pidref->fd, &si, options));
+                if (r >= 0) {
+                        if (ret)
+                                *ret = si;
+                        return r;
+                }
+                if (r != -EINVAL) /* P_PIDFD was added in kernel 5.4 only */
+                        return r;
+        }
+
+        r = RET_NERRNO(waitid(P_PID, pidref->pid, &si, options));
+        if (r >= 0 && ret)
+                *ret = si;
+        return r;
+}
+
+int pidref_wait_for_terminate(const PidRef *pidref, siginfo_t *ret) {
+        int r;
+
+        for (;;) {
+                r = pidref_wait(pidref, ret, WEXITED);
+                if (r != -EINTR)
+                        return r;
+        }
+}
+
 static void pidref_hash_func(const PidRef *pidref, struct siphash *state) {
         siphash24_compress_typesafe(pidref->pid, state);
 }
index a01d4cc85ba078b8f1200895051d9fbd1bccb4fc..0fbffb332064fb4debf95df57e7bef8ec67ec57c 100644 (file)
@@ -55,7 +55,19 @@ int pidref_new_from_pid(pid_t pid, PidRef **ret);
 
 int pidref_kill(const PidRef *pidref, int sig);
 int pidref_kill_and_sigcont(const PidRef *pidref, int sig);
-int pidref_sigqueue(const PidRef *pidfref, int sig, int value);
+int pidref_sigqueue(const PidRef *pidref, int sig, int value);
+
+int pidref_wait(const PidRef *pidref, siginfo_t *siginfo, int options);
+int pidref_wait_for_terminate(const PidRef *pidref, siginfo_t *ret);
+
+static inline void pidref_done_sigkill_wait(PidRef *pidref) {
+        if (!pidref_is_set(pidref))
+                return;
+
+        (void) pidref_kill(pidref, SIGKILL);
+        (void) pidref_wait_for_terminate(pidref, NULL);
+        pidref_done(pidref);
+}
 
 int pidref_verify(const PidRef *pidref);
 
index 201c5596ae9f969ee771395189b036c52a59d4e8..0f6cace426d4626ce579cf7ed8f3b8bfff28fec1 100644 (file)
@@ -730,6 +730,82 @@ int get_process_ppid(pid_t pid, pid_t *ret) {
         return 0;
 }
 
+int pid_get_start_time(pid_t pid, uint64_t *ret) {
+        _cleanup_free_ char *line = NULL;
+        const char *p;
+        int r;
+
+        assert(pid >= 0);
+
+        p = procfs_file_alloca(pid, "stat");
+        r = read_one_line_file(p, &line);
+        if (r == -ENOENT)
+                return -ESRCH;
+        if (r < 0)
+                return r;
+
+        /* Let's skip the pid and comm fields. The latter is enclosed in () but does not escape any () in its
+         * value, so let's skip over it manually */
+
+        p = strrchr(line, ')');
+        if (!p)
+                return -EIO;
+
+        p++;
+
+        unsigned long llu;
+
+        if (sscanf(p, " "
+                   "%*c "  /* state */
+                   "%*u " /* ppid */
+                   "%*u " /* pgrp */
+                   "%*u " /* session */
+                   "%*u " /* tty_nr */
+                   "%*u " /* tpgid */
+                   "%*u " /* flags */
+                   "%*u " /* minflt */
+                   "%*u " /* cminflt */
+                   "%*u " /* majflt */
+                   "%*u " /* cmajflt */
+                   "%*u " /* utime */
+                   "%*u " /* stime */
+                   "%*u " /* cutime */
+                   "%*u " /* cstime */
+                   "%*i " /* priority */
+                   "%*i " /* nice */
+                   "%*u " /* num_threads */
+                   "%*u " /* itrealvalue */
+                   "%lu ", /* starttime */
+                   &llu) != 1)
+                return -EIO;
+
+        if (ret)
+                *ret = llu;
+
+        return 0;
+}
+
+int pidref_get_start_time(const PidRef *pid, uint64_t *ret) {
+        uint64_t t;
+        int r;
+
+        if (!pidref_is_set(pid))
+                return -ESRCH;
+
+        r = pid_get_start_time(pid->pid, ret ? &t : NULL);
+        if (r < 0)
+                return r;
+
+        r = pidref_verify(pid);
+        if (r < 0)
+                return r;
+
+        if (ret)
+                *ret = t;
+
+        return 0;
+}
+
 int get_process_umask(pid_t pid, mode_t *ret) {
         _cleanup_free_ char *m = NULL;
         const char *p;
@@ -1112,8 +1188,10 @@ int pidref_is_alive(const PidRef *pidref) {
                 return -ESRCH;
 
         result = pid_is_alive(pidref->pid);
-        if (result < 0)
+        if (result < 0) {
+                assert(result != -ESRCH);
                 return result;
+        }
 
         r = pidref_verify(pidref);
         if (r == -ESRCH)
@@ -1589,6 +1667,9 @@ int safe_fork_full(
                                 log_full_errno(prio, r, "Failed to rearrange stdio fds: %m");
                                 _exit(EXIT_FAILURE);
                         }
+
+                        /* Turn off O_NONBLOCK on the fdio fds, in case it was left on */
+                        stdio_disable_nonblock();
                 } else {
                         r = make_null_stdio();
                         if (r < 0) {
@@ -1650,6 +1731,30 @@ int safe_fork_full(
         return 0;
 }
 
+int pidref_safe_fork_full(
+                const char *name,
+                const int stdio_fds[3],
+                const int except_fds[],
+                size_t n_except_fds,
+                ForkFlags flags,
+                PidRef *ret_pid) {
+
+        pid_t pid;
+        int r, q;
+
+        assert(!FLAGS_SET(flags, FORK_WAIT));
+
+        r = safe_fork_full(name, stdio_fds, except_fds, n_except_fds, flags, &pid);
+        if (r < 0)
+                return r;
+
+        q = pidref_set_pid(ret_pid, pid);
+        if (q < 0) /* Let's not fail for this, no matter what, the process exists after all, and that's key */
+                *ret_pid = PIDREF_MAKE_FROM_PID(pid);
+
+        return r;
+}
+
 int namespace_fork(
                 const char *outer_name,
                 const char *inner_name,
index af6cba13ebdd3f6e09422fe71b3ad475b57acf3f..de6a2bd2038cfff6897375c48dfbbb64a1c9aa2f 100644 (file)
@@ -54,6 +54,8 @@ int get_process_cwd(pid_t pid, char **ret);
 int get_process_root(pid_t pid, char **ret);
 int get_process_environ(pid_t pid, char **ret);
 int get_process_ppid(pid_t pid, pid_t *ret);
+int pid_get_start_time(pid_t pid, uint64_t *ret);
+int pidref_get_start_time(const PidRef* pid, uint64_t *ret);
 int get_process_umask(pid_t pid, mode_t *ret);
 
 int container_get_leader(const char *machine, pid_t *pid);
@@ -191,6 +193,18 @@ static inline int safe_fork(const char *name, ForkFlags flags, pid_t *ret_pid) {
         return safe_fork_full(name, NULL, NULL, 0, flags, ret_pid);
 }
 
+int pidref_safe_fork_full(
+                const char *name,
+                const int stdio_fds[3],
+                const int except_fds[],
+                size_t n_except_fds,
+                ForkFlags flags,
+                PidRef *ret_pid);
+
+static inline int pidref_safe_fork(const char *name, ForkFlags flags, PidRef *ret_pid) {
+        return pidref_safe_fork_full(name, NULL, NULL, 0, flags, ret_pid);
+}
+
 int namespace_fork(const char *outer_name, const char *inner_name, const int except_fds[], size_t n_except_fds, ForkFlags flags, int pidns_fd, int mntns_fd, int netns_fd, int userns_fd, int root_fd, pid_t *ret_pid);
 
 int set_oom_score_adjust(int value);
index 7e5a493f6b03b57e75c6be10368e588e39f75222..47ab0b81d887622cb769b229fa19da014e9a6205 100644 (file)
@@ -40,14 +40,14 @@ static void sigbus_push(void *addr) {
         }
 
         /* If we can't, make sure the queue size is out of bounds, to
-         * mark it as overflow */
+         * mark it as overflowed */
         for (;;) {
                 sig_atomic_t c;
 
                 __atomic_thread_fence(__ATOMIC_SEQ_CST);
                 c = n_sigbus_queue;
 
-                if (c > SIGBUS_QUEUE_MAX) /* already overflow */
+                if (c > SIGBUS_QUEUE_MAX) /* already overflowed */
                         return;
 
                 /* OK if we clobber c here, since we either immediately return
@@ -70,7 +70,7 @@ int sigbus_pop(void **ret) {
                 if (_likely_(c == 0))
                         return 0;
 
-                if (_unlikely_(c >= SIGBUS_QUEUE_MAX))
+                if (_unlikely_(c > SIGBUS_QUEUE_MAX))
                         return -EOVERFLOW;
 
                 for (u = 0; u < SIGBUS_QUEUE_MAX; u++) {
index 86472c88474f6de43ab1e9037b3991bbca6d8a68..0a6c87b023d189c0d7b0c0ba0d9db397e7db29ef 100644 (file)
@@ -628,28 +628,33 @@ int getsockname_pretty(int fd, char **ret) {
         return sockaddr_pretty(&sa.sa, salen, false, true, ret);
 }
 
-int socknameinfo_pretty(union sockaddr_union *sa, socklen_t salen, char **_ret) {
+int socknameinfo_pretty(const struct sockaddr *sa, socklen_t salen, char **ret) {
+        char host[NI_MAXHOST];
         int r;
-        char host[NI_MAXHOST], *ret;
 
-        assert(_ret);
+        assert(sa);
+        assert(salen > sizeof(sa_family_t));
 
-        r = getnameinfo(&sa->sa, salen, host, sizeof(host), NULL, 0, IDN_FLAGS);
+        r = getnameinfo(sa, salen, host, sizeof(host), /* service= */ NULL, /* service_len= */ 0, IDN_FLAGS);
         if (r != 0) {
-                int saved_errno = errno;
+                if (r == EAI_MEMORY)
+                        return log_oom_debug();
+                if (r == EAI_SYSTEM)
+                        log_debug_errno(errno, "getnameinfo() failed, ignoring: %m");
+                else
+                        log_debug("getnameinfo() failed, ignoring: %s", gai_strerror(r));
 
-                r = sockaddr_pretty(&sa->sa, salen, true, true, &ret);
-                if (r < 0)
-                        return r;
+                return sockaddr_pretty(sa, salen, /* translate_ipv6= */ true, /* include_port= */ true, ret);
+        }
 
-                log_debug_errno(saved_errno, "getnameinfo(%s) failed: %m", ret);
-        } else {
-                ret = strdup(host);
-                if (!ret)
+        if (ret) {
+                char *copy = strdup(host);
+                if (!copy)
                         return -ENOMEM;
+
+                *ret = copy;
         }
 
-        *_ret = ret;
         return 0;
 }
 
@@ -956,6 +961,21 @@ int getpeergroups(int fd, gid_t **ret) {
         return (int) n;
 }
 
+int getpeerpidfd(int fd) {
+        socklen_t n = sizeof(int);
+        int pidfd = -EBADF;
+
+        assert(fd >= 0);
+
+        if (getsockopt(fd, SOL_SOCKET, SO_PEERPIDFD, &pidfd, &n) < 0)
+                return -errno;
+
+        if (n != sizeof(int))
+                return -EIO;
+
+        return pidfd;
+}
+
 ssize_t send_many_fds_iov_sa(
                 int transport_fd,
                 int *fds_array, size_t n_fds_array,
@@ -1641,6 +1661,50 @@ int socket_address_parse_unix(SocketAddress *ret_address, const char *s) {
         return 0;
 }
 
+int vsock_parse_port(const char *s, unsigned *ret) {
+        int r;
+
+        assert(ret);
+
+        if (!s)
+                return -EINVAL;
+
+        unsigned u;
+        r = safe_atou(s, &u);
+        if (r < 0)
+                return r;
+
+        /* Port 0 is apparently valid and not special in AF_VSOCK (unlike on IP). But VMADDR_PORT_ANY
+         * (UINT32_MAX) is. Hence refuse that. */
+
+        if (u == VMADDR_PORT_ANY)
+                return -EINVAL;
+
+        *ret = u;
+        return 0;
+}
+
+int vsock_parse_cid(const char *s, unsigned *ret) {
+        assert(ret);
+
+        if (!s)
+                return -EINVAL;
+
+        /* Parsed an AF_VSOCK "CID". This is a 32bit entity, and the usual type is "unsigned". We recognize
+         * the three special CIDs as strings, and otherwise parse the numeric CIDs. */
+
+        if (streq(s, "hypervisor"))
+                *ret = VMADDR_CID_HYPERVISOR;
+        else if (streq(s, "local"))
+                *ret = VMADDR_CID_LOCAL;
+        else if (streq(s, "host"))
+                *ret = VMADDR_CID_HOST;
+        else
+                return safe_atou(s, ret);
+
+        return 0;
+}
+
 int socket_address_parse_vsock(SocketAddress *ret_address, const char *s) {
         /* AF_VSOCK socket in vsock:cid:port notation */
         _cleanup_free_ char *n = NULL;
@@ -1666,7 +1730,7 @@ int socket_address_parse_vsock(SocketAddress *ret_address, const char *s) {
         if (!e)
                 return -EINVAL;
 
-        r = safe_atou(e+1, &port);
+        r = vsock_parse_port(e+1, &port);
         if (r < 0)
                 return r;
 
@@ -1677,15 +1741,15 @@ int socket_address_parse_vsock(SocketAddress *ret_address, const char *s) {
         if (isempty(n))
                 cid = VMADDR_CID_ANY;
         else {
-                r = safe_atou(n, &cid);
+                r = vsock_parse_cid(n, &cid);
                 if (r < 0)
                         return r;
         }
 
         *ret_address = (SocketAddress) {
                 .sockaddr.vm = {
-                        .svm_cid = cid,
                         .svm_family = AF_VSOCK,
+                        .svm_cid = cid,
                         .svm_port = port,
                 },
                 .type = type,
@@ -1694,3 +1758,18 @@ int socket_address_parse_vsock(SocketAddress *ret_address, const char *s) {
 
         return 0;
 }
+
+int vsock_get_local_cid(unsigned *ret) {
+        _cleanup_close_ int vsock_fd = -EBADF;
+
+        assert(ret);
+
+        vsock_fd = open("/dev/vsock", O_RDONLY|O_CLOEXEC);
+        if (vsock_fd < 0)
+                return log_debug_errno(errno, "Failed to open /dev/vsock: %m");
+
+        if (ioctl(vsock_fd, IOCTL_VM_SOCKETS_GET_LOCAL_CID, ret) < 0)
+                return log_debug_errno(errno, "Failed to query local AF_VSOCK CID: %m");
+
+        return 0;
+}
index 9a11df834d113ebd26a76a88458dc62d52eef185..86791608b4dc57b475a85b8ae7fcc9df202fa2a7 100644 (file)
@@ -113,7 +113,7 @@ int sockaddr_pretty(const struct sockaddr *_sa, socklen_t salen, bool translate_
 int getpeername_pretty(int fd, bool include_port, char **ret);
 int getsockname_pretty(int fd, char **ret);
 
-int socknameinfo_pretty(union sockaddr_union *sa, socklen_t salen, char **_ret);
+int socknameinfo_pretty(const struct sockaddr *sa, socklen_t salen, char **_ret);
 
 const char* socket_address_bind_ipv6_only_to_string(SocketAddressBindIPv6Only b) _const_;
 SocketAddressBindIPv6Only socket_address_bind_ipv6_only_from_string(const char *s) _pure_;
@@ -152,6 +152,7 @@ bool address_label_valid(const char *p);
 int getpeercred(int fd, struct ucred *ucred);
 int getpeersec(int fd, char **ret);
 int getpeergroups(int fd, gid_t **ret);
+int getpeerpidfd(int fd);
 
 ssize_t send_many_fds_iov_sa(
                 int transport_fd,
@@ -373,6 +374,9 @@ int socket_get_mtu(int fd, int af, size_t *ret);
 
 int connect_unix_path(int fd, int dir_fd, const char *path);
 
+int vsock_parse_port(const char *s, unsigned *ret);
+int vsock_parse_cid(const char *s, unsigned *ret);
+
 /* Parses AF_UNIX and AF_VSOCK addresses. AF_INET[6] require some netlink calls, so it cannot be in
  * src/basic/ and is done from 'socket_local_address from src/shared/. Return -EPROTO in case of
  * protocol mismatch. */
@@ -385,3 +389,5 @@ int socket_address_parse_vsock(SocketAddress *ret_address, const char *s);
  * /proc/sys/net/core/somaxconn anyway, thus by setting this to unbounded we just make that sysctl file
  * authoritative. */
 #define SOMAXCONN_DELUXE INT_MAX
+
+int vsock_get_local_cid(unsigned *ret);
index 9bcd63d3e9461e270a4e44d70c124a1e13732fdc..51715668fe718f3447ae802c43a55cfd877728d2 100644 (file)
@@ -516,3 +516,25 @@ const char* inode_type_to_string(mode_t m) {
 
         return NULL;
 }
+
+mode_t inode_type_from_string(const char *s) {
+        if (!s)
+                return MODE_INVALID;
+
+        if (streq(s, "reg"))
+                return S_IFREG;
+        if (streq(s, "dir"))
+                return S_IFDIR;
+        if (streq(s, "lnk"))
+                return S_IFLNK;
+        if (streq(s, "chr"))
+                return S_IFCHR;
+        if (streq(s, "blk"))
+                return S_IFBLK;
+        if (streq(s, "fifo"))
+                return S_IFIFO;
+        if (streq(s, "sock"))
+                return S_IFSOCK;
+
+        return MODE_INVALID;
+}
index ae0aaf8f512a4051e482aef0233c61dd3649cfa4..dc11a85f6265324ab42baf9991a4574d86b46cff 100644 (file)
@@ -12,6 +12,7 @@
 #include "macro.h"
 #include "missing_stat.h"
 #include "siphash24.h"
+#include "time-util.h"
 
 int is_symlink(const char *path);
 int is_dir_full(int atfd, const char *fname, bool follow);
@@ -109,8 +110,16 @@ int xstatfsat(int dir_fd, const char *path, struct statfs *ret);
         } var
 #endif
 
+static inline usec_t statx_timestamp_load(const struct statx_timestamp *ts) {
+        return timespec_load(&(const struct timespec) { .tv_sec = ts->tv_sec, .tv_nsec = ts->tv_nsec });
+}
+static inline nsec_t statx_timestamp_load_nsec(const struct statx_timestamp *ts) {
+        return timespec_load_nsec(&(const struct timespec) { .tv_sec = ts->tv_sec, .tv_nsec = ts->tv_nsec });
+}
+
 void inode_hash_func(const struct stat *q, struct siphash *state);
 int inode_compare_func(const struct stat *a, const struct stat *b);
 extern const struct hash_ops inode_hash_ops;
 
 const char* inode_type_to_string(mode_t m);
+mode_t inode_type_from_string(const char *s);
index 7329bfacdf05b91ca4ad2c0f8b320644163fc6b2..38ca04b8597b873fd4456215027db17ad998b739 100644 (file)
@@ -1420,18 +1420,6 @@ char *find_line_startswith(const char *haystack, const char *needle) {
         return p + strlen(needle);
 }
 
-char *startswith_strv(const char *string, char **strv) {
-        char *found = NULL;
-
-        STRV_FOREACH(i, strv) {
-                found = startswith(string, *i);
-                if (found)
-                        break;
-        }
-
-        return found;
-}
-
 bool version_is_valid(const char *s) {
         if (isempty(s))
                 return false;
@@ -1519,3 +1507,26 @@ ssize_t strlevenshtein(const char *x, const char *y) {
 
         return t1[yl];
 }
+
+char *strrstr(const char *haystack, const char *needle) {
+        const char *f = NULL;
+        size_t l;
+
+        /* Like strstr() but returns the last rather than the first occurrence of "needle" in "haystack". */
+
+        if (!haystack || !needle)
+                return NULL;
+
+        l = strlen(needle);
+
+        /* Special case: for the empty string we return the very last possible occurrence, i.e. *after* the
+         * last char, not before. */
+        if (l == 0)
+                return strchr(haystack, 0);
+
+        for (const char *p = haystack; *p; p++)
+                if (strneq(p, needle, l))
+                        f = p;
+
+        return (char*) f;
+}
index b6d8be30834e5adbfa357e80d8734bfea698c71b..e162765aa7174a8113952e2b606b5c22b09cb482 100644 (file)
@@ -291,11 +291,6 @@ char *strdupcspn(const char *a, const char *reject);
 
 char *find_line_startswith(const char *haystack, const char *needle);
 
-char *startswith_strv(const char *string, char **strv);
-
-#define STARTSWITH_SET(p, ...)                                  \
-        startswith_strv(p, STRV_MAKE(__VA_ARGS__))
-
 bool version_is_valid(const char *s);
 
 bool version_is_valid_versionspec(const char *s);
@@ -322,3 +317,5 @@ static inline int strdup_or_null(const char *s, char **ret) {
         *ret = c;
         return 1;
 }
+
+char *strrstr(const char *haystack, const char *needle);
index c2109d35bc25ed42e7898965512cc2a3bd87cab2..908e9e251398a15e5e95b01622dd003c7d4b0f6c 100644 (file)
@@ -706,6 +706,26 @@ int strv_extendf(char ***l, const char *format, ...) {
         return strv_consume(l, x);
 }
 
+char* startswith_strv(const char *s, char * const *l) {
+        STRV_FOREACH(i, l) {
+                char *found = startswith(s, *i);
+                if (found)
+                        return found;
+        }
+
+        return NULL;
+}
+
+char* endswith_strv(const char *s, char * const *l) {
+        STRV_FOREACH(i, l) {
+                char *found = endswith(s, *i);
+                if (found)
+                        return found;
+        }
+
+        return NULL;
+}
+
 char** strv_reverse(char **l) {
         size_t n;
 
index fec26167339dbd37e4c793331b54d9e1c2e46203..f1a8bc49109540bfbbecf94d4ce9f52e997937b4 100644 (file)
@@ -159,6 +159,16 @@ static inline void strv_print(char * const *l) {
         strv_print_full(l, NULL);
 }
 
+char* startswith_strv(const char *s, char * const *l);
+
+#define STARTSWITH_SET(p, ...)                                  \
+        startswith_strv(p, STRV_MAKE(__VA_ARGS__))
+
+char* endswith_strv(const char *s, char * const *l);
+
+#define ENDSWITH_SET(p, ...)                                    \
+        endswith_strv(p, STRV_MAKE(__VA_ARGS__))
+
 #define strv_from_stdarg_alloca(first)                          \
         ({                                                      \
                 char **_l;                                      \
@@ -202,18 +212,6 @@ static inline void strv_print(char * const *l) {
                 _x && strv_contains_case(STRV_MAKE(__VA_ARGS__), _x); \
         })
 
-#define ENDSWITH_SET(p, ...)                                    \
-        ({                                                      \
-                const char *_p = (p);                           \
-                char *_found = NULL;                            \
-                STRV_FOREACH(_i, STRV_MAKE(__VA_ARGS__)) {      \
-                        _found = endswith(_p, *_i);             \
-                        if (_found)                             \
-                                break;                          \
-                }                                               \
-                _found;                                         \
-        })
-
 #define _FOREACH_STRING(uniq, x, y, ...)                                \
         for (const char *x, * const*UNIQ_T(l, uniq) = STRV_MAKE_CONST(({ x = y; }), ##__VA_ARGS__); \
              x;                                                         \
similarity index 99%
rename from src/basic/uid-alloc-range.c
rename to src/basic/uid-classification.c
index 669cb6d56f7be263dc712ab8732b068e95881c17..e2d2cebc6de271ad53049568e0cf44127b8aa9fd 100644 (file)
@@ -5,7 +5,7 @@
 #include "fileio.h"
 #include "missing_threads.h"
 #include "string-util.h"
-#include "uid-alloc-range.h"
+#include "uid-classification.h"
 #include "user-util.h"
 
 static const UGIDAllocationRange default_ugid_allocation_range = {
index d933d9fa5c1faf0c3e377423f1e6eb3c37c4edea..90eb0cd9112591dbf662c5ff67d354e5cc739594 100644 (file)
@@ -15,7 +15,7 @@
 #include "uid-range.h"
 #include "user-util.h"
 
-UidRange *uid_range_free(UidRange *range) {
+UIDRange *uid_range_free(UIDRange *range) {
         if (!range)
                 return NULL;
 
@@ -23,14 +23,14 @@ UidRange *uid_range_free(UidRange *range) {
         return mfree(range);
 }
 
-static bool uid_range_entry_intersect(const UidRangeEntry *a, const UidRangeEntry *b) {
+static bool uid_range_entry_intersect(const UIDRangeEntry *a, const UIDRangeEntry *b) {
         assert(a);
         assert(b);
 
         return a->start <= b->start + b->nr && a->start + a->nr >= b->start;
 }
 
-static int uid_range_entry_compare(const UidRangeEntry *a, const UidRangeEntry *b) {
+static int uid_range_entry_compare(const UIDRangeEntry *a, const UIDRangeEntry *b) {
         int r;
 
         assert(a);
@@ -43,7 +43,7 @@ static int uid_range_entry_compare(const UidRangeEntry *a, const UidRangeEntry *
         return CMP(a->nr, b->nr);
 }
 
-static void uid_range_coalesce(UidRange *range) {
+static void uid_range_coalesce(UIDRange *range) {
         assert(range);
 
         if (range->n_entries <= 0)
@@ -52,10 +52,10 @@ static void uid_range_coalesce(UidRange *range) {
         typesafe_qsort(range->entries, range->n_entries, uid_range_entry_compare);
 
         for (size_t i = 0; i < range->n_entries; i++) {
-                UidRangeEntry *x = range->entries + i;
+                UIDRangeEntry *x = range->entries + i;
 
                 for (size_t j = i + 1; j < range->n_entries; j++) {
-                        UidRangeEntry *y = range->entries + j;
+                        UIDRangeEntry *y = range->entries + j;
                         uid_t begin, end;
 
                         if (!uid_range_entry_intersect(x, y))
@@ -68,7 +68,7 @@ static void uid_range_coalesce(UidRange *range) {
                         x->nr = end - begin;
 
                         if (range->n_entries > j + 1)
-                                memmove(y, y + 1, sizeof(UidRangeEntry) * (range->n_entries - j - 1));
+                                memmove(y, y + 1, sizeof(UIDRangeEntry) * (range->n_entries - j - 1));
 
                         range->n_entries--;
                         j--;
@@ -76,9 +76,9 @@ static void uid_range_coalesce(UidRange *range) {
         }
 }
 
-int uid_range_add_internal(UidRange **range, uid_t start, uid_t nr, bool coalesce) {
-        _cleanup_(uid_range_freep) UidRange *range_new = NULL;
-        UidRange *p;
+int uid_range_add_internal(UIDRange **range, uid_t start, uid_t nr, bool coalesce) {
+        _cleanup_(uid_range_freep) UIDRange *range_new = NULL;
+        UIDRange *p;
 
         assert(range);
 
@@ -91,7 +91,7 @@ int uid_range_add_internal(UidRange **range, uid_t start, uid_t nr, bool coalesc
         if (*range)
                 p = *range;
         else {
-                range_new = new0(UidRange, 1);
+                range_new = new0(UIDRange, 1);
                 if (!range_new)
                         return -ENOMEM;
 
@@ -101,7 +101,7 @@ int uid_range_add_internal(UidRange **range, uid_t start, uid_t nr, bool coalesc
         if (!GREEDY_REALLOC(p->entries, p->n_entries + 1))
                 return -ENOMEM;
 
-        p->entries[p->n_entries++] = (UidRangeEntry) {
+        p->entries[p->n_entries++] = (UIDRangeEntry) {
                 .start = start,
                 .nr = nr,
         };
@@ -115,7 +115,7 @@ int uid_range_add_internal(UidRange **range, uid_t start, uid_t nr, bool coalesc
         return 0;
 }
 
-int uid_range_add_str(UidRange **range, const char *s) {
+int uid_range_add_str(UIDRange **range, const char *s) {
         uid_t start, end;
         int r;
 
@@ -129,7 +129,7 @@ int uid_range_add_str(UidRange **range, const char *s) {
         return uid_range_add_internal(range, start, end - start + 1, /* coalesce = */ true);
 }
 
-int uid_range_next_lower(const UidRange *range, uid_t *uid) {
+int uid_range_next_lower(const UIDRange *range, uid_t *uid) {
         uid_t closest = UID_INVALID, candidate;
 
         assert(range);
@@ -162,7 +162,7 @@ int uid_range_next_lower(const UidRange *range, uid_t *uid) {
         return 1;
 }
 
-bool uid_range_covers(const UidRange *range, uid_t start, uid_t nr) {
+bool uid_range_covers(const UIDRange *range, uid_t start, uid_t nr) {
         if (nr == 0) /* empty range? always covered... */
                 return true;
 
@@ -204,8 +204,8 @@ int uid_map_read_one(FILE *f, uid_t *ret_base, uid_t *ret_shift, uid_t *ret_rang
         return 0;
 }
 
-int uid_range_load_userns(UidRange **ret, const char *path) {
-        _cleanup_(uid_range_freep) UidRange *range = NULL;
+int uid_range_load_userns(UIDRange **ret, const char *path) {
+        _cleanup_(uid_range_freep) UIDRange *range = NULL;
         _cleanup_fclose_ FILE *f = NULL;
         int r;
 
@@ -230,7 +230,7 @@ int uid_range_load_userns(UidRange **ret, const char *path) {
                 return r;
         }
 
-        range = new0(UidRange, 1);
+        range = new0(UIDRange, 1);
         if (!range)
                 return -ENOMEM;
 
index bfe78926698c150cb8b4b0d554c956783c4e51d5..5c4aac3726089b8869f6a59dd785c29e96c9a75a 100644 (file)
@@ -6,31 +6,31 @@
 
 #include "macro.h"
 
-typedef struct UidRangeEntry {
+typedef struct UIDRangeEntry {
         uid_t start, nr;
-} UidRangeEntry;
+} UIDRangeEntry;
 
-typedef struct UidRange {
-        UidRangeEntry *entries;
+typedef struct UIDRange {
+        UIDRangeEntry *entries;
         size_t n_entries;
-} UidRange;
+} UIDRange;
 
-UidRange *uid_range_free(UidRange *range);
-DEFINE_TRIVIAL_CLEANUP_FUNC(UidRange*, uid_range_free);
+UIDRange *uid_range_free(UIDRange *range);
+DEFINE_TRIVIAL_CLEANUP_FUNC(UIDRange*, uid_range_free);
 
-int uid_range_add_internal(UidRange **range, uid_t start, uid_t nr, bool coalesce);
-static inline int uid_range_add(UidRange **range, uid_t start, uid_t nr) {
+int uid_range_add_internal(UIDRange **range, uid_t start, uid_t nr, bool coalesce);
+static inline int uid_range_add(UIDRange **range, uid_t start, uid_t nr) {
         return uid_range_add_internal(range, start, nr, true);
 }
-int uid_range_add_str(UidRange **range, const char *s);
+int uid_range_add_str(UIDRange **range, const char *s);
 
-int uid_range_next_lower(const UidRange *range, uid_t *uid);
+int uid_range_next_lower(const UIDRange *range, uid_t *uid);
 
-bool uid_range_covers(const UidRange *range, uid_t start, uid_t nr);
-static inline bool uid_range_contains(const UidRange *range, uid_t uid) {
+bool uid_range_covers(const UIDRange *range, uid_t start, uid_t nr);
+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);
+int uid_range_load_userns(UIDRange **ret, const char *path);
index 09aebabcd5e35a4d3fc503eb202a531dd86d60d9..9c1410886f49fe248245da51f8dde5f5dea8c433 100644 (file)
@@ -169,22 +169,23 @@ static Virtualization detect_vm_dmi_vendor(void) {
                 const char *vendor;
                 Virtualization id;
         } dmi_vendor_table[] = {
-                { "KVM",                  VIRTUALIZATION_KVM       },
-                { "OpenStack",            VIRTUALIZATION_KVM       }, /* Detect OpenStack instance as KVM in non x86 architecture */
-                { "KubeVirt",             VIRTUALIZATION_KVM       }, /* Detect KubeVirt instance as KVM in non x86 architecture */
-                { "Amazon EC2",           VIRTUALIZATION_AMAZON    },
-                { "QEMU",                 VIRTUALIZATION_QEMU      },
-                { "VMware",               VIRTUALIZATION_VMWARE    }, /* https://kb.vmware.com/s/article/1009458 */
-                { "VMW",                  VIRTUALIZATION_VMWARE    },
-                { "innotek GmbH",         VIRTUALIZATION_ORACLE    },
-                { "VirtualBox",           VIRTUALIZATION_ORACLE    },
-                { "Xen",                  VIRTUALIZATION_XEN       },
-                { "Bochs",                VIRTUALIZATION_BOCHS     },
-                { "Parallels",            VIRTUALIZATION_PARALLELS },
+                { "KVM",                   VIRTUALIZATION_KVM       },
+                { "OpenStack",             VIRTUALIZATION_KVM       }, /* Detect OpenStack instance as KVM in non x86 architecture */
+                { "KubeVirt",              VIRTUALIZATION_KVM       }, /* Detect KubeVirt instance as KVM in non x86 architecture */
+                { "Amazon EC2",            VIRTUALIZATION_AMAZON    },
+                { "QEMU",                  VIRTUALIZATION_QEMU      },
+                { "VMware",                VIRTUALIZATION_VMWARE    }, /* https://kb.vmware.com/s/article/1009458 */
+                { "VMW",                   VIRTUALIZATION_VMWARE    },
+                { "innotek GmbH",          VIRTUALIZATION_ORACLE    },
+                { "VirtualBox",            VIRTUALIZATION_ORACLE    },
+                { "Xen",                   VIRTUALIZATION_XEN       },
+                { "Bochs",                 VIRTUALIZATION_BOCHS     },
+                { "Parallels",             VIRTUALIZATION_PARALLELS },
                 /* https://wiki.freebsd.org/bhyve */
-                { "BHYVE",                VIRTUALIZATION_BHYVE     },
-                { "Hyper-V",              VIRTUALIZATION_MICROSOFT },
-                { "Apple Virtualization", VIRTUALIZATION_APPLE     },
+                { "BHYVE",                 VIRTUALIZATION_BHYVE     },
+                { "Hyper-V",               VIRTUALIZATION_MICROSOFT },
+                { "Apple Virtualization",  VIRTUALIZATION_APPLE     },
+                { "Google Compute Engine", VIRTUALIZATION_GOOGLE    }, /* https://cloud.google.com/run/docs/container-contract#sandbox */
         };
         int r;
 
@@ -997,7 +998,7 @@ static bool real_has_cpu_with_flag(const char *flag) {
                         return true;
         }
 
-        if (__get_cpuid(7, &eax, &ebx, &ecx, &edx)) {
+        if (__get_cpuid_count(7, 0, &eax, &ebx, &ecx, &edx)) {
                 if (given_flag_in_set(flag, leaf7_ebx, ELEMENTSOF(leaf7_ebx), ebx))
                         return true;
         }
@@ -1046,6 +1047,7 @@ static const char *const virtualization_table[_VIRTUALIZATION_MAX] = {
         [VIRTUALIZATION_POWERVM]         = "powervm",
         [VIRTUALIZATION_APPLE]           = "apple",
         [VIRTUALIZATION_SRE]             = "sre",
+        [VIRTUALIZATION_GOOGLE]          = "google",
         [VIRTUALIZATION_VM_OTHER]        = "vm-other",
 
         [VIRTUALIZATION_SYSTEMD_NSPAWN]  = "systemd-nspawn",
index d49f3237e816f98b1ef25dda6897ca5ee3aeef51..dea39e4e763c1fe85f5e9825d2ca24f5590f6080 100644 (file)
@@ -27,6 +27,7 @@ typedef enum Virtualization {
         VIRTUALIZATION_POWERVM,
         VIRTUALIZATION_APPLE,
         VIRTUALIZATION_SRE,
+        VIRTUALIZATION_GOOGLE,
         VIRTUALIZATION_VM_OTHER,
         VIRTUALIZATION_VM_LAST = VIRTUALIZATION_VM_OTHER,
 
index 5c5071eabe5c7d948af5733700ae2da82e12cc57..262ef89f1a75cceef4ec88fd456e7d94ea42d850 100644 (file)
@@ -419,7 +419,7 @@ static int measure_kernel(PcrState *pcr_states, size_t n) {
                         if (r < 0)
                                 return log_error_errno(r, "Failed to read '%s': %m", p);
 
-                        r = unhexmem(strstrip(s), SIZE_MAX, &v, &sz);
+                        r = unhexmem(strstrip(s), &v, &sz);
                         if (r < 0)
                                 return log_error_errno(r, "Failed to decode PCR value '%s': %m", s);
 
@@ -995,7 +995,7 @@ static int verb_status(int argc, char *argv[], void *userdata) {
                         if (r < 0)
                                 return log_error_errno(r, "Failed to read '%s': %m", p);
 
-                        r = unhexmem(strstrip(s), SIZE_MAX, &h, &l);
+                        r = unhexmem(strstrip(s), &h, &l);
                         if (r < 0)
                                 return log_error_errno(r, "Failed to decode PCR value '%s': %m", s);
 
index 311a4197aa1434067f9e20980105c0df57ebb560..d5df93940b9dd79985bfc300e0e2233b293e913b 100644 (file)
@@ -190,33 +190,35 @@ void cgroup_context_init(CGroupContext *c) {
         };
 }
 
-int cgroup_context_add_io_device_weight_dup(CGroupContext *c, CGroupIODeviceWeight *w) {
+int cgroup_context_add_io_device_weight_dup(CGroupContext *c, const CGroupIODeviceWeight *w) {
         _cleanup_free_ CGroupIODeviceWeight *n = NULL;
 
         assert(c);
         assert(w);
 
-        n = new0(CGroupIODeviceWeight, 1);
+        n = new(CGroupIODeviceWeight, 1);
         if (!n)
                 return -ENOMEM;
 
-        n->path = strdup(w->path);
+        *n = (CGroupIODeviceWeight) {
+                .path = strdup(w->path),
+                .weight = w->weight,
+        };
         if (!n->path)
                 return -ENOMEM;
-        n->weight = w->weight;
 
         LIST_PREPEND(device_weights, c->io_device_weights, TAKE_PTR(n));
         return 0;
 }
 
-int cgroup_context_add_io_device_limit_dup(CGroupContext *c, CGroupIODeviceLimit *l) {
+int cgroup_context_add_io_device_limit_dup(CGroupContext *c, const CGroupIODeviceLimit *l) {
         _cleanup_free_ CGroupIODeviceLimit *n = NULL;
 
         assert(c);
         assert(l);
 
         n = new0(CGroupIODeviceLimit, 1);
-        if (!l)
+        if (!n)
                 return -ENOMEM;
 
         n->path = strdup(l->path);
@@ -230,53 +232,55 @@ int cgroup_context_add_io_device_limit_dup(CGroupContext *c, CGroupIODeviceLimit
         return 0;
 }
 
-int cgroup_context_add_io_device_latency_dup(CGroupContext *c, CGroupIODeviceLatency *l) {
+int cgroup_context_add_io_device_latency_dup(CGroupContext *c, const CGroupIODeviceLatency *l) {
         _cleanup_free_ CGroupIODeviceLatency *n = NULL;
 
         assert(c);
         assert(l);
 
-        n = new0(CGroupIODeviceLatency, 1);
+        n = new(CGroupIODeviceLatency, 1);
         if (!n)
                 return -ENOMEM;
 
-        n->path = strdup(l->path);
+        *n = (CGroupIODeviceLatency) {
+                .path = strdup(l->path),
+                .target_usec = l->target_usec,
+        };
         if (!n->path)
                 return -ENOMEM;
 
-        n->target_usec = l->target_usec;
-
         LIST_PREPEND(device_latencies, c->io_device_latencies, TAKE_PTR(n));
         return 0;
 }
 
-int cgroup_context_add_block_io_device_weight_dup(CGroupContext *c, CGroupBlockIODeviceWeight *w) {
+int cgroup_context_add_block_io_device_weight_dup(CGroupContext *c, const CGroupBlockIODeviceWeight *w) {
         _cleanup_free_ CGroupBlockIODeviceWeight *n = NULL;
 
         assert(c);
         assert(w);
 
-        n = new0(CGroupBlockIODeviceWeight, 1);
+        n = new(CGroupBlockIODeviceWeight, 1);
         if (!n)
                 return -ENOMEM;
 
-        n->path = strdup(w->path);
+        *n = (CGroupBlockIODeviceWeight) {
+                .path = strdup(w->path),
+                .weight = w->weight,
+        };
         if (!n->path)
                 return -ENOMEM;
 
-        n->weight = w->weight;
-
         LIST_PREPEND(device_weights, c->blockio_device_weights, TAKE_PTR(n));
         return 0;
 }
 
-int cgroup_context_add_block_io_device_bandwidth_dup(CGroupContext *c, CGroupBlockIODeviceBandwidth *b) {
+int cgroup_context_add_block_io_device_bandwidth_dup(CGroupContext *c, const CGroupBlockIODeviceBandwidth *b) {
         _cleanup_free_ CGroupBlockIODeviceBandwidth *n = NULL;
 
         assert(c);
         assert(b);
 
-        n = new0(CGroupBlockIODeviceBandwidth, 1);
+        n = new(CGroupBlockIODeviceBandwidth, 1);
         if (!n)
                 return -ENOMEM;
 
@@ -289,33 +293,34 @@ int cgroup_context_add_block_io_device_bandwidth_dup(CGroupContext *c, CGroupBlo
         return 0;
 }
 
-int cgroup_context_add_device_allow_dup(CGroupContext *c, CGroupDeviceAllow *a) {
+int cgroup_context_add_device_allow_dup(CGroupContext *c, const CGroupDeviceAllow *a) {
         _cleanup_free_ CGroupDeviceAllow *n = NULL;
 
         assert(c);
         assert(a);
 
-        n = new0(CGroupDeviceAllow, 1);
+        n = new(CGroupDeviceAllow, 1);
         if (!n)
                 return -ENOMEM;
 
-        n->path = strdup(a->path);
+        *n = (CGroupDeviceAllow) {
+                .path = strdup(a->path),
+                .permissions = a->permissions,
+        };
         if (!n->path)
                 return -ENOMEM;
 
-        n->permissions = a->permissions;
-
         LIST_PREPEND(device_allow, c->device_allow, TAKE_PTR(n));
         return 0;
 }
 
-static int cgroup_context_add_socket_bind_item_dup(CGroupContext *c, CGroupSocketBindItem *i, CGroupSocketBindItem *h) {
+static int cgroup_context_add_socket_bind_item_dup(CGroupContext *c, const CGroupSocketBindItem *i, CGroupSocketBindItem *h) {
         _cleanup_free_ CGroupSocketBindItem *n = NULL;
 
         assert(c);
         assert(i);
 
-        n = new0(CGroupSocketBindItem, 1);
+        n = new(CGroupSocketBindItem, 1);
         if (!n)
                 return -ENOMEM;
 
@@ -330,11 +335,11 @@ static int cgroup_context_add_socket_bind_item_dup(CGroupContext *c, CGroupSocke
         return 0;
 }
 
-int cgroup_context_add_socket_bind_item_allow_dup(CGroupContext *c, CGroupSocketBindItem *i) {
+int cgroup_context_add_socket_bind_item_allow_dup(CGroupContext *c, const CGroupSocketBindItem *i) {
         return cgroup_context_add_socket_bind_item_dup(c, i, c->socket_bind_allow);
 }
 
-int cgroup_context_add_socket_bind_item_deny_dup(CGroupContext *c, CGroupSocketBindItem *i) {
+int cgroup_context_add_socket_bind_item_deny_dup(CGroupContext *c, const CGroupSocketBindItem *i) {
         return cgroup_context_add_socket_bind_item_dup(c, i, c->socket_bind_deny);
 }
 
@@ -353,7 +358,7 @@ int cgroup_context_copy(CGroupContext *dst, const CGroupContext *src) {
         dst->tasks_accounting = src->tasks_accounting;
         dst->ip_accounting = src->ip_accounting;
 
-        dst->memory_oom_group = dst->memory_oom_group;
+        dst->memory_oom_group = src->memory_oom_group;
 
         dst->cpu_weight = src->cpu_weight;
         dst->startup_cpu_weight = src->startup_cpu_weight;
@@ -4555,6 +4560,57 @@ int unit_get_ip_accounting(
         return r;
 }
 
+static uint64_t unit_get_effective_limit_one(Unit *u, CGroupLimitType type) {
+        CGroupContext *cc;
+
+        assert(u);
+        assert(UNIT_HAS_CGROUP_CONTEXT(u));
+
+        if (unit_has_name(u, SPECIAL_ROOT_SLICE))
+                switch (type) {
+                        case CGROUP_LIMIT_MEMORY_MAX:
+                        case CGROUP_LIMIT_MEMORY_HIGH:
+                                return physical_memory();
+                        case CGROUP_LIMIT_TASKS_MAX:
+                                return system_tasks_max();
+                        default:
+                                assert_not_reached();
+                }
+
+        cc = ASSERT_PTR(unit_get_cgroup_context(u));
+        switch (type) {
+                /* Note: on legacy/hybrid hierarchies memory_max stays CGROUP_LIMIT_MAX unless configured
+                 * explicitly. Effective value of MemoryLimit= (cgroup v1) is not implemented. */
+                case CGROUP_LIMIT_MEMORY_MAX:
+                        return cc->memory_max;
+                case CGROUP_LIMIT_MEMORY_HIGH:
+                        return cc->memory_high;
+                case CGROUP_LIMIT_TASKS_MAX:
+                        return cgroup_tasks_max_resolve(&cc->tasks_max);
+                default:
+                        assert_not_reached();
+        }
+}
+
+int unit_get_effective_limit(Unit *u, CGroupLimitType type, uint64_t *ret) {
+        uint64_t infimum;
+
+        assert(u);
+        assert(ret);
+        assert(type >= 0);
+        assert(type < _CGROUP_LIMIT_TYPE_MAX);
+
+        if (!UNIT_HAS_CGROUP_CONTEXT(u))
+                return -EINVAL;
+
+        infimum = unit_get_effective_limit_one(u, type);
+        for (Unit *slice = UNIT_GET_SLICE(u); slice; slice = UNIT_GET_SLICE(slice))
+                infimum = MIN(infimum, unit_get_effective_limit_one(slice, type));
+
+        *ret = infimum;
+        return 0;
+}
+
 static int unit_get_io_accounting_raw(Unit *u, uint64_t ret[static _CGROUP_IO_ACCOUNTING_METRIC_MAX]) {
         static const char *const field_names[_CGROUP_IO_ACCOUNTING_METRIC_MAX] = {
                 [CGROUP_IO_READ_BYTES]       = "rbytes=",
@@ -4975,3 +5031,11 @@ static const char* const cgroup_memory_accounting_metric_table[_CGROUP_MEMORY_AC
 };
 
 DEFINE_STRING_TABLE_LOOKUP(cgroup_memory_accounting_metric, CGroupMemoryAccountingMetric);
+
+static const char *const cgroup_limit_type_table[_CGROUP_LIMIT_TYPE_MAX] = {
+        [CGROUP_LIMIT_MEMORY_MAX]  = "EffectiveMemoryMax",
+        [CGROUP_LIMIT_MEMORY_HIGH] = "EffectiveMemoryHigh",
+        [CGROUP_LIMIT_TASKS_MAX]   = "EffectiveTasksMax",
+};
+
+DEFINE_STRING_TABLE_LOOKUP(cgroup_limit_type, CGroupLimitType);
index ecd6759983f692956dceeeb43bf256bc2199a985..c56979de01fd331564b2af6716b2ae3aa5111892 100644 (file)
@@ -277,6 +277,15 @@ typedef enum CGroupMemoryAccountingMetric {
         _CGROUP_MEMORY_ACCOUNTING_METRIC_INVALID = -EINVAL,
 } CGroupMemoryAccountingMetric;
 
+/* Used for limits whose value sets have infimum */
+typedef enum CGroupLimitType {
+        CGROUP_LIMIT_MEMORY_MAX,
+        CGROUP_LIMIT_MEMORY_HIGH,
+        CGROUP_LIMIT_TASKS_MAX,
+        _CGROUP_LIMIT_TYPE_MAX,
+        _CGROUP_LIMIT_INVALID = -EINVAL,
+} CGroupLimitType;
+
 typedef struct Unit Unit;
 typedef struct Manager Manager;
 typedef enum ManagerState ManagerState;
@@ -311,18 +320,17 @@ static inline bool cgroup_context_want_memory_pressure(const CGroupContext *c) {
 int cgroup_context_add_device_allow(CGroupContext *c, const char *dev, CGroupDevicePermissions p);
 int cgroup_context_add_or_update_device_allow(CGroupContext *c, const char *dev, CGroupDevicePermissions p);
 int cgroup_context_add_bpf_foreign_program(CGroupContext *c, uint32_t attach_type, const char *path);
-int cgroup_context_add_io_device_limit_dup(CGroupContext *c, CGroupIODeviceLimit *l);
-int cgroup_context_add_io_device_weight_dup(CGroupContext *c, CGroupIODeviceWeight *w);
-int cgroup_context_add_io_device_latency_dup(CGroupContext *c, CGroupIODeviceLatency *l);
-int cgroup_context_add_block_io_device_weight_dup(CGroupContext *c, CGroupBlockIODeviceWeight *w);
-int cgroup_context_add_block_io_device_bandwidth_dup(CGroupContext *c, CGroupBlockIODeviceBandwidth *b);
-int cgroup_context_add_device_allow_dup(CGroupContext *c, CGroupDeviceAllow *a);
-int cgroup_context_add_socket_bind_item_allow_dup(CGroupContext *c, CGroupSocketBindItem *i);
-int cgroup_context_add_socket_bind_item_deny_dup(CGroupContext *c, CGroupSocketBindItem *i);
-
-static inline int cgroup_context_add_bpf_foreign_program_dup(CGroupContext *c, CGroupBPFForeignProgram *p) {
+static inline int cgroup_context_add_bpf_foreign_program_dup(CGroupContext *c, const CGroupBPFForeignProgram *p) {
         return cgroup_context_add_bpf_foreign_program(c, p->attach_type, p->bpffs_path);
 }
+int cgroup_context_add_io_device_limit_dup(CGroupContext *c, const CGroupIODeviceLimit *l);
+int cgroup_context_add_io_device_weight_dup(CGroupContext *c, const CGroupIODeviceWeight *w);
+int cgroup_context_add_io_device_latency_dup(CGroupContext *c, const CGroupIODeviceLatency *l);
+int cgroup_context_add_block_io_device_weight_dup(CGroupContext *c, const CGroupBlockIODeviceWeight *w);
+int cgroup_context_add_block_io_device_bandwidth_dup(CGroupContext *c, const CGroupBlockIODeviceBandwidth *b);
+int cgroup_context_add_device_allow_dup(CGroupContext *c, const CGroupDeviceAllow *a);
+int cgroup_context_add_socket_bind_item_allow_dup(CGroupContext *c, const CGroupSocketBindItem *i);
+int cgroup_context_add_socket_bind_item_deny_dup(CGroupContext *c, const CGroupSocketBindItem *i);
 
 void unit_modify_nft_set(Unit *u, bool add);
 
@@ -388,6 +396,7 @@ int unit_get_tasks_current(Unit *u, uint64_t *ret);
 int unit_get_cpu_usage(Unit *u, nsec_t *ret);
 int unit_get_io_accounting(Unit *u, CGroupIOAccountingMetric metric, bool allow_cache, uint64_t *ret);
 int unit_get_ip_accounting(Unit *u, CGroupIPAccountingMetric metric, uint64_t *ret);
+int unit_get_effective_limit(Unit *u, CGroupLimitType type, uint64_t *ret);
 
 int unit_reset_cpu_accounting(Unit *u);
 void unit_reset_memory_accounting_last(Unit *u);
@@ -439,5 +448,8 @@ CGroupIPAccountingMetric cgroup_ip_accounting_metric_from_string(const char *s)
 const char* cgroup_io_accounting_metric_to_string(CGroupIOAccountingMetric m) _const_;
 CGroupIOAccountingMetric cgroup_io_accounting_metric_from_string(const char *s) _pure_;
 
+const char* cgroup_limit_type_to_string(CGroupLimitType m) _const_;
+CGroupLimitType cgroup_limit_type_from_string(const char *s) _pure_;
+
 const char* cgroup_memory_accounting_metric_to_string(CGroupMemoryAccountingMetric m) _const_;
 CGroupMemoryAccountingMetric cgroup_memory_accounting_metric_from_string(const char *s) _pure_;
index a62133a4c21fbc662626b77984e7740ca2d15a03..65524912613a70e6b55d3ee207e7cab256e18e35 100644 (file)
@@ -3004,7 +3004,7 @@ static int aux_scope_from_message(Manager *m, sd_bus_message *message, Unit **re
 
                 unit = manager_get_unit_by_pidref(m, &p);
                 if (!unit) {
-                        log_unit_warning_errno(from, SYNTHETIC_ERRNO(ENOENT), "Failed to get unit from PIDFD, ingoring: %m");
+                        log_unit_warning_errno(from, SYNTHETIC_ERRNO(ENOENT), "Failed to get unit from PIDFD, ignoring: %m");
                         continue;
                 }
 
index 7dbbdd07f50870609fea6dd2dfb9c129fcd67149..2278c3ee2fa4c15f3e3da71e036f67455d1d27b2 100644 (file)
@@ -6,6 +6,7 @@
 #include "dbus-kill.h"
 #include "dbus-mount.h"
 #include "dbus-util.h"
+#include "fstab-util.h"
 #include "mount.h"
 #include "string-util.h"
 #include "unit.h"
@@ -88,6 +89,7 @@ static int bus_mount_set_transient_property(
                 sd_bus_error *error) {
 
         Unit *u = UNIT(m);
+        int r;
 
         assert(m);
         assert(name);
@@ -98,8 +100,31 @@ static int bus_mount_set_transient_property(
         if (streq(name, "Where"))
                 return bus_set_transient_path(u, name, &m->where, message, flags, error);
 
-        if (streq(name, "What"))
-                return bus_set_transient_string(u, name, &m->parameters_fragment.what, message, flags, error);
+        if (streq(name, "What")) {
+                _cleanup_free_ char *path = NULL;
+                const char *v;
+
+                r = sd_bus_message_read(message, "s", &v);
+                if (r < 0)
+                        return r;
+
+                if (!isempty(v)) {
+                        path = fstab_node_to_udev_node(v);
+                        if (!path)
+                                return -ENOMEM;
+
+                        /* path_is_valid is not used - see the comment for config_parse_mount_node */
+                        if (strlen(path) >= PATH_MAX)
+                                return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Resolved What=%s too long", path);
+                }
+
+                if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
+                        free_and_replace(m->parameters_fragment.what, path);
+                        unit_write_settingf(u, flags|UNIT_ESCAPE_SPECIFIERS, name, "What=%s", strempty(m->parameters_fragment.what));
+                }
+
+                return 1;
+        }
 
         if (streq(name, "Options"))
                 return bus_set_transient_string(u, name, &m->parameters_fragment.options, message, flags, error);
index 8b4983dcb5f9e874c9fd3a52c23b71eb3028e0e1..3aeb85e45248e146f4bbc6a9ee0adba6647f20a9 100644 (file)
@@ -1426,6 +1426,28 @@ static int property_get_io_counter(
         return sd_bus_message_append(reply, "t", value);
 }
 
+static int property_get_effective_limit(
+                sd_bus *bus,
+                const char *path,
+                const char *interface,
+                const char *property,
+                sd_bus_message *reply,
+                void *userdata,
+                sd_bus_error *error) {
+
+        uint64_t value = CGROUP_LIMIT_MAX;
+        Unit *u = ASSERT_PTR(userdata);
+        ssize_t type;
+
+        assert(bus);
+        assert(reply);
+        assert(property);
+
+        assert_se((type = cgroup_limit_type_from_string(property)) >= 0);
+        (void) unit_get_effective_limit(u, type, &value);
+        return sd_bus_message_append(reply, "t", value);
+}
+
 int bus_unit_method_attach_processes(sd_bus_message *message, void *userdata, sd_bus_error *error) {
         _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
         _cleanup_set_free_ Set *pids = NULL;
@@ -1547,10 +1569,13 @@ const sd_bus_vtable bus_unit_cgroup_vtable[] = {
         SD_BUS_PROPERTY("MemorySwapPeak", "t", property_get_memory_accounting, 0, 0),
         SD_BUS_PROPERTY("MemoryZSwapCurrent", "t", property_get_memory_accounting, 0, 0),
         SD_BUS_PROPERTY("MemoryAvailable", "t", property_get_available_memory, 0, 0),
+        SD_BUS_PROPERTY("EffectiveMemoryMax", "t", property_get_effective_limit, 0, 0),
+        SD_BUS_PROPERTY("EffectiveMemoryHigh", "t", property_get_effective_limit, 0, 0),
         SD_BUS_PROPERTY("CPUUsageNSec", "t", property_get_cpu_usage, 0, 0),
         SD_BUS_PROPERTY("EffectiveCPUs", "ay", property_get_cpuset_cpus, 0, 0),
         SD_BUS_PROPERTY("EffectiveMemoryNodes", "ay", property_get_cpuset_mems, 0, 0),
         SD_BUS_PROPERTY("TasksCurrent", "t", property_get_current_tasks, 0, 0),
+        SD_BUS_PROPERTY("EffectiveTasksMax", "t", property_get_effective_limit, 0, 0),
         SD_BUS_PROPERTY("IPIngressBytes", "t", property_get_ip_counter, 0, 0),
         SD_BUS_PROPERTY("IPIngressPackets", "t", property_get_ip_counter, 0, 0),
         SD_BUS_PROPERTY("IPEgressBytes", "t", property_get_ip_counter, 0, 0),
index f7d4a97096215dd4a8a4d27e836b7a4e0b73845a..e24c5bbc53394140e9a5276b01d3642b9c8ad1e8 100644 (file)
@@ -1073,7 +1073,7 @@ void bus_done(Manager *m) {
         assert(!m->subscribed);
 
         m->deserialized_subscribed = strv_free(m->deserialized_subscribed);
-        bus_verify_polkit_async_registry_free(m->polkit_registry);
+        m->polkit_registry = hashmap_free(m->polkit_registry);
 }
 
 int bus_fdset_add_all(Manager *m, FDSet *fds) {
index 12724c682c02f3d5573f537fb1393e627c2e133b..484b0e29abdce7218c8d4eead231635b74cd9bb8 100644 (file)
@@ -20,7 +20,7 @@
 #include "stdio-util.h"
 #include "string-util.h"
 #include "strv.h"
-#include "uid-alloc-range.h"
+#include "uid-classification.h"
 #include "user-util.h"
 
 /* Takes a value generated randomly or by hashing and turns it into a UID in the right range */
index 6bcfb68d8f21644b63da2dc700b9c184e03f4ab0..4aa3e35bd85bdff7ee564e6d2c09ecf86e21b274 100644 (file)
@@ -9,6 +9,7 @@
 #include "fileio.h"
 #include "glob-util.h"
 #include "io-util.h"
+#include "iovec-util.h"
 #include "label-util.h"
 #include "mkdir-label.h"
 #include "mount-util.h"
@@ -271,20 +272,24 @@ static int maybe_decrypt_and_write_credential(
                 size_t size,
                 uint64_t *left) {
 
-        _cleanup_free_ void *plaintext = NULL;
+        _cleanup_(iovec_done_erase) struct iovec plaintext = {};
         size_t add;
         int r;
 
         if (encrypted) {
-                size_t plaintext_size = 0;
-
-                r = decrypt_credential_and_warn(id, now(CLOCK_REALTIME), NULL, NULL, data, size,
-                                                &plaintext, &plaintext_size);
+                r = decrypt_credential_and_warn(
+                                id,
+                                now(CLOCK_REALTIME),
+                                /* tpm2_device= */ NULL,
+                                /* tpm2_signature_path= */ NULL,
+                                &IOVEC_MAKE(data, size),
+                                /* flags= */ 0,
+                                &plaintext);
                 if (r < 0)
                         return r;
 
-                data = plaintext;
-                size = plaintext_size;
+                data = plaintext.iov_base;
+                size = plaintext.iov_len;
         }
 
         add = strlen(id) + size;
@@ -684,7 +689,7 @@ static int acquire_credentials(
         /* Finally, we add in literally specified credentials. If the credentials already exist, we'll not
          * add them, so that they can act as a "default" if the same credential is specified multiple times. */
         HASHMAP_FOREACH(sc, context->set_credentials) {
-                _cleanup_(erase_and_freep) void *plaintext = NULL;
+                _cleanup_(iovec_done_erase) struct iovec plaintext = {};
                 const char *data;
                 size_t size, add;
 
@@ -698,11 +703,19 @@ static int acquire_credentials(
                         return log_debug_errno(errno, "Failed to test if credential %s exists: %m", sc->id);
 
                 if (sc->encrypted) {
-                        r = decrypt_credential_and_warn(sc->id, now(CLOCK_REALTIME), NULL, NULL, sc->data, sc->size, &plaintext, &size);
+                        r = decrypt_credential_and_warn(
+                                        sc->id,
+                                        now(CLOCK_REALTIME),
+                                        /* tpm2_device= */ NULL,
+                                        /* tpm2_signature_path= */ NULL,
+                                        &IOVEC_MAKE(sc->data, sc->size),
+                                        /* flags= */ 0,
+                                        &plaintext);
                         if (r < 0)
                                 return r;
 
-                        data = plaintext;
+                        data = plaintext.iov_base;
+                        size = plaintext.iov_len;
                 } else {
                         data = sc->data;
                         size = sc->size;
index f76db133b114da6d28dae5c33b7a4f7e4c6029db..f39b53280067157b1db4cee58458fd9e0b7af040 100644 (file)
@@ -59,6 +59,7 @@
 #include "strv.h"
 #include "terminal-util.h"
 #include "utmp-wtmp.h"
+#include "vpick.h"
 
 #define IDLE_TIMEOUT_USEC (5*USEC_PER_SEC)
 #define IDLE_TIMEOUT2_USEC (1*USEC_PER_SEC)
@@ -1106,7 +1107,8 @@ static int setup_pam(
                 gid_t gid,
                 const char *tty,
                 char ***env, /* updated on success */
-                const int fds[], size_t n_fds) {
+                const int fds[], size_t n_fds,
+                int exec_fd) {
 
 #if HAVE_PAM
 
@@ -1209,6 +1211,11 @@ static int setup_pam(
                  * those fds are open here that have been opened by PAM. */
                 (void) close_many(fds, n_fds);
 
+                /* Also close the 'exec_fd' in the child, since the service manager waits for the EOF induced
+                 * by the execve() to wait for completion, and if we'd keep the fd open here in the child
+                 * we'd never signal completion. */
+                exec_fd = safe_close(exec_fd);
+
                 /* Drop privileges - we don't need any to pam_close_session and this will make
                  * PR_SET_PDEATHSIG work in most cases.  If this fails, ignore the error - but expect sd-pam
                  * threads to fail to exit normally */
@@ -2859,13 +2866,33 @@ static bool insist_on_sandboxing(
         return false;
 }
 
-static int setup_ephemeral(const ExecContext *context, ExecRuntime *runtime) {
+static int setup_ephemeral(
+                const ExecContext *context,
+                ExecRuntime *runtime,
+                char **root_image,            /* both input and output! modified if ephemeral logic enabled */
+                char **root_directory) {      /* ditto */
+
         _cleanup_close_ int fd = -EBADF;
+        _cleanup_free_ char *new_root = NULL;
         int r;
 
+        assert(context);
+        assert(root_image);
+        assert(root_directory);
+
+        if (!*root_image && !*root_directory)
+                return 0;
+
         if (!runtime || !runtime->ephemeral_copy)
                 return 0;
 
+        assert(runtime->ephemeral_storage_socket[0] >= 0);
+        assert(runtime->ephemeral_storage_socket[1] >= 0);
+
+        new_root = strdup(runtime->ephemeral_copy);
+        if (!new_root)
+                return log_oom_debug();
+
         r = posix_lock(runtime->ephemeral_storage_socket[0], LOCK_EX);
         if (r < 0)
                 return log_debug_errno(r, "Failed to lock ephemeral storage socket: %m");
@@ -2876,28 +2903,23 @@ static int setup_ephemeral(const ExecContext *context, ExecRuntime *runtime) {
         if (fd >= 0)
                 /* We got an fd! That means ephemeral has already been set up, so nothing to do here. */
                 return 0;
-
         if (fd != -EAGAIN)
                 return log_debug_errno(fd, "Failed to receive file descriptor queued on ephemeral storage socket: %m");
 
-        log_debug("Making ephemeral snapshot of %s to %s",
-                  context->root_image ?: context->root_directory, runtime->ephemeral_copy);
+        if (*root_image) {
+                log_debug("Making ephemeral copy of %s to %s", *root_image, new_root);
 
-        if (context->root_image)
-                fd = copy_file(context->root_image, runtime->ephemeral_copy, O_EXCL, 0600,
-                               COPY_LOCK_BSD|COPY_REFLINK|COPY_CRTIME);
-        else
-                fd = btrfs_subvol_snapshot_at(AT_FDCWD, context->root_directory,
-                                              AT_FDCWD, runtime->ephemeral_copy,
-                                              BTRFS_SNAPSHOT_FALLBACK_COPY |
-                                              BTRFS_SNAPSHOT_FALLBACK_DIRECTORY |
-                                              BTRFS_SNAPSHOT_RECURSIVE |
-                                              BTRFS_SNAPSHOT_LOCK_BSD);
-        if (fd < 0)
-                return log_debug_errno(fd, "Failed to snapshot %s to %s: %m",
-                                       context->root_image ?: context->root_directory, runtime->ephemeral_copy);
+                fd = copy_file(*root_image,
+                               new_root,
+                               O_EXCL,
+                               0600,
+                               COPY_LOCK_BSD|
+                               COPY_REFLINK|
+                               COPY_CRTIME);
+                if (fd < 0)
+                        return log_debug_errno(fd, "Failed to copy image %s to %s: %m",
+                                               *root_image, new_root);
 
-        if (context->root_image) {
                 /* A root image might be subject to lots of random writes so let's try to disable COW on it
                  * which tends to not perform well in combination with lots of random writes.
                  *
@@ -2906,13 +2928,35 @@ static int setup_ephemeral(const ExecContext *context, ExecRuntime *runtime) {
                  */
                 r = chattr_fd(fd, FS_NOCOW_FL, FS_NOCOW_FL, NULL);
                 if (r < 0)
-                        log_debug_errno(fd, "Failed to disable copy-on-write for %s, ignoring: %m", runtime->ephemeral_copy);
+                        log_debug_errno(fd, "Failed to disable copy-on-write for %s, ignoring: %m", new_root);
+        } else {
+                assert(*root_directory);
+
+                log_debug("Making ephemeral snapshot of %s to %s", *root_directory, new_root);
+
+                fd = btrfs_subvol_snapshot_at(
+                                AT_FDCWD, *root_directory,
+                                AT_FDCWD, new_root,
+                                BTRFS_SNAPSHOT_FALLBACK_COPY |
+                                BTRFS_SNAPSHOT_FALLBACK_DIRECTORY |
+                                BTRFS_SNAPSHOT_RECURSIVE |
+                                BTRFS_SNAPSHOT_LOCK_BSD);
+                if (fd < 0)
+                        return log_debug_errno(fd, "Failed to snapshot directory %s to %s: %m",
+                                               *root_directory, new_root);
         }
 
         r = send_one_fd(runtime->ephemeral_storage_socket[1], fd, MSG_DONTWAIT);
         if (r < 0)
                 return log_debug_errno(r, "Failed to queue file descriptor on ephemeral storage socket: %m");
 
+        if (*root_image)
+                free_and_replace(*root_image, new_root);
+        else {
+                assert(*root_directory);
+                free_and_replace(*root_directory, new_root);
+        }
+
         return 1;
 }
 
@@ -2972,6 +3016,63 @@ static int verity_settings_prepare(
         return 0;
 }
 
+static int pick_versions(
+                const ExecContext *context,
+                const ExecParameters *params,
+                char **ret_root_image,
+                char **ret_root_directory) {
+
+        int r;
+
+        assert(context);
+        assert(params);
+        assert(ret_root_image);
+        assert(ret_root_directory);
+
+        if (context->root_image) {
+                _cleanup_(pick_result_done) PickResult result = PICK_RESULT_NULL;
+
+                r = path_pick(/* toplevel_path= */ NULL,
+                              /* toplevel_fd= */ AT_FDCWD,
+                              context->root_image,
+                              &pick_filter_image_raw,
+                              PICK_ARCHITECTURE|PICK_TRIES|PICK_RESOLVE,
+                              &result);
+                if (r < 0)
+                        return r;
+
+                if (!result.path)
+                        return log_exec_debug_errno(context, params, SYNTHETIC_ERRNO(ENOENT), "No matching entry in .v/ directory %s found.", context->root_image);
+
+                *ret_root_image = TAKE_PTR(result.path);
+                *ret_root_directory = NULL;
+                return r;
+        }
+
+        if (context->root_directory) {
+                _cleanup_(pick_result_done) PickResult result = PICK_RESULT_NULL;
+
+                r = path_pick(/* toplevel_path= */ NULL,
+                              /* toplevel_fd= */ AT_FDCWD,
+                              context->root_directory,
+                              &pick_filter_image_dir,
+                              PICK_ARCHITECTURE|PICK_TRIES|PICK_RESOLVE,
+                              &result);
+                if (r < 0)
+                        return r;
+
+                if (!result.path)
+                        return log_exec_debug_errno(context, params, SYNTHETIC_ERRNO(ENOENT), "No matching entry in .v/ directory %s found.", context->root_directory);
+
+                *ret_root_image = NULL;
+                *ret_root_directory = TAKE_PTR(result.path);
+                return r;
+        }
+
+        *ret_root_image = *ret_root_directory = NULL;
+        return 0;
+}
+
 static int apply_mount_namespace(
                 ExecCommandFlags command_flags,
                 const ExecContext *context,
@@ -2984,8 +3085,8 @@ static int apply_mount_namespace(
         _cleanup_strv_free_ char **empty_directories = NULL, **symlinks = NULL,
                         **read_write_paths_cleanup = NULL;
         _cleanup_free_ char *creds_path = NULL, *incoming_dir = NULL, *propagate_dir = NULL,
-                        *extension_dir = NULL, *host_os_release_stage = NULL;
-        const char *root_dir = NULL, *root_image = NULL, *tmp_dir = NULL, *var_tmp_dir = NULL;
+                *extension_dir = NULL, *host_os_release_stage = NULL, *root_image = NULL, *root_dir = NULL;
+        const char *tmp_dir = NULL, *var_tmp_dir = NULL;
         char **read_write_paths;
         bool needs_sandboxing, setup_os_release_symlink;
         BindMount *bind_mounts = NULL;
@@ -2997,14 +3098,21 @@ static int apply_mount_namespace(
         CLEANUP_ARRAY(bind_mounts, n_bind_mounts, bind_mount_free_many);
 
         if (params->flags & EXEC_APPLY_CHROOT) {
-                r = setup_ephemeral(context, runtime);
+                r = pick_versions(
+                                context,
+                                params,
+                                &root_image,
+                                &root_dir);
                 if (r < 0)
                         return r;
 
-                if (context->root_image)
-                        root_image = (runtime ? runtime->ephemeral_copy : NULL) ?: context->root_image;
-                else
-                        root_dir = (runtime ? runtime->ephemeral_copy : NULL) ?: context->root_directory;
+                r = setup_ephemeral(
+                                context,
+                                runtime,
+                                &root_image,
+                                &root_dir);
+                if (r < 0)
+                        return r;
         }
 
         r = compile_bind_mounts(context, params, &bind_mounts, &n_bind_mounts, &empty_directories);
@@ -4529,7 +4637,7 @@ int exec_invoke(
                  * wins here. (See above.) */
 
                 /* All fds passed in the fds array will be closed in the pam child process. */
-                r = setup_pam(context->pam_name, username, uid, gid, context->tty_path, &accum_env, params->fds, n_fds);
+                r = setup_pam(context->pam_name, username, uid, gid, context->tty_path, &accum_env, params->fds, n_fds, params->exec_fd);
                 if (r < 0) {
                         *exit_status = EXIT_PAM;
                         return log_exec_error_errno(context, params, r, "Failed to set up PAM session: %m");
index 55d24094f77f00ebd44263e6fd27bcad7ff42abc..ccfc00c6e950f6dd49af6174bc3607d3f839acc6 100644 (file)
@@ -1930,7 +1930,7 @@ static int exec_context_serialize(const ExecContext *c, FILE *f) {
                 FOREACH_ARRAY(i, c->directories[dt].items, c->directories[dt].n_items) {
                         _cleanup_free_ char *path_escaped = NULL;
 
-                        path_escaped = shell_escape(i->path, ":");
+                        path_escaped = shell_escape(i->path, ":" WHITESPACE);
                         if (!path_escaped)
                                 return log_oom_debug();
 
@@ -1943,7 +1943,7 @@ static int exec_context_serialize(const ExecContext *c, FILE *f) {
                         STRV_FOREACH(d, i->symlinks) {
                                 _cleanup_free_ char *link_escaped = NULL;
 
-                                link_escaped = shell_escape(*d, ":");
+                                link_escaped = shell_escape(*d, ":" WHITESPACE);
                                 if (!link_escaped)
                                         return log_oom_debug();
 
@@ -2264,11 +2264,11 @@ static int exec_context_serialize(const ExecContext *c, FILE *f) {
         FOREACH_ARRAY(mount, c->bind_mounts, c->n_bind_mounts) {
                 _cleanup_free_ char *src_escaped = NULL, *dst_escaped = NULL;
 
-                src_escaped = shell_escape(mount->source, ":");
+                src_escaped = shell_escape(mount->source, ":" WHITESPACE);
                 if (!src_escaped)
                         return log_oom_debug();
 
-                dst_escaped = shell_escape(mount->destination, ":");
+                dst_escaped = shell_escape(mount->destination, ":" WHITESPACE);
                 if (!dst_escaped)
                         return log_oom_debug();
 
@@ -2455,11 +2455,11 @@ static int exec_context_serialize(const ExecContext *c, FILE *f) {
         FOREACH_ARRAY(mount, c->mount_images, c->n_mount_images) {
                 _cleanup_free_ char *s = NULL, *source_escaped = NULL, *dest_escaped = NULL;
 
-                source_escaped = shell_escape(mount->source, " ");
+                source_escaped = shell_escape(mount->source, WHITESPACE);
                 if (!source_escaped)
                         return log_oom_debug();
 
-                dest_escaped = shell_escape(mount->destination, " ");
+                dest_escaped = shell_escape(mount->destination, WHITESPACE);
                 if (!dest_escaped)
                         return log_oom_debug();
 
@@ -2496,7 +2496,7 @@ static int exec_context_serialize(const ExecContext *c, FILE *f) {
         FOREACH_ARRAY(mount, c->extension_images, c->n_extension_images) {
                 _cleanup_free_ char *s = NULL, *source_escaped = NULL;
 
-                source_escaped = shell_escape(mount->source, ":");
+                source_escaped = shell_escape(mount->source, ":" WHITESPACE);
                 if (!source_escaped)
                         return log_oom_debug();
 
@@ -2670,12 +2670,12 @@ static int exec_context_deserialize(ExecContext *c, FILE *f) {
                                 return r;
                 } else if ((val = startswith(l, "exec-context-root-hash="))) {
                         c->root_hash = mfree(c->root_hash);
-                        r = unhexmem(val, strlen(val), &c->root_hash, &c->root_hash_size);
+                        r = unhexmem(val, &c->root_hash, &c->root_hash_size);
                         if (r < 0)
                                 return r;
                 } else if ((val = startswith(l, "exec-context-root-hash-sig="))) {
                         c->root_hash_sig = mfree(c->root_hash_sig);
-                        r= unbase64mem(val, strlen(val), &c->root_hash_sig, &c->root_hash_sig_size);
+                        r= unbase64mem(val, &c->root_hash_sig, &c->root_hash_sig_size);
                         if (r < 0)
                                 return r;
                 } else if ((val = startswith(l, "exec-context-root-ephemeral="))) {
@@ -2847,7 +2847,8 @@ static int exec_context_deserialize(ExecContext *c, FILE *f) {
                                 _cleanup_free_ char *tuple = NULL, *path = NULL, *only_create = NULL;
                                 const char *p;
 
-                                r = extract_first_word(&val, &tuple, WHITESPACE, EXTRACT_RETAIN_ESCAPE);
+                                /* Use EXTRACT_UNESCAPE_RELAX here, as we unescape the colons in subsequent calls */
+                                r = extract_first_word(&val, &tuple, WHITESPACE, EXTRACT_UNESCAPE_SEPARATORS|EXTRACT_UNESCAPE_RELAX);
                                 if (r < 0)
                                         return r;
                                 if (r == 0)
@@ -3054,7 +3055,7 @@ static int exec_context_deserialize(ExecContext *c, FILE *f) {
                         if (c->stdin_data)
                                 return -EINVAL; /* duplicated */
 
-                        r = unbase64mem(val, strlen(val), &c->stdin_data, &c->stdin_data_size);
+                        r = unbase64mem(val, &c->stdin_data, &c->stdin_data_size);
                         if (r < 0)
                                 return r;
                 } else if ((val = startswith(l, "exec-context-tty-path="))) {
@@ -3689,7 +3690,7 @@ static int exec_context_deserialize(ExecContext *c, FILE *f) {
                                 .encrypted = r,
                         };
 
-                        r = unbase64mem(data, strlen(data), &sc->data, &sc->size);
+                        r = unbase64mem(data, &sc->data, &sc->size);
                         if (r < 0)
                                 return r;
 
index e53deb639ec422bc1d11abef07b896395d31b1df..be56c4676f308f71f3800817f568c2229c4c68a5 100644 (file)
@@ -314,7 +314,7 @@ static int proc_cmdline_callback(const char *key, const char *value, void *data)
         colon++;
 
         if (base64) {
-                r = unbase64mem(colon, SIZE_MAX, &binary, &l);
+                r = unbase64mem(colon, &binary, &l);
                 if (r < 0) {
                         log_warning_errno(r, "Failed to decode binary credential '%s' data, ignoring: %m", n);
                         return 0;
@@ -525,7 +525,7 @@ static int parse_smbios_strings(ImportCredentialContext *c, const char *data, si
 
                 /* Optionally base64 decode the data, if requested, to allow binary credentials */
                 if (unbase64) {
-                        r = unbase64mem(eq + 1, nul - (eq + 1), &buf, &buflen);
+                        r = unbase64mem_full(eq + 1, nul - (eq + 1), /* secure = */ false, &buf, &buflen);
                         if (r < 0) {
                                 log_warning_errno(r, "Failed to base64 decode credential '%s', ignoring: %m", cn);
                                 continue;
index ed19c846975319ec76f873bdd6c0289bce06a185..92d5fc4cc3ffc69f1f3f1b19e013cc4b68ea361b 100644 (file)
@@ -531,7 +531,7 @@ Socket.SELinuxContextFromNet,            config_parse_warn_compat,
 {{ EXEC_CONTEXT_CONFIG_ITEMS('Socket') }}
 {{ CGROUP_CONTEXT_CONFIG_ITEMS('Socket') }}
 {{ KILL_CONTEXT_CONFIG_ITEMS('Socket') }}
-Mount.What,                              config_parse_unit_string_printf,             0,                                  offsetof(Mount, parameters_fragment.what)
+Mount.What,                              config_parse_mount_node,                     0,                                  offsetof(Mount, parameters_fragment.what)
 Mount.Where,                             config_parse_unit_path_printf,               0,                                  offsetof(Mount, where)
 Mount.Options,                           config_parse_unit_string_printf,             0,                                  offsetof(Mount, parameters_fragment.options)
 Mount.Type,                              config_parse_unit_string_printf,             0,                                  offsetof(Mount, parameters_fragment.fstype)
@@ -548,7 +548,7 @@ Automount.Where,                         config_parse_unit_path_printf,
 Automount.ExtraOptions,                  config_parse_unit_string_printf,             0,                                  offsetof(Automount, extra_options)
 Automount.DirectoryMode,                 config_parse_mode,                           0,                                  offsetof(Automount, directory_mode)
 Automount.TimeoutIdleSec,                config_parse_sec_fix_0,                      0,                                  offsetof(Automount, timeout_idle_usec)
-Swap.What,                               config_parse_unit_path_printf,               0,                                  offsetof(Swap, parameters_fragment.what)
+Swap.What,                               config_parse_mount_node,                     0,                                  offsetof(Swap, parameters_fragment.what)
 Swap.Priority,                           config_parse_swap_priority,                  0,                                  0
 Swap.Options,                            config_parse_unit_string_printf,             0,                                  offsetof(Swap, parameters_fragment.options)
 Swap.TimeoutSec,                         config_parse_sec_fix_0,                      0,                                  offsetof(Swap, timeout_usec)
index b424ef06207de53c8a9614662779568616afddf7..6fc6bb360980e59af70425a3cb0d873c3a9aa2fa 100644 (file)
@@ -38,6 +38,7 @@
 #include "fileio.h"
 #include "firewall-util.h"
 #include "fs-util.h"
+#include "fstab-util.h"
 #include "hexdecoct.h"
 #include "iovec-util.h"
 #include "ioprio-util.h"
@@ -1254,7 +1255,7 @@ int config_parse_exec_input_data(
                 return 0;
         }
 
-        r = unbase64mem(rvalue, SIZE_MAX, &p, &sz);
+        r = unbase64mem(rvalue, &p, &sz);
         if (r < 0) {
                 log_syntax(unit, LOG_WARNING, filename, line, r,
                            "Failed to decode base64 data, ignoring: %s", rvalue);
@@ -1748,7 +1749,7 @@ int config_parse_exec_root_hash(
         }
 
         /* We have a roothash to decode, eg: RootHash=012345789abcdef */
-        r = unhexmem(rvalue, strlen(rvalue), &roothash_decoded, &roothash_decoded_size);
+        r = unhexmem(rvalue, &roothash_decoded, &roothash_decoded_size);
         if (r < 0) {
                 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to decode RootHash=, ignoring: %s", rvalue);
                 return 0;
@@ -1816,7 +1817,7 @@ int config_parse_exec_root_hash_sig(
         }
 
         /* We have a roothash signature to decode, eg: RootHashSignature=base64:012345789abcdef */
-        r = unbase64mem(value, strlen(value), &roothash_sig_decoded, &roothash_sig_decoded_size);
+        r = unbase64mem(value, &roothash_sig_decoded, &roothash_sig_decoded_size);
         if (r < 0) {
                 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to decode RootHashSignature=, ignoring: %s", rvalue);
                 return 0;
@@ -2697,7 +2698,7 @@ int config_parse_unit_env_file(const char *unit,
                 return 0;
         }
 
-        r = unit_full_printf_full(u, rvalue, PATH_MAX, &n);
+        r = unit_path_printf(u, rvalue, &n);
         if (r < 0) {
                 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in '%s', ignoring: %m", rvalue);
                 return 0;
@@ -5268,7 +5269,7 @@ int config_parse_bind_paths(
                 if (r == 0)
                         break;
 
-                r = unit_full_printf_full(u, source, PATH_MAX, &sresolved);
+                r = unit_path_printf(u, source, &sresolved);
                 if (r < 0) {
                         log_syntax(unit, LOG_WARNING, filename, line, r,
                                    "Failed to resolve unit specifiers in \"%s\", ignoring: %m", source);
@@ -6113,6 +6114,47 @@ int config_parse_restrict_network_interfaces(
         return 0;
 }
 
+int config_parse_mount_node(
+                const char *unit,
+                const char *filename,
+                unsigned line,
+                const char *section,
+                unsigned section_line,
+                const char *lvalue,
+                int ltype,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
+
+        const Unit *u = ASSERT_PTR(userdata);
+        _cleanup_free_ char *resolved = NULL, *path = NULL;
+        int r;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+
+        r = unit_full_printf(u, rvalue, &resolved);
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in '%s', ignoring: %m", rvalue);
+                return 0;
+        }
+
+        path = fstab_node_to_udev_node(resolved);
+        if (!path)
+                return log_oom();
+
+        /* The source passed is not necessarily something we understand, and we pass it as-is to mount/swapon,
+         * so path_is_valid is not used. But let's check for basic sanety, i.e. if the source is longer than
+         * PATH_MAX, you're likely doing something wrong. */
+        if (strlen(path) >= PATH_MAX) {
+                log_syntax(unit, LOG_WARNING, filename, line, 0, "Resolved mount path '%s' too long, ignoring.", path);
+                return 0;
+        }
+
+        return config_parse_string(unit, filename, line, section, section_line, lvalue, ltype, path, data, userdata);
+}
+
 static int merge_by_names(Unit *u, Set *names, const char *id) {
         char *k;
         int r;
@@ -6365,6 +6407,7 @@ void unit_dump_config_items(FILE *f) {
                 { config_parse_job_mode_isolate,      "BOOLEAN" },
                 { config_parse_personality,           "PERSONALITY" },
                 { config_parse_log_filter_patterns,   "REGEX" },
+                { config_parse_mount_node,            "NODE" },
         };
 
         const char *prev = NULL;
index c001397ff2cedff1f2e2f7ca3bda6191c3fb67bd..9394347d683aabd1a70afeb28e28b00d2dae71c4 100644 (file)
@@ -159,6 +159,7 @@ CONFIG_PARSER_PROTOTYPE(config_parse_log_filter_patterns);
 CONFIG_PARSER_PROTOTYPE(config_parse_open_file);
 CONFIG_PARSER_PROTOTYPE(config_parse_memory_pressure_watch);
 CONFIG_PARSER_PROTOTYPE(config_parse_cgroup_nft_set);
+CONFIG_PARSER_PROTOTYPE(config_parse_mount_node);
 
 /* gperf prototypes */
 const struct ConfigPerfItem* load_fragment_gperf_lookup(const char *key, GPERF_LEN_TYPE length);
index f15d2ff25cb3696f8602dd7e044b076c9a426819..10f60c24251c333751f929f3b2c8c6f5b364629e 100644 (file)
@@ -464,7 +464,7 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
                 if (proc_cmdline_value_missing(key, value))
                         return 0;
 
-                r = unbase64mem(value, SIZE_MAX, &p, &sz);
+                r = unbase64mem(value, &p, &sz);
                 if (r < 0)
                         log_warning_errno(r, "Failed to parse systemd.random_seed= argument, ignoring: %s", value);
 
index c07f537b9f2ca4223753816cf0bbed642e01cee0..95c5f6381d3f26a27c033e472f62e995df433a39 100644 (file)
@@ -4767,7 +4767,7 @@ int manager_dispatch_user_lookup_fd(sd_event_source *source, int fd, uint32_t re
 }
 
 static int short_uid_range(const char *path) {
-        _cleanup_(uid_range_freep) UidRange *p = NULL;
+        _cleanup_(uid_range_freep) UIDRange *p = NULL;
         int r;
 
         assert(path);
index df6d0b4485c76ea52402a8bce7d6f6fa8a5efcf0..1bfd6b6ca0eb745bf0a503c7f27f34ccccbca26e 100644 (file)
@@ -626,8 +626,7 @@ static int append_tmpfs_mounts(MountList *ml, const TemporaryFileSystem *tmpfs,
                         return log_debug_errno(r, "Failed to parse mount option '%s': %m", str);
 
                 ro = flags & MS_RDONLY;
-                if (ro)
-                        flags ^= MS_RDONLY;
+                flags &= ~MS_RDONLY;
 
                 MountEntry *me = mount_list_extend(ml);
                 if (!me)
index 9f95984eb630419f1a31cde6e45a100103097fed..be4cb139e9b8e6f6bf1500e9bb625a99b460c9bb 100644 (file)
@@ -4,6 +4,7 @@
 #include "cgroup-util.h"
 #include "format-util.h"
 #include "macro.h"
+#include "sd-path.h"
 #include "specifier.h"
 #include "string-util.h"
 #include "strv.h"
@@ -164,6 +165,14 @@ static int specifier_credentials_dir(char specifier, const void *data, const cha
         return 0;
 }
 
+static int specifier_shared_data_dir(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
+        const Unit *u = ASSERT_PTR(userdata);
+
+        assert(ret);
+
+        return sd_path_lookup(MANAGER_IS_SYSTEM(u->manager) ? SD_PATH_SYSTEM_SHARED : SD_PATH_USER_SHARED, NULL, ret);
+}
+
 int unit_name_printf(const Unit *u, const char* format, char **ret) {
         /*
          * This will use the passed string as format string and replace the following specifiers (which should all be
@@ -208,6 +217,7 @@ int unit_full_printf_full(const Unit *u, const char *format, size_t max_length,
          *
          * %C: the cache directory root (e.g. /var/cache or $XDG_CACHE_HOME)
          * %d: the credentials directory ($CREDENTIALS_DIRECTORY)
+         * %D: the shared data root (e.g. /usr/share or $XDG_DATA_HOME)
          * %E: the configuration directory root (e.g. /etc or $XDG_CONFIG_HOME)
          * %L: the log directory root (e.g. /var/log or $XDG_STATE_HOME/log)
          * %S: the state directory root (e.g. /var/lib or $XDG_STATE_HOME)
@@ -245,6 +255,7 @@ int unit_full_printf_full(const Unit *u, const char *format, size_t max_length,
 
                 { 'C', specifier_special_directory,        UINT_TO_PTR(EXEC_DIRECTORY_CACHE) },
                 { 'd', specifier_credentials_dir,          NULL },
+                { 'D', specifier_shared_data_dir,          NULL },
                 { 'E', specifier_special_directory,        UINT_TO_PTR(EXEC_DIRECTORY_CONFIGURATION) },
                 { 'L', specifier_special_directory,        UINT_TO_PTR(EXEC_DIRECTORY_LOGS) },
                 { 'S', specifier_special_directory,        UINT_TO_PTR(EXEC_DIRECTORY_STATE) },
index 2b6222b308e03de259e0d19f74582cd0445347b8..cd5e5343f083c8b50f8e3a286a83dd014617fed0 100644 (file)
@@ -51,7 +51,7 @@
 #include "strv.h"
 #include "sync-util.h"
 #include "tmpfile-util.h"
-#include "uid-alloc-range.h"
+#include "uid-classification.h"
 #include "user-util.h"
 
 /* The maximum size up to which we process coredumps. We use 1G on 32-bit systems, and 32G on 64-bit systems */
index 84d45316d4094ffd339afcfa4c80ead864d75f16..53370c9f6e4238a13f26ae7ddd6e773942d37c34 100644 (file)
@@ -67,6 +67,8 @@ static bool arg_all = false;
 static ImagePolicy *arg_image_policy = NULL;
 
 STATIC_DESTRUCTOR_REGISTER(arg_debugger_args, strv_freep);
+STATIC_DESTRUCTOR_REGISTER(arg_root, freep);
+STATIC_DESTRUCTOR_REGISTER(arg_image, freep);
 STATIC_DESTRUCTOR_REGISTER(arg_file, strv_freep);
 STATIC_DESTRUCTOR_REGISTER(arg_image_policy, image_policy_freep);
 
index ed39ffe51e138f46f99cec4e507bef7b50431e2b..c9d1a6e8d94f3029db33005b885329bd581a8bd2 100644 (file)
@@ -4,6 +4,7 @@
 #include <unistd.h>
 
 #include "build.h"
+#include "bus-polkit.h"
 #include "creds-util.h"
 #include "dirent-util.h"
 #include "escape.h"
@@ -420,22 +421,22 @@ static int verb_cat(int argc, char **argv, void *userdata) {
                 }
 
                 if (encrypted) {
-                        _cleanup_(erase_and_freep) void *plaintext = NULL;
-                        size_t plaintext_size;
+                        _cleanup_(iovec_done_erase) struct iovec plaintext = {};
 
                         r = decrypt_credential_and_warn(
                                         *cn,
                                         timestamp,
                                         arg_tpm2_device,
                                         arg_tpm2_signature,
-                                        data, size,
-                                        &plaintext, &plaintext_size);
+                                        &IOVEC_MAKE(data, size),
+                                        /* flags= */ 0,
+                                        &plaintext);
                         if (r < 0)
                                 return r;
 
                         erase_and_free(data);
-                        data = TAKE_PTR(plaintext);
-                        size = plaintext_size;
+                        data = TAKE_PTR(plaintext.iov_base);
+                        size = plaintext.iov_len;
                 }
 
                 r = write_blob(stdout, data, size);
@@ -447,11 +448,9 @@ static int verb_cat(int argc, char **argv, void *userdata) {
 }
 
 static int verb_encrypt(int argc, char **argv, void *userdata) {
+        _cleanup_(iovec_done_erase) struct iovec plaintext = {}, output = {};
         _cleanup_free_ char *base64_buf = NULL, *fname = NULL;
-        _cleanup_(erase_and_freep) char *plaintext = NULL;
         const char *input_path, *output_path, *name;
-        _cleanup_free_ void *output = NULL;
-        size_t plaintext_size, output_size;
         ssize_t base64_size;
         usec_t timestamp;
         int r;
@@ -461,9 +460,9 @@ static int verb_encrypt(int argc, char **argv, void *userdata) {
         input_path = empty_or_dash(argv[1]) ? NULL : argv[1];
 
         if (input_path)
-                r = read_full_file_full(AT_FDCWD, input_path, UINT64_MAX, CREDENTIAL_SIZE_MAX, READ_FULL_FILE_SECURE|READ_FULL_FILE_FAIL_WHEN_LARGER, NULL, &plaintext, &plaintext_size);
+                r = read_full_file_full(AT_FDCWD, input_path, UINT64_MAX, CREDENTIAL_SIZE_MAX, READ_FULL_FILE_SECURE|READ_FULL_FILE_FAIL_WHEN_LARGER, NULL, (char**) &plaintext.iov_base, &plaintext.iov_len);
         else
-                r = read_full_stream_full(stdin, NULL, UINT64_MAX, CREDENTIAL_SIZE_MAX, READ_FULL_FILE_SECURE|READ_FULL_FILE_FAIL_WHEN_LARGER, &plaintext, &plaintext_size);
+                r = read_full_stream_full(stdin, NULL, UINT64_MAX, CREDENTIAL_SIZE_MAX, READ_FULL_FILE_SECURE|READ_FULL_FILE_FAIL_WHEN_LARGER, (char**) &plaintext.iov_base, &plaintext.iov_len);
         if (r == -E2BIG)
                 return log_error_errno(r, "Plaintext too long for credential (allowed size: %zu).", (size_t) CREDENTIAL_SIZE_MAX);
         if (r < 0)
@@ -502,12 +501,13 @@ static int verb_encrypt(int argc, char **argv, void *userdata) {
                         arg_tpm2_pcr_mask,
                         arg_tpm2_public_key,
                         arg_tpm2_public_key_pcr_mask,
-                        plaintext, plaintext_size,
-                        &output, &output_size);
+                        &plaintext,
+                        /* flags= */ 0,
+                        &output);
         if (r < 0)
                 return r;
 
-        base64_size = base64mem_full(output, output_size, arg_pretty ? 69 : 79, &base64_buf);
+        base64_size = base64mem_full(output.iov_base, output.iov_len, arg_pretty ? 69 : 79, &base64_buf);
         if (base64_size < 0)
                 return base64_size;
 
@@ -543,11 +543,10 @@ static int verb_encrypt(int argc, char **argv, void *userdata) {
 }
 
 static int verb_decrypt(int argc, char **argv, void *userdata) {
-        _cleanup_(erase_and_freep) void *plaintext = NULL;
-        _cleanup_free_ char *input = NULL, *fname = NULL;
+        _cleanup_(iovec_done_erase) struct iovec input = {}, plaintext = {};
+        _cleanup_free_ char *fname = NULL;
         _cleanup_fclose_ FILE *output_file = NULL;
         const char *input_path, *output_path, *name;
-        size_t input_size, plaintext_size;
         usec_t timestamp;
         FILE *f;
         int r;
@@ -557,9 +556,9 @@ static int verb_decrypt(int argc, char **argv, void *userdata) {
         input_path = empty_or_dash(argv[1]) ? NULL : argv[1];
 
         if (input_path)
-                r = read_full_file_full(AT_FDCWD, argv[1], UINT64_MAX, CREDENTIAL_ENCRYPTED_SIZE_MAX, READ_FULL_FILE_UNBASE64|READ_FULL_FILE_FAIL_WHEN_LARGER, NULL, &input, &input_size);
+                r = read_full_file_full(AT_FDCWD, argv[1], UINT64_MAX, CREDENTIAL_ENCRYPTED_SIZE_MAX, READ_FULL_FILE_UNBASE64|READ_FULL_FILE_FAIL_WHEN_LARGER, NULL, (char**) &input, &input.iov_len);
         else
-                r = read_full_stream_full(stdin, NULL, UINT64_MAX, CREDENTIAL_ENCRYPTED_SIZE_MAX, READ_FULL_FILE_UNBASE64|READ_FULL_FILE_FAIL_WHEN_LARGER, &input, &input_size);
+                r = read_full_stream_full(stdin, NULL, UINT64_MAX, CREDENTIAL_ENCRYPTED_SIZE_MAX, READ_FULL_FILE_UNBASE64|READ_FULL_FILE_FAIL_WHEN_LARGER, (char**) &input, &input.iov_len);
         if (r == -E2BIG)
                 return log_error_errno(r, "Data too long for encrypted credential (allowed size: %zu).", (size_t) CREDENTIAL_ENCRYPTED_SIZE_MAX);
         if (r < 0)
@@ -591,8 +590,9 @@ static int verb_decrypt(int argc, char **argv, void *userdata) {
                         timestamp,
                         arg_tpm2_device,
                         arg_tpm2_signature,
-                        input, input_size,
-                        &plaintext, &plaintext_size);
+                        &input,
+                        /* flags= */ 0,
+                        &plaintext);
         if (r < 0)
                 return r;
 
@@ -605,7 +605,7 @@ static int verb_decrypt(int argc, char **argv, void *userdata) {
         } else
                 f = stdout;
 
-        r = write_blob(f, plaintext, plaintext_size);
+        r = write_blob(f, plaintext.iov_base, plaintext.iov_len);
         if (r < 0)
                 return r;
 
@@ -613,14 +613,14 @@ static int verb_decrypt(int argc, char **argv, void *userdata) {
 }
 
 static int verb_setup(int argc, char **argv, void *userdata) {
-        size_t size;
+        _cleanup_(iovec_done_erase) struct iovec host_key = {};
         int r;
 
-        r = get_credential_host_secret(CREDENTIAL_SECRET_GENERATE|CREDENTIAL_SECRET_WARN_NOT_ENCRYPTED, NULL, &size);
+        r = get_credential_host_secret(CREDENTIAL_SECRET_GENERATE|CREDENTIAL_SECRET_WARN_NOT_ENCRYPTED, &host_key);
         if (r < 0)
                 return log_error_errno(r, "Failed to setup credentials host key: %m");
 
-        log_info("%zu byte credentials host key set up.", size);
+        log_info("%zu byte credentials host key set up.", host_key.iov_len);
 
         return EXIT_SUCCESS;
 }
@@ -842,8 +842,8 @@ static int parse_argv(int argc, char *argv[]) {
                                 arg_with_key = CRED_AES256_GCM_BY_HOST_AND_TPM2_HMAC;
                         else if (STR_IN_SET(optarg, "host+tpm2-with-public-key", "tpm2-with-public-key+host"))
                                 arg_with_key = CRED_AES256_GCM_BY_HOST_AND_TPM2_HMAC_WITH_PK;
-                        else if (streq(optarg, "tpm2-absent"))
-                                arg_with_key = CRED_AES256_GCM_BY_TPM2_ABSENT;
+                        else if (STR_IN_SET(optarg, "null", "tpm2-absent"))
+                                arg_with_key = CRED_AES256_GCM_BY_NULL;
                         else
                                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unknown key type: %s", optarg);
 
@@ -983,6 +983,7 @@ static int vl_method_encrypt(Varlink *link, JsonVariant *parameters, VarlinkMeth
                 { "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 },
+                VARLINK_DISPATCH_POLKIT_FIELD,
                 {}
         };
         _cleanup_(method_encrypt_parameters_done) MethodEncryptParameters p = {
@@ -990,6 +991,7 @@ static int vl_method_encrypt(Varlink *link, JsonVariant *parameters, VarlinkMeth
                 .not_after = UINT64_MAX,
         };
         _cleanup_(iovec_done) struct iovec output = {};
+        Hashmap **polkit_registry = ASSERT_PTR(userdata);
         int r;
 
         assert(link);
@@ -1010,6 +1012,16 @@ static int vl_method_encrypt(Varlink *link, JsonVariant *parameters, VarlinkMeth
         if (p.not_after != UINT64_MAX && p.not_after < p.timestamp)
                 return varlink_error_invalid_parameter_name(link, "notAfter");
 
+        r = varlink_verify_polkit_async(
+                        link,
+                        /* bus= */ NULL,
+                        "io.systemd.credentials.encrypt",
+                        /* details= */ NULL,
+                        /* good_user= */ UID_INVALID,
+                        polkit_registry);
+        if (r <= 0)
+                return r;
+
         r = encrypt_credential_and_warn(
                         arg_with_key,
                         p.name,
@@ -1019,8 +1031,9 @@ static int vl_method_encrypt(Varlink *link, JsonVariant *parameters, VarlinkMeth
                         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);
+                        p.text ? &IOVEC_MAKE_STRING(p.text) : &p.data,
+                        /* flags= */ 0,
+                        &output);
         if (r < 0)
                 return r;
 
@@ -1051,15 +1064,17 @@ static void method_decrypt_parameters_done(MethodDecryptParameters *p) {
 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 },
+                { "name",      JSON_VARIANT_STRING,        json_dispatch_const_string,   offsetof(MethodDecryptParameters, name),      0              },
+                { "blob",      JSON_VARIANT_STRING,        json_dispatch_unbase64_iovec, offsetof(MethodDecryptParameters, blob),      JSON_MANDATORY },
+                { "timestamp", _JSON_VARIANT_TYPE_INVALID, json_dispatch_uint64,         offsetof(MethodDecryptParameters, timestamp), 0              },
+                VARLINK_DISPATCH_POLKIT_FIELD,
                 {}
         };
         _cleanup_(method_decrypt_parameters_done) MethodDecryptParameters p = {
                 .timestamp = UINT64_MAX,
         };
         _cleanup_(iovec_done_erase) struct iovec output = {};
+        Hashmap **polkit_registry = ASSERT_PTR(userdata);
         int r;
 
         assert(link);
@@ -1073,18 +1088,27 @@ static int vl_method_decrypt(Varlink *link, JsonVariant *parameters, VarlinkMeth
 
         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 = varlink_verify_polkit_async(
+                        link,
+                        /* bus= */ NULL,
+                        "io.systemd.credentials.decrypt",
+                        /* details= */ NULL,
+                        /* good_user= */ UID_INVALID,
+                        polkit_registry);
+        if (r <= 0)
+                return r;
+
         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);
+                        &p.blob,
+                        /* flags= */ 0,
+                        &output);
         if (r == -EBADMSG)
                 return varlink_error(link, "io.systemd.Credentials.BadFormat", NULL);
         if (r == -EREMOTE)
@@ -1116,10 +1140,11 @@ static int run(int argc, char *argv[]) {
 
         if (arg_varlink) {
                 _cleanup_(varlink_server_unrefp) VarlinkServer *varlink_server = NULL;
+                _cleanup_(hashmap_freep) Hashmap *polkit_registry = NULL;
 
                 /* Invocation as Varlink service */
 
-                r = varlink_server_new(&varlink_server, VARLINK_SERVER_ROOT_ONLY|VARLINK_SERVER_INHERIT_USERDATA);
+                r = varlink_server_new(&varlink_server, VARLINK_SERVER_ACCOUNT_UID|VARLINK_SERVER_INHERIT_USERDATA);
                 if (r < 0)
                         return log_error_errno(r, "Failed to allocate Varlink server: %m");
 
@@ -1134,6 +1159,8 @@ static int run(int argc, char *argv[]) {
                 if (r < 0)
                         return log_error_errno(r, "Failed to bind Varlink methods: %m");
 
+                varlink_server_set_userdata(varlink_server, &polkit_registry);
+
                 r = varlink_server_loop_auto(varlink_server);
                 if (r < 0)
                         return log_error_errno(r, "Failed to run Varlink event loop: %m");
diff --git a/src/creds/io.systemd.credentials.policy b/src/creds/io.systemd.credentials.policy
new file mode 100644 (file)
index 0000000..f94571b
--- /dev/null
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?> <!--*-nxml-*-->
+<!DOCTYPE policyconfig PUBLIC "-//freedesktop//DTD PolicyKit Policy Configuration 1.0//EN"
+        "https://www.freedesktop.org/standards/PolicyKit/1/policyconfig.dtd">
+
+<!--
+  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.
+-->
+
+<policyconfig>
+
+        <vendor>The systemd Project</vendor>
+        <vendor_url>https://systemd.io</vendor_url>
+
+        <action id="io.systemd.credentials.encrypt">
+                <description gettext-domain="systemd">Allow encryption and signing of system credentials.</description>
+                <message gettext-domain="systemd">Authentication is required for an application to encrypt and sign a system credential.</message>
+                <defaults>
+                        <allow_any>auth_admin_keep</allow_any>
+                        <allow_inactive>auth_admin_keep</allow_inactive>
+                        <allow_active>auth_admin_keep</allow_active>
+                </defaults>
+        </action>
+
+        <action id="io.systemd.credentials.decrypt">
+                <description gettext-domain="systemd">Allow decryption of system credentials.</description>
+                <message gettext-domain="systemd">Authentication is required for an application to decrypto a system credential.</message>
+                <defaults>
+                        <allow_any>auth_admin_keep</allow_any>
+                        <allow_inactive>auth_admin_keep</allow_inactive>
+                        <allow_active>auth_admin_keep</allow_active>
+                </defaults>
+        </action>
+</policyconfig>
index 85572568f6c0b85c91676b569962fc9dac0e613c..24833110d532239c7df39fc0d46eade8bdc05c65 100644 (file)
@@ -23,3 +23,6 @@ if install_sysconfdir
         install_emptydir(sysconfdir / 'credstore.encrypted',
                          install_mode : 'rwx------')
 endif
+
+install_data('io.systemd.credentials.policy',
+             install_dir : polkitpolicydir)
index 7d6112e40271cb1d87ad7c334e2a1e88a53bbec8..ea969102cbe12731a7a3d7c257366d1068bb2856 100644 (file)
@@ -7,6 +7,30 @@
 #include "openssl-util.h"
 #include "pkcs11-util.h"
 
+static int uri_set_private_class(const char *uri, char **ret_uri) {
+        _cleanup_(sym_p11_kit_uri_freep) P11KitUri *p11kit_uri = NULL;
+        _cleanup_free_ char *private_uri = NULL;
+        int r;
+
+        r = uri_from_string(uri, &p11kit_uri);
+        if (r < 0)
+                return log_error_errno(r, "Failed to parse PKCS#11 URI '%s': %m", uri);
+
+        if (sym_p11_kit_uri_get_attribute(p11kit_uri, CKA_CLASS)) {
+                CK_OBJECT_CLASS class = CKO_PRIVATE_KEY;
+                CK_ATTRIBUTE attribute = { CKA_CLASS, &class, sizeof(class) };
+
+                if (sym_p11_kit_uri_set_attribute(p11kit_uri, &attribute) != P11_KIT_URI_OK)
+                        return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to set class for URI '%s': %m", uri);
+
+                if (sym_p11_kit_uri_format(p11kit_uri, P11_KIT_URI_FOR_ANY, &private_uri) != P11_KIT_URI_OK)
+                        return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to format PKCS#11 URI: %m");
+        }
+
+        *ret_uri = TAKE_PTR(private_uri);
+        return 0;
+}
+
 int enroll_pkcs11(
                 struct crypt_device *cd,
                 const void *volume_key,
@@ -16,13 +40,13 @@ int enroll_pkcs11(
         _cleanup_(erase_and_freep) void *decrypted_key = NULL;
         _cleanup_(erase_and_freep) char *base64_encoded = NULL;
         _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
-        _cleanup_free_ char *keyslot_as_string = NULL;
+        _cleanup_free_ char *keyslot_as_string = NULL, *private_uri = 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;
-        int keyslot, r;
+        int r;
 
         assert_se(cd);
         assert_se(volume_key);
@@ -49,7 +73,7 @@ int enroll_pkcs11(
         if (r < 0)
                 return log_error_errno(r, "Failed to set minimal PBKDF: %m");
 
-        keyslot = crypt_keyslot_add_by_volume_key(
+        int keyslot = crypt_keyslot_add_by_volume_key(
                         cd,
                         CRYPT_ANY_SLOT,
                         volume_key,
@@ -62,12 +86,18 @@ int enroll_pkcs11(
         if (asprintf(&keyslot_as_string, "%i", keyslot) < 0)
                 return log_oom();
 
+        /* Change 'type=cert' in the provided URI to 'type=private' before storing in a LUKS2 header.
+           This allows users to use output of some PKCS#11 tools directly without modifications. */
+        r = uri_set_private_class(uri, &private_uri);
+        if (r < 0)
+                return r;
+
         r = json_build(&v,
-                       JSON_BUILD_OBJECT(
-                                       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(saved_key, saved_key_size))));
+                JSON_BUILD_OBJECT(
+                        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(private_uri ?: uri)),
+                        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 0268d8f7721d83bdf02e6573f263fa5dd7fd858f..87e19814db4a16f430c5ee65eafc227bd87f6dd5 100644 (file)
@@ -51,7 +51,7 @@ static int search_policy_hash(
                         return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
                                                "TPM2 token data lacks 'tpm2-policy-hash' field.");
 
-                r = unhexmem(json_variant_string(w), SIZE_MAX, &thash, &thash_size);
+                r = unhexmem(json_variant_string(w), &thash, &thash_size);
                 if (r < 0)
                         return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
                                                "Invalid base64 data in 'tpm2-policy-hash' field.");
@@ -143,12 +143,10 @@ int enroll_tpm2(struct crypt_device *cd,
                 bool use_pin,
                 const char *pcrlock_path) {
 
-        _cleanup_(erase_and_freep) void *secret = NULL;
         _cleanup_(json_variant_unrefp) JsonVariant *v = NULL, *signature_json = NULL;
         _cleanup_(erase_and_freep) char *base64_encoded = NULL;
-        _cleanup_free_ void *srk_buf = NULL;
-        size_t secret_size, blob_size, pubkey_size = 0, srk_buf_size = 0;
-        _cleanup_free_ void *blob = NULL, *pubkey = NULL;
+        _cleanup_(iovec_done) struct iovec srk = {}, blob = {}, pubkey = {};
+        _cleanup_(iovec_done_erase) struct iovec secret = {};
         const char *node;
         _cleanup_(erase_and_freep) char *pin_str = NULL;
         ssize_t base64_encoded_size;
@@ -194,7 +192,7 @@ int enroll_tpm2(struct crypt_device *cd,
         }
 
         TPM2B_PUBLIC public = {};
-        r = tpm2_load_pcr_public_key(pubkey_path, &pubkey, &pubkey_size);
+        r = tpm2_load_pcr_public_key(pubkey_path, &pubkey.iov_base, &pubkey.iov_len);
         if (r < 0) {
                 if (pubkey_path || signature_path || r != -ENOENT)
                         return log_error_errno(r, "Failed to read TPM PCR public key: %m");
@@ -202,7 +200,7 @@ int enroll_tpm2(struct crypt_device *cd,
                 log_debug_errno(r, "Failed to read TPM2 PCR public key, proceeding without: %m");
                 pubkey_pcr_mask = 0;
         } else {
-                r = tpm2_tpm2b_public_from_pem(pubkey, pubkey_size, &public);
+                r = tpm2_tpm2b_public_from_pem(pubkey.iov_base, pubkey.iov_len, &public);
                 if (r < 0)
                         return log_error_errno(r, "Could not convert public key to TPM2B_PUBLIC: %m");
 
@@ -271,7 +269,7 @@ int enroll_tpm2(struct crypt_device *cd,
         r = tpm2_calculate_sealing_policy(
                         hash_pcr_values,
                         n_hash_pcr_values,
-                        pubkey ? &public : NULL,
+                        iovec_is_set(&pubkey) ? &public : NULL,
                         use_pin,
                         pcrlock_path ? &pcrlock_policy : NULL,
                         &policy);
@@ -283,21 +281,21 @@ int enroll_tpm2(struct crypt_device *cd,
                                 seal_key_handle,
                                 &device_key_public,
                                 /* attributes= */ NULL,
-                                /* secret= */ NULL, /* secret_size= */ 0,
+                                /* secret= */ NULL,
                                 &policy,
                                 pin_str,
-                                &secret, &secret_size,
-                                &blob, &blob_size,
-                                &srk_buf, &srk_buf_size);
+                                &secret,
+                                &blob,
+                                &srk);
         else
                 r = tpm2_seal(tpm2_context,
                               seal_key_handle,
                               &policy,
                               pin_str,
-                              &secret, &secret_size,
-                              &blob, &blob_size,
+                              &secret,
+                              &blob,
                               /* ret_primary_alg= */ NULL,
-                              &srk_buf, &srk_buf_size);
+                              &srk);
         if (r < 0)
                 return log_error_errno(r, "Failed to seal to TPM2: %m");
 
@@ -313,33 +311,32 @@ int enroll_tpm2(struct crypt_device *cd,
         }
 
         /* If possible, verify the sealed data object. */
-        if ((!pubkey || signature_json) && !any_pcr_value_specified && !device_key) {
-                _cleanup_(erase_and_freep) void *secret2 = NULL;
-                size_t secret2_size;
+        if ((!iovec_is_set(&pubkey) || signature_json) && !any_pcr_value_specified && !device_key) {
+                _cleanup_(iovec_done_erase) struct iovec secret2 = {};
 
                 log_debug("Unsealing for verification...");
                 r = tpm2_unseal(tpm2_context,
                                 hash_pcr_mask,
                                 hash_pcr_bank,
-                                pubkey, pubkey_size,
+                                &pubkey,
                                 pubkey_pcr_mask,
                                 signature_json,
                                 pin_str,
                                 pcrlock_path ? &pcrlock_policy : NULL,
                                 /* primary_alg= */ 0,
-                                blob, blob_size,
-                                policy.buffer, policy.size,
-                                srk_buf, srk_buf_size,
-                                &secret2, &secret2_size);
+                                &blob,
+                                &IOVEC_MAKE(policy.buffer, policy.size),
+                                &srk,
+                                &secret2);
                 if (r < 0)
                         return log_error_errno(r, "Failed to unseal secret using TPM2: %m");
 
-                if (memcmp_nn(secret, secret_size, secret2, secret2_size) != 0)
+                if (iovec_memcmp(&secret, &secret2) != 0)
                         return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "TPM2 seal/unseal verification failed.");
         }
 
         /* let's base64 encode the key to use, for compat with homed (and it's easier to every type it in by keyboard, if that might end up being necessary. */
-        base64_encoded_size = base64mem(secret, secret_size, &base64_encoded);
+        base64_encoded_size = base64mem(secret.iov_base, secret.iov_len, &base64_encoded);
         if (base64_encoded_size < 0)
                 return log_error_errno(base64_encoded_size, "Failed to base64 encode secret key: %m");
 
@@ -361,14 +358,13 @@ int enroll_tpm2(struct crypt_device *cd,
                         keyslot,
                         hash_pcr_mask,
                         hash_pcr_bank,
-                        pubkey, pubkey_size,
+                        &pubkey,
                         pubkey_pcr_mask,
                         /* primary_alg= */ 0,
-                        blob, blob_size,
-                        policy.buffer, policy.size,
-                        use_pin ? binary_salt : NULL,
-                        use_pin ? sizeof(binary_salt) : 0,
-                        srk_buf, srk_buf_size,
+                        &blob,
+                        &IOVEC_MAKE(policy.buffer, policy.size),
+                        use_pin ? &IOVEC_MAKE(binary_salt, sizeof(binary_salt)) : NULL,
+                        &srk,
                         flags,
                         &v);
         if (r < 0)
index f991389aa5b5cf026e3c6c44b03e25a30eb64511..4ef249509d46dddbd8e68e7ec17f83d2cbfb3071 100644 (file)
@@ -154,7 +154,7 @@ int find_pkcs11_auto_data(
 
                 assert(!key);
                 assert(key_size == 0);
-                r = unbase64mem(json_variant_string(w), SIZE_MAX, &key, &key_size);
+                r = unbase64mem(json_variant_string(w), &key, &key_size);
                 if (r < 0)
                         return log_error_errno(r, "Failed to decode base64 encoded key.");
         }
index fdb3b17d2dd8f20fdf0ee705d1cb3e2e7b73eecb..2f79d07a87ff74ff7e9bbb78102f00c198d04a7b 100644 (file)
@@ -172,7 +172,7 @@ _public_ int cryptsetup_token_validate(
                 return 1;
         }
 
-        r = unbase64mem(json_variant_string(w), SIZE_MAX, NULL, NULL);
+        r = unbase64mem(json_variant_string(w), NULL, NULL);
         if (r < 0)
                 return crypt_log_debug_errno(cd, r, "Invalid base64 data in 'fido2-credential' field: %m");
 
@@ -182,7 +182,7 @@ _public_ int cryptsetup_token_validate(
                 return 1;
         }
 
-        r = unbase64mem(json_variant_string(w), SIZE_MAX, NULL, NULL);
+        r = unbase64mem(json_variant_string(w), NULL, NULL);
         if (r < 0)
                 return crypt_log_debug_errno(cd, r, "Failed to decode base64 encoded salt: %m.");
 
index 2ac8a270c5d0e846b569721a499a04d424d4ef3a..98f4b08b76302bfff5fbf6a3a9f4f6022fb4625e 100644 (file)
@@ -136,7 +136,7 @@ _public_ int cryptsetup_token_validate(
                 return 1;
         }
 
-        r = unbase64mem(json_variant_string(w), SIZE_MAX, NULL, NULL);
+        r = unbase64mem(json_variant_string(w), NULL, NULL);
         if (r < 0)
                 return crypt_log_debug_errno(cd, r, "Failed to decode base64 encoded key: %m.");
 
index 6fee8319a7f0bb0b11e1fed3d3cacdc036d2f05c..14b98abc060728dc6214faf93dea322ec3ce34bd 100644 (file)
@@ -42,9 +42,8 @@ _public_ int cryptsetup_token_open_pin(
                 void *usrptr /* plugin defined parameter passed to crypt_activate_by_token*() API */) {
 
         _cleanup_(erase_and_freep) char *base64_encoded = NULL, *pin_string = NULL;
-        _cleanup_free_ void *blob = NULL, *pubkey = NULL, *policy_hash = NULL, *salt = NULL, *srk_buf = NULL;
-        size_t blob_size, policy_hash_size, decrypted_key_size, pubkey_size, salt_size = 0, srk_buf_size = 0;
-        _cleanup_(erase_and_freep) void *decrypted_key = NULL;
+        _cleanup_(iovec_done) struct iovec blob = {}, pubkey = {}, policy_hash = {}, salt = {}, srk = {};
+        _cleanup_(iovec_done_erase) struct iovec decrypted_key = {};
         _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
         uint32_t hash_pcr_mask, pubkey_pcr_mask;
         systemd_tpm2_plugin_params params = {
@@ -79,21 +78,16 @@ _public_ int cryptsetup_token_open_pin(
 
         r = tpm2_parse_luks2_json(
                         v,
-                        NULL,
+                        /* ret_keyslot= */ NULL,
                         &hash_pcr_mask,
                         &pcr_bank,
                         &pubkey,
-                        &pubkey_size,
                         &pubkey_pcr_mask,
                         &primary_alg,
                         &blob,
-                        &blob_size,
                         &policy_hash,
-                        &policy_hash_size,
                         &salt,
-                        &salt_size,
-                        &srk_buf,
-                        &srk_buf_size,
+                        &srk,
                         &flags);
         if (r < 0)
                 return log_debug_open_error(cd, r);
@@ -105,28 +99,23 @@ _public_ int cryptsetup_token_open_pin(
                         params.device,
                         hash_pcr_mask,
                         pcr_bank,
-                        pubkey, pubkey_size,
+                        &pubkey,
                         pubkey_pcr_mask,
                         params.signature_path,
                         pin_string,
                         params.pcrlock_path,
                         primary_alg,
-                        blob,
-                        blob_size,
-                        policy_hash,
-                        policy_hash_size,
-                        salt,
-                        salt_size,
-                        srk_buf,
-                        srk_buf_size,
+                        &blob,
+                        &policy_hash,
+                        &salt,
+                        &srk,
                         flags,
-                        &decrypted_key,
-                        &decrypted_key_size);
+                        &decrypted_key);
         if (r < 0)
                 return log_debug_open_error(cd, r);
 
         /* Before using this key as passphrase we base64 encode it, for compat with homed */
-        base64_encoded_size = base64mem(decrypted_key, decrypted_key_size, &base64_encoded);
+        base64_encoded_size = base64mem(decrypted_key.iov_base, decrypted_key.iov_len, &base64_encoded);
         if (base64_encoded_size < 0)
                 return log_debug_open_error(cd, base64_encoded_size);
 
@@ -177,9 +166,8 @@ _public_ void cryptsetup_token_dump(
                 const char *json /* validated 'systemd-tpm2' token if cryptsetup_token_validate is defined */) {
 
         _cleanup_free_ char *hash_pcrs_str = NULL, *pubkey_pcrs_str = NULL, *blob_str = NULL, *policy_hash_str = NULL, *pubkey_str = NULL;
-        _cleanup_free_ void *blob = NULL, *pubkey = NULL, *policy_hash = NULL, *salt = NULL, *srk_buf = NULL;
+        _cleanup_(iovec_done) struct iovec blob = {}, pubkey = {}, policy_hash = {}, salt = {}, srk = {};
         _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
-        size_t blob_size, policy_hash_size, pubkey_size, salt_size = 0, srk_buf_size = 0;
         uint32_t hash_pcr_mask, pubkey_pcr_mask;
         uint16_t pcr_bank, primary_alg;
         TPM2Flags flags = 0;
@@ -197,17 +185,12 @@ _public_ void cryptsetup_token_dump(
                         &hash_pcr_mask,
                         &pcr_bank,
                         &pubkey,
-                        &pubkey_size,
                         &pubkey_pcr_mask,
                         &primary_alg,
                         &blob,
-                        &blob_size,
                         &policy_hash,
-                        &policy_hash_size,
                         &salt,
-                        &salt_size,
-                        &srk_buf,
-                        &srk_buf_size,
+                        &srk,
                         &flags);
         if (r < 0)
                 return (void) crypt_log_debug_errno(cd, r, "Failed to parse " TOKEN_NAME " JSON fields: %m");
@@ -220,15 +203,15 @@ _public_ void cryptsetup_token_dump(
         if (!pubkey_pcrs_str)
                 return (void) crypt_log_debug_errno(cd, ENOMEM, "Cannot format PCR hash mask: %m");
 
-        r = crypt_dump_buffer_to_hex_string(blob, blob_size, &blob_str);
+        r = crypt_dump_buffer_to_hex_string(blob.iov_base, blob.iov_len, &blob_str);
         if (r < 0)
                 return (void) crypt_log_debug_errno(cd, r, "Cannot dump " TOKEN_NAME " content: %m");
 
-        r = crypt_dump_buffer_to_hex_string(pubkey, pubkey_size, &pubkey_str);
+        r = crypt_dump_buffer_to_hex_string(pubkey.iov_base, pubkey.iov_len, &pubkey_str);
         if (r < 0)
                 return (void) crypt_log_debug_errno(cd, r, "Cannot dump " TOKEN_NAME " content: %m");
 
-        r = crypt_dump_buffer_to_hex_string(policy_hash, policy_hash_size, &policy_hash_str);
+        r = crypt_dump_buffer_to_hex_string(policy_hash.iov_base, policy_hash.iov_len, &policy_hash_str);
         if (r < 0)
                 return (void) crypt_log_debug_errno(cd, r, "Cannot dump " TOKEN_NAME " content: %m");
 
@@ -241,8 +224,8 @@ _public_ void cryptsetup_token_dump(
         crypt_log(cd, "\ttpm2-policy-hash:" CRYPT_DUMP_LINE_SEP "%s\n", policy_hash_str);
         crypt_log(cd, "\ttpm2-pin:         %s\n", true_false(flags & TPM2_FLAGS_USE_PIN));
         crypt_log(cd, "\ttpm2-pcrlock:     %s\n", true_false(flags & TPM2_FLAGS_USE_PCRLOCK));
-        crypt_log(cd, "\ttpm2-salt:        %s\n", true_false(salt));
-        crypt_log(cd, "\ttpm2-srk:         %s\n", true_false(srk_buf));
+        crypt_log(cd, "\ttpm2-salt:        %s\n", true_false(iovec_is_set(&salt)));
+        crypt_log(cd, "\ttpm2-srk:         %s\n", true_false(iovec_is_set(&srk)));
 }
 
 /*
@@ -326,7 +309,7 @@ _public_ int cryptsetup_token_validate(
                 return 1;
         }
 
-        r = unbase64mem(json_variant_string(w), SIZE_MAX, NULL, NULL);
+        r = unbase64mem(json_variant_string(w), NULL, NULL);
         if (r < 0)
                 return crypt_log_debug_errno(cd, r, "Invalid base64 data in 'tpm2-blob' field: %m");
 
@@ -336,7 +319,7 @@ _public_ int cryptsetup_token_validate(
                 return 1;
         }
 
-        r = unhexmem(json_variant_string(w), SIZE_MAX, NULL, NULL);
+        r = unhexmem(json_variant_string(w), NULL, NULL);
         if (r < 0)
                 return crypt_log_debug_errno(cd, r, "Invalid base64 data in 'tpm2-policy-hash' field: %m");
 
index a1c85e600c7e3d7211faef1caa8f9bb0ea28dc33..5b386133a82a14831e8eee67aa249b4ac8a523d4 100644 (file)
@@ -104,7 +104,7 @@ int parse_luks2_fido2_data(
         if (!w)
                 return -EINVAL;
 
-        r = unbase64mem(json_variant_string(w), SIZE_MAX, &cid, &cid_size);
+        r = unbase64mem(json_variant_string(w), &cid, &cid_size);
         if (r < 0)
                 return crypt_log_error_errno(cd, r, "Failed to parse 'fido2-credentials' field: %m");
 
@@ -112,7 +112,7 @@ int parse_luks2_fido2_data(
         if (!w)
                 return -EINVAL;
 
-        r = unbase64mem(json_variant_string(w), SIZE_MAX, &salt, &salt_size);
+        r = unbase64mem(json_variant_string(w), &salt, &salt_size);
         if (r < 0)
                 return crypt_log_error_errno(cd, r, "Failed to parse 'fido2-salt' field: %m");
 
index 178fc7a87b4c5620b52e5da35a965f2bb4c7b4eb..512a4cf18ac99b93eb9c0f631b0181bd44935ecd 100644 (file)
@@ -260,7 +260,7 @@ int parse_luks2_pkcs11_data(
         if (!w)
                 return -EINVAL;
 
-        r = unbase64mem(json_variant_string(w), SIZE_MAX, &key, &key_size);
+        r = unbase64mem(json_variant_string(w), &key, &key_size);
         if (r < 0)
                 return crypt_log_debug_errno(cd, r, "Failed to decode base64 encoded key: %m.");
 
index 72be5cc71d3b4453d26d936778479977f294ef7b..e2e4d0dc049b7aec121b6cc737b35748c92059ac 100644 (file)
@@ -17,33 +17,26 @@ int acquire_luks2_key(
                 const char *device,
                 uint32_t hash_pcr_mask,
                 uint16_t pcr_bank,
-                const void *pubkey,
-                size_t pubkey_size,
+                const struct iovec *pubkey,
                 uint32_t pubkey_pcr_mask,
                 const char *signature_path,
                 const char *pin,
                 const char *pcrlock_path,
                 uint16_t primary_alg,
-                const void *key_data,
-                size_t key_data_size,
-                const void *policy_hash,
-                size_t policy_hash_size,
-                const void *salt,
-                size_t salt_size,
-                const void *srk_buf,
-                size_t srk_buf_size,
+                const struct iovec *blob,
+                const struct iovec *policy_hash,
+                const struct iovec *salt,
+                const struct iovec *srk,
                 TPM2Flags flags,
-                void **ret_decrypted_key,
-                size_t *ret_decrypted_key_size) {
+                struct iovec *ret_decrypted_key) {
 
         _cleanup_(json_variant_unrefp) JsonVariant *signature_json = NULL;
         _cleanup_free_ char *auto_device = NULL;
         _cleanup_(erase_and_freep) char *b64_salted_pin = NULL;
         int r;
 
-        assert(salt || salt_size == 0);
+        assert(iovec_is_valid(salt));
         assert(ret_decrypted_key);
-        assert(ret_decrypted_key_size);
 
         if (!device) {
                 r = tpm2_find_device_auto(&auto_device);
@@ -58,10 +51,10 @@ int acquire_luks2_key(
         if ((flags & TPM2_FLAGS_USE_PIN) && !pin)
                 return -ENOANO;
 
-        if (pin && salt_size > 0) {
+        if (pin && iovec_is_set(salt)) {
                 uint8_t salted_pin[SHA256_DIGEST_SIZE] = {};
                 CLEANUP_ERASE(salted_pin);
-                r = tpm2_util_pbkdf2_hmac_sha256(pin, strlen(pin), salt, salt_size, salted_pin);
+                r = tpm2_util_pbkdf2_hmac_sha256(pin, strlen(pin), salt->iov_base, salt->iov_len, salted_pin);
                 if (r < 0)
                         return log_error_errno(r, "Failed to perform PBKDF2: %m");
 
@@ -92,16 +85,16 @@ int acquire_luks2_key(
         r = tpm2_unseal(tpm2_context,
                         hash_pcr_mask,
                         pcr_bank,
-                        pubkey, pubkey_size,
+                        pubkey,
                         pubkey_pcr_mask,
                         signature_json,
                         pin,
                         FLAGS_SET(flags, TPM2_FLAGS_USE_PCRLOCK) ? &pcrlock_policy : NULL,
                         primary_alg,
-                        key_data, key_data_size,
-                        policy_hash, policy_hash_size,
-                        srk_buf, srk_buf_size,
-                        ret_decrypted_key, ret_decrypted_key_size);
+                        blob,
+                        policy_hash,
+                        srk,
+                        ret_decrypted_key);
         if (r < 0)
                 return log_error_errno(r, "Failed to unseal secret using TPM2: %m");
 
index d84e5a3c3ba713be515c6a546aa8ea5d721fc2ed..20151d6ca9f89b037491b1dfc40d6b8165567c5c 100644 (file)
@@ -10,21 +10,15 @@ int acquire_luks2_key(
                 const char *device,
                 uint32_t pcr_mask,
                 uint16_t pcr_bank,
-                const void *pubkey,
-                size_t pubkey_size,
+                const struct iovec *pubkey,
                 uint32_t pubkey_pcr_mask,
                 const char *signature_path,
                 const char *pcrlock_path,
                 const char *pin,
                 uint16_t primary_alg,
-                const void *key_data,
-                size_t key_data_size,
-                const void *policy_hash,
-                size_t policy_hash_size,
-                const void *salt,
-                size_t salt_size,
-                const void *srk_buf,
-                size_t srk_buf_size,
+                const struct iovec *key_data,
+                const struct iovec *policy_hash,
+                const struct iovec *salt,
+                const struct iovec *srk,
                 TPM2Flags flags,
-                void **ret_decrypted_key,
-                size_t *ret_decrypted_key_size);
+                struct iovec *decrypted_key);
index f59d5f9d1dc0b8cdc041606324c63183657f1c23..fc0ec443a512a9e486d867131b3c358971b9742c 100644 (file)
@@ -58,8 +58,7 @@ int acquire_tpm2_key(
                 const char *device,
                 uint32_t hash_pcr_mask,
                 uint16_t pcr_bank,
-                const void *pubkey,
-                size_t pubkey_size,
+                const struct iovec *pubkey,
                 uint32_t pubkey_pcr_mask,
                 const char *signature_path,
                 const char *pcrlock_path,
@@ -67,29 +66,23 @@ int acquire_tpm2_key(
                 const char *key_file,
                 size_t key_file_size,
                 uint64_t key_file_offset,
-                const void *key_data,
-                size_t key_data_size,
-                const void *policy_hash,
-                size_t policy_hash_size,
-                const void *salt,
-                size_t salt_size,
-                const void *srk_buf,
-                size_t srk_buf_size,
+                const struct iovec *key_data,
+                const struct iovec *policy_hash,
+                const struct iovec *salt,
+                const struct iovec *srk,
                 TPM2Flags flags,
                 usec_t until,
                 bool headless,
                 AskPasswordFlags ask_password_flags,
-                void **ret_decrypted_key,
-                size_t *ret_decrypted_key_size) {
+                struct iovec *ret_decrypted_key) {
 
         _cleanup_(json_variant_unrefp) JsonVariant *signature_json = NULL;
         _cleanup_free_ void *loaded_blob = NULL;
         _cleanup_free_ char *auto_device = NULL;
-        size_t blob_size;
-        const void *blob;
+        struct iovec blob;
         int r;
 
-        assert(salt || salt_size == 0);
+        assert(iovec_is_valid(salt));
 
         if (!device) {
                 r = tpm2_find_device_auto(&auto_device);
@@ -101,10 +94,9 @@ int acquire_tpm2_key(
                 device = auto_device;
         }
 
-        if (key_data) {
-                blob = key_data;
-                blob_size = key_data_size;
-        } else {
+        if (iovec_is_set(key_data))
+                blob = *key_data;
+        else {
                 _cleanup_free_ char *bindname = NULL;
 
                 /* If we read the salt via AF_UNIX, make this client recognizable */
@@ -117,11 +109,11 @@ int acquire_tpm2_key(
                                 key_file_size == 0 ? SIZE_MAX : key_file_size,
                                 READ_FULL_FILE_CONNECT_SOCKET,
                                 bindname,
-                                (char**) &loaded_blob, &blob_size);
+                                (char**) &loaded_blob, &blob.iov_len);
                 if (r < 0)
                         return r;
 
-                blob = loaded_blob;
+                blob.iov_base = loaded_blob;
         }
 
         if (pubkey_pcr_mask != 0) {
@@ -147,20 +139,16 @@ int acquire_tpm2_key(
                 r = tpm2_unseal(tpm2_context,
                                 hash_pcr_mask,
                                 pcr_bank,
-                                pubkey, pubkey_size,
+                                pubkey,
                                 pubkey_pcr_mask,
                                 signature_json,
                                 /* pin= */ NULL,
                                 FLAGS_SET(flags, TPM2_FLAGS_USE_PCRLOCK) ? &pcrlock_policy : NULL,
                                 primary_alg,
-                                blob,
-                                blob_size,
+                                &blob,
                                 policy_hash,
-                                policy_hash_size,
-                                srk_buf,
-                                srk_buf_size,
-                                ret_decrypted_key,
-                                ret_decrypted_key_size);
+                                srk,
+                                ret_decrypted_key);
                 if (r < 0)
                         return log_error_errno(r, "Failed to unseal secret using TPM2: %m");
 
@@ -177,11 +165,11 @@ int acquire_tpm2_key(
                 if (r < 0)
                         return r;
 
-                if (salt_size > 0) {
+                if (iovec_is_set(salt)) {
                         uint8_t salted_pin[SHA256_DIGEST_SIZE] = {};
                         CLEANUP_ERASE(salted_pin);
 
-                        r = tpm2_util_pbkdf2_hmac_sha256(pin_str, strlen(pin_str), salt, salt_size, salted_pin);
+                        r = tpm2_util_pbkdf2_hmac_sha256(pin_str, strlen(pin_str), salt->iov_base, salt->iov_len, salted_pin);
                         if (r < 0)
                                 return log_error_errno(r, "Failed to perform PBKDF2: %m");
 
@@ -195,20 +183,16 @@ int acquire_tpm2_key(
                 r = tpm2_unseal(tpm2_context,
                                 hash_pcr_mask,
                                 pcr_bank,
-                                pubkey, pubkey_size,
+                                pubkey,
                                 pubkey_pcr_mask,
                                 signature_json,
                                 b64_salted_pin,
                                 pcrlock_path ? &pcrlock_policy : NULL,
                                 primary_alg,
-                                blob,
-                                blob_size,
+                                &blob,
                                 policy_hash,
-                                policy_hash_size,
-                                srk_buf,
-                                srk_buf_size,
-                                ret_decrypted_key,
-                                ret_decrypted_key_size);
+                                srk,
+                                ret_decrypted_key);
                 if (r < 0) {
                         log_error_errno(r, "Failed to unseal secret using TPM2: %m");
 
@@ -228,18 +212,13 @@ int find_tpm2_auto_data(
                 int start_token,
                 uint32_t *ret_hash_pcr_mask,
                 uint16_t *ret_pcr_bank,
-                void **ret_pubkey,
-                size_t *ret_pubkey_size,
+                struct iovec *ret_pubkey,
                 uint32_t *ret_pubkey_pcr_mask,
                 uint16_t *ret_primary_alg,
-                void **ret_blob,
-                size_t *ret_blob_size,
-                void **ret_policy_hash,
-                size_t *ret_policy_hash_size,
-                void **ret_salt,
-                size_t *ret_salt_size,
-                void **ret_srk_buf,
-                size_t *ret_srk_buf_size,
+                struct iovec *ret_blob,
+                struct iovec *ret_policy_hash,
+                struct iovec *ret_salt,
+                struct iovec *ret_srk,
                 TPM2Flags *ret_flags,
                 int *ret_keyslot,
                 int *ret_token) {
@@ -249,9 +228,8 @@ int find_tpm2_auto_data(
         assert(cd);
 
         for (token = start_token; token < sym_crypt_token_max(CRYPT_LUKS2); token++) {
-                _cleanup_free_ void *blob = NULL, *policy_hash = NULL, *pubkey = NULL, *salt = NULL, *srk_buf = NULL;
+                _cleanup_(iovec_done) struct iovec blob = {}, policy_hash = {}, pubkey = {}, salt = {}, srk = {};
                 _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
-                size_t blob_size, policy_hash_size, pubkey_size, salt_size = 0, srk_buf_size = 0;
                 uint32_t hash_pcr_mask, pubkey_pcr_mask;
                 uint16_t pcr_bank, primary_alg;
                 TPM2Flags flags;
@@ -268,13 +246,13 @@ int find_tpm2_auto_data(
                                 &keyslot,
                                 &hash_pcr_mask,
                                 &pcr_bank,
-                                &pubkey, &pubkey_size,
+                                &pubkey,
                                 &pubkey_pcr_mask,
                                 &primary_alg,
-                                &blob, &blob_size,
-                                &policy_hash, &policy_hash_size,
-                                &salt, &salt_size,
-                                &srk_buf, &srk_buf_size,
+                                &blob,
+                                &policy_hash,
+                                &salt,
+                                &srk,
                                 &flags);
                 if (r == -EUCLEAN) /* Gracefully handle issues in JSON fields not owned by us */
                         continue;
@@ -289,20 +267,15 @@ int find_tpm2_auto_data(
 
                         *ret_hash_pcr_mask = hash_pcr_mask;
                         *ret_pcr_bank = pcr_bank;
-                        *ret_pubkey = TAKE_PTR(pubkey);
-                        *ret_pubkey_size = pubkey_size;
+                        *ret_pubkey = TAKE_STRUCT(pubkey);
                         *ret_pubkey_pcr_mask = pubkey_pcr_mask;
                         *ret_primary_alg = primary_alg;
-                        *ret_blob = TAKE_PTR(blob);
-                        *ret_blob_size = blob_size;
-                        *ret_policy_hash = TAKE_PTR(policy_hash);
-                        *ret_policy_hash_size = policy_hash_size;
-                        *ret_salt = TAKE_PTR(salt);
-                        *ret_salt_size = salt_size;
+                        *ret_blob = TAKE_STRUCT(blob);
+                        *ret_policy_hash = TAKE_STRUCT(policy_hash);
+                        *ret_salt = TAKE_STRUCT(salt);
                         *ret_keyslot = keyslot;
                         *ret_token = token;
-                        *ret_srk_buf = TAKE_PTR(srk_buf);
-                        *ret_srk_buf_size = srk_buf_size;
+                        *ret_srk = TAKE_STRUCT(srk);
                         *ret_flags = flags;
                         return 0;
                 }
index a50a9435a986759323e5dd75f90e66ba21c5e478..a593e043749ab1d83cab678d028cfdd827220576 100644 (file)
@@ -16,8 +16,7 @@ int acquire_tpm2_key(
                 const char *device,
                 uint32_t hash_pcr_mask,
                 uint16_t pcr_bank,
-                const void *pubkey,
-                size_t pubkey_size,
+                const struct iovec *pubkey,
                 uint32_t pubkey_pcr_mask,
                 const char *signature_path,
                 const char *pcrlock_path,
@@ -25,20 +24,15 @@ int acquire_tpm2_key(
                 const char *key_file,
                 size_t key_file_size,
                 uint64_t key_file_offset,
-                const void *key_data,
-                size_t key_data_size,
-                const void *policy_hash,
-                size_t policy_hash_size,
-                const void *salt,
-                size_t salt_size,
-                const void *srk_buf,
-                size_t salt_srk_buf_size,
+                const struct iovec *key_data,
+                const struct iovec *policy_hash,
+                const struct iovec *salt,
+                const struct iovec *srk,
                 TPM2Flags flags,
                 usec_t until,
                 bool headless,
                 AskPasswordFlags ask_password_flags,
-                void **ret_decrypted_key,
-                size_t *ret_decrypted_key_size);
+                struct iovec *ret_decrypted_key);
 
 int find_tpm2_auto_data(
                 struct crypt_device *cd,
@@ -46,18 +40,13 @@ int find_tpm2_auto_data(
                 int start_token,
                 uint32_t *ret_hash_pcr_mask,
                 uint16_t *ret_pcr_bank,
-                void **ret_pubkey,
-                size_t *ret_pubkey_size,
+                struct iovec *ret_pubkey,
                 uint32_t *ret_pubkey_pcr_mask,
                 uint16_t *ret_primary_alg,
-                void **ret_blob,
-                size_t *ret_blob_size,
-                void **ret_policy_hash,
-                size_t *ret_policy_hash_size,
-                void **ret_salt,
-                size_t *ret_salt_size,
-                void **ret_srk_buf,
-                size_t *ret_srk_size,
+                struct iovec *ret_blob,
+                struct iovec *ret_policy_hash,
+                struct iovec *ret_salt,
+                struct iovec *ret_srk,
                 TPM2Flags *ret_flags,
                 int *ret_keyslot,
                 int *ret_token);
@@ -69,8 +58,7 @@ static inline int acquire_tpm2_key(
                 const char *device,
                 uint32_t hash_pcr_mask,
                 uint16_t pcr_bank,
-                const void *pubkey,
-                size_t pubkey_size,
+                const struct iovec *pubkey,
                 uint32_t pubkey_pcr_mask,
                 const char *signature_path,
                 const char *pcrlock_path,
@@ -78,20 +66,15 @@ static inline int acquire_tpm2_key(
                 const char *key_file,
                 size_t key_file_size,
                 uint64_t key_file_offset,
-                const void *key_data,
-                size_t key_data_size,
-                const void *policy_hash,
-                size_t policy_hash_size,
-                const void *salt,
-                size_t salt_size,
-                const void *srk_buf,
-                size_t salt_srk_buf_size,
+                const struct iovec *key_data,
+                const struct iovec *policy_hash,
+                const struct iovec *salt,
+                const struct iovec *srk,
                 TPM2Flags flags,
                 usec_t until,
                 bool headless,
                 AskPasswordFlags ask_password_flags,
-                void **ret_decrypted_key,
-                size_t *ret_decrypted_key_size) {
+                struct iovec *ret_decrypted_key) {
 
         return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
                                "TPM2 support not available.");
@@ -103,18 +86,13 @@ static inline int find_tpm2_auto_data(
                 int start_token,
                 uint32_t *ret_hash_pcr_mask,
                 uint16_t *ret_pcr_bank,
-                void **ret_pubkey,
-                size_t *ret_pubkey_size,
+                struct iovec *ret_pubkey,
                 uint32_t *ret_pubkey_pcr_mask,
                 uint16_t *ret_primary_alg,
-                void **ret_blob,
-                size_t *ret_blob_size,
-                void **ret_policy_hash,
-                size_t *ret_policy_hash_size,
-                void **ret_salt,
-                size_t *ret_salt_size,
-                void **ret_srk_buf,
-                size_t *ret_srk_size,
+                struct iovec *ret_blob,
+                struct iovec *ret_policy_hash,
+                struct iovec *ret_salt,
+                struct iovec *ret_srk,
                 TPM2Flags *ret_flags,
                 int *ret_keyslot,
                 int *ret_token) {
index b56b51a134f2eb761fa4fdcff81c472fcdec7e5b..a8be8052370dbfa262f0f106c6a8239c696ed7c1 100644 (file)
@@ -368,7 +368,7 @@ static int parse_one_option(const char *option) {
                         _cleanup_free_ void *cid = NULL;
                         size_t cid_size;
 
-                        r = unbase64mem(val, SIZE_MAX, &cid, &cid_size);
+                        r = unbase64mem(val, &cid, &cid_size);
                         if (r < 0)
                                 return log_error_errno(r, "Failed to decode FIDO2 CID data: %m");
 
@@ -1650,18 +1650,16 @@ static int attach_luks_or_plain_or_bitlk_by_tpm2(
                 struct crypt_device *cd,
                 const char *name,
                 const char *key_file,
-                const void *key_data,
-                size_t key_data_size,
+                const struct iovec *key_data,
                 usec_t until,
                 uint32_t flags,
                 bool pass_volume_key) {
 
         _cleanup_(sd_device_monitor_unrefp) sd_device_monitor *monitor = NULL;
-        _cleanup_(erase_and_freep) void *decrypted_key = NULL;
+        _cleanup_(iovec_done_erase) struct iovec decrypted_key = {};
         _cleanup_(sd_event_unrefp) sd_event *event = NULL;
         _cleanup_free_ char *friendly = NULL;
         int keyslot = arg_key_slot, r;
-        size_t decrypted_key_size;
 
         assert(cd);
         assert(name);
@@ -1672,7 +1670,7 @@ static int attach_luks_or_plain_or_bitlk_by_tpm2(
                 return log_oom();
 
         for (;;) {
-                if (key_file || key_data) {
+                if (key_file || iovec_is_set(key_data)) {
                         /* If key data is specified, use that */
 
                         r = acquire_tpm2_key(
@@ -1680,21 +1678,21 @@ static int attach_luks_or_plain_or_bitlk_by_tpm2(
                                         arg_tpm2_device,
                                         arg_tpm2_pcr_mask == UINT32_MAX ? TPM2_PCR_MASK_DEFAULT : arg_tpm2_pcr_mask,
                                         UINT16_MAX,
-                                        /* pubkey= */ NULL, /* pubkey_size= */ 0,
+                                        /* pubkey= */ NULL,
                                         /* pubkey_pcr_mask= */ 0,
                                         /* signature_path= */ NULL,
                                         /* pcrlock_path= */ NULL,
                                         /* primary_alg= */ 0,
                                         key_file, arg_keyfile_size, arg_keyfile_offset,
-                                        key_data, key_data_size,
-                                        /* policy_hash= */ NULL, /* policy_hash_size= */ 0, /* we don't know the policy hash */
-                                        /* salt= */ NULL, /* salt_size= */ 0,
-                                        /* srk_buf= */ NULL, /* srk_buf_size= */ 0,
+                                        key_data,
+                                        /* policy_hash= */ NULL, /* we don't know the policy hash */
+                                        /* salt= */ NULL,
+                                        /* srk= */ NULL,
                                         arg_tpm2_pin ? TPM2_FLAGS_USE_PIN : 0,
                                         until,
                                         arg_headless,
                                         arg_ask_password_flags,
-                                        &decrypted_key, &decrypted_key_size);
+                                        &decrypted_key);
                         if (r >= 0)
                                 break;
                         if (IN_SET(r, -EACCES, -ENOLCK))
@@ -1725,8 +1723,7 @@ static int attach_luks_or_plain_or_bitlk_by_tpm2(
                 }
 
                 if (r == -EOPNOTSUPP) { /* Plugin not available, let's process TPM2 stuff right here instead */
-                        _cleanup_free_ void *blob = NULL, *policy_hash = NULL;
-                        size_t blob_size, policy_hash_size;
+                        _cleanup_(iovec_done) struct iovec blob = {}, policy_hash = {};
                         bool found_some = false;
                         int token = 0; /* first token to look at */
 
@@ -1735,8 +1732,7 @@ static int attach_luks_or_plain_or_bitlk_by_tpm2(
                          * works. */
 
                         for (;;) {
-                                _cleanup_free_ void *pubkey = NULL, *salt = NULL, *srk_buf = NULL;
-                                size_t pubkey_size = 0, salt_size = 0, srk_buf_size = 0;
+                                _cleanup_(iovec_done) struct iovec pubkey = {}, salt = {}, srk = {};
                                 uint32_t hash_pcr_mask, pubkey_pcr_mask;
                                 uint16_t pcr_bank, primary_alg;
                                 TPM2Flags tpm2_flags;
@@ -1747,13 +1743,13 @@ static int attach_luks_or_plain_or_bitlk_by_tpm2(
                                                 token, /* search for the token with this index, or any later index than this */
                                                 &hash_pcr_mask,
                                                 &pcr_bank,
-                                                &pubkey, &pubkey_size,
+                                                &pubkey,
                                                 &pubkey_pcr_mask,
                                                 &primary_alg,
-                                                &blob, &blob_size,
-                                                &policy_hash, &policy_hash_size,
-                                                &salt, &salt_size,
-                                                &srk_buf, &srk_buf_size,
+                                                &blob,
+                                                &policy_hash,
+                                                &salt,
+                                                &srk,
                                                 &tpm2_flags,
                                                 &keyslot,
                                                 &token);
@@ -1778,21 +1774,21 @@ static int attach_luks_or_plain_or_bitlk_by_tpm2(
                                                 arg_tpm2_device,
                                                 hash_pcr_mask,
                                                 pcr_bank,
-                                                pubkey, pubkey_size,
+                                                &pubkey,
                                                 pubkey_pcr_mask,
                                                 arg_tpm2_signature,
                                                 arg_tpm2_pcrlock,
                                                 primary_alg,
                                                 /* key_file= */ NULL, /* key_file_size= */ 0, /* key_file_offset= */ 0, /* no key file */
-                                                blob, blob_size,
-                                                policy_hash, policy_hash_size,
-                                                salt, salt_size,
-                                                srk_buf, srk_buf_size,
+                                                &blob,
+                                                &policy_hash,
+                                                &salt,
+                                                &srk,
                                                 tpm2_flags,
                                                 until,
                                                 arg_headless,
                                                 arg_ask_password_flags,
-                                                &decrypted_key, &decrypted_key_size);
+                                                &decrypted_key);
                                 if (IN_SET(r, -EACCES, -ENOLCK))
                                         return log_notice_errno(SYNTHETIC_ERRNO(EAGAIN), "TPM2 PIN unlock failed, falling back to traditional unlocking.");
                                 if (r != -EPERM)
@@ -1837,17 +1833,16 @@ static int attach_luks_or_plain_or_bitlk_by_tpm2(
 
                 log_debug("Got one or more potentially relevant udev events, rescanning for TPM2...");
         }
-        assert(decrypted_key);
 
         if (pass_volume_key)
-                r = measured_crypt_activate_by_volume_key(cd, name, decrypted_key, decrypted_key_size, flags);
+                r = measured_crypt_activate_by_volume_key(cd, name, decrypted_key.iov_base, decrypted_key.iov_len, flags);
         else {
                 _cleanup_(erase_and_freep) char *base64_encoded = NULL;
                 ssize_t base64_encoded_size;
 
                 /* Before using this key as passphrase we base64 encode it, for compat with homed */
 
-                base64_encoded_size = base64mem(decrypted_key, decrypted_key_size, &base64_encoded);
+                base64_encoded_size = base64mem(decrypted_key.iov_base, decrypted_key.iov_len, &base64_encoded);
                 if (base64_encoded_size < 0)
                         return log_oom();
 
@@ -2045,7 +2040,7 @@ static int attach_luks_or_plain_or_bitlk(
                  crypt_get_device_name(cd));
 
         if (arg_tpm2_device || arg_tpm2_device_auto)
-                return attach_luks_or_plain_or_bitlk_by_tpm2(cd, name, key_file, key_data, key_data_size, until, flags, pass_volume_key);
+                return attach_luks_or_plain_or_bitlk_by_tpm2(cd, name, key_file, &IOVEC_MAKE(key_data, key_data_size), until, flags, pass_volume_key);
         if (arg_fido2_device || arg_fido2_device_auto)
                 return attach_luks_or_plain_or_bitlk_by_fido2(cd, name, key_file, key_data, key_data_size, until, flags, pass_volume_key);
         if (arg_pkcs11_uri || arg_pkcs11_uri_auto)
index 6a8193f6187fadb39633936377efaf43ed6e0648..2b080257fed7363fff77e02c8a3f12a19a17ad69 100644 (file)
@@ -46,8 +46,9 @@
 #include "strv.h"
 #include "terminal-util.h"
 #include "tmpfile-util.h"
-#include "uid-alloc-range.h"
+#include "uid-classification.h"
 #include "user-util.h"
+#include "vpick.h"
 
 static enum {
         ACTION_DISSECT,
@@ -422,7 +423,7 @@ static int parse_argv(int argc, char *argv[]) {
                         _cleanup_free_ void *p = NULL;
                         size_t l;
 
-                        r = unhexmem(optarg, strlen(optarg), &p, &l);
+                        r = unhexmem(optarg, &p, &l);
                         if (r < 0)
                                 return log_error_errno(r, "Failed to parse root hash '%s': %m", optarg);
                         if (l < sizeof(sd_id128_t))
@@ -440,7 +441,7 @@ static int parse_argv(int argc, char *argv[]) {
                         void *p;
 
                         if ((value = startswith(optarg, "base64:"))) {
-                                r = unbase64mem(value, strlen(value), &p, &l);
+                                r = unbase64mem(value, &p, &l);
                                 if (r < 0)
                                         return log_error_errno(r, "Failed to parse root hash signature '%s': %m", optarg);
                         } else {
@@ -1708,6 +1709,8 @@ static int action_detach(const char *path) {
         struct stat st;
         int r;
 
+        assert(path);
+
         fd = open(path, O_PATH|O_CLOEXEC);
         if (fd < 0)
                 return log_error_errno(errno, "Failed to open '%s': %m", path);
@@ -1817,6 +1820,16 @@ static int run(int argc, char *argv[]) {
         if (r <= 0)
                 return r;
 
+        if (arg_image) {
+                r = path_pick_update_warn(
+                                &arg_image,
+                                &pick_filter_image_raw,
+                                PICK_ARCHITECTURE|PICK_TRIES,
+                                /* ret_result= */ NULL);
+                if (r < 0)
+                        return r;
+        }
+
         switch (arg_action) {
         case ACTION_UMOUNT:
                 return action_umount(arg_path);
index 0e92c56e802c63986351abb485cad0a67083b046..18a7cb83b97d62a28e86a6c552900f2ced9495f7 100644 (file)
@@ -89,6 +89,8 @@ STATIC_DESTRUCTOR_REGISTER(arg_keymap, freep);
 STATIC_DESTRUCTOR_REGISTER(arg_timezone, freep);
 STATIC_DESTRUCTOR_REGISTER(arg_hostname, freep);
 STATIC_DESTRUCTOR_REGISTER(arg_root_password, erase_and_freep);
+STATIC_DESTRUCTOR_REGISTER(arg_root_shell, freep);
+STATIC_DESTRUCTOR_REGISTER(arg_kernel_cmdline, freep);
 STATIC_DESTRUCTOR_REGISTER(arg_image_policy, image_policy_freep);
 
 static bool press_any_key(void) {
@@ -1239,11 +1241,13 @@ static int help(void) {
                "     --timezone=TIMEZONE          Set timezone\n"
                "     --hostname=NAME              Set hostname\n"
                "     --setup-machine-id           Set a random machine ID\n"
-               "     --machine-ID=ID              Set specified machine ID\n"
+               "     --machine-id=ID              Set specified machine ID\n"
                "     --root-password=PASSWORD     Set root password from plaintext password\n"
                "     --root-password-file=FILE    Set root password from file\n"
                "     --root-password-hashed=HASH  Set root password from hashed password\n"
                "     --root-shell=SHELL           Set root shell\n"
+               "     --kernel-command-line=CMDLINE\n"
+               "                                  Set kernel command line\n"
                "     --prompt-locale              Prompt the user for locale settings\n"
                "     --prompt-keymap              Prompt the user for keymap settings\n"
                "     --prompt-timezone            Prompt the user for timezone\n"
index 729209fc89d5016c99c7cfcb2743d2cde52a2921..4ec8989a3ff29c3cde2b306fa1f06f4e31eb1f6a 100644 (file)
@@ -54,7 +54,7 @@ static void start_target(const char *target, const char *mode) {
         log_info("Requesting %s/start/%s", target, mode);
 
         /* Start this unit only if we can replace basic.target with it */
-        r = bus_call_method(bus, bus_systemd_mgr, "StartUnitReplace", &error, NULL, "sss", "basic.target", target, mode);
+        r = bus_call_method(bus, bus_systemd_mgr, "StartUnitReplace", &error, NULL, "sss", SPECIAL_BASIC_TARGET, target, mode);
 
         /* Don't print a warning if we aren't called during startup */
         if (r < 0 && !sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_JOB))
index a5bafc63f4769e3202532e352f9bd3e3e8746b48..a18b2bc4c9d1494f6ba9c30422e46d36675a053f 100644 (file)
@@ -33,14 +33,14 @@ sd_char *startswith_no_case(const sd_char *s, const sd_char *prefix) {
         return (sd_char*) s + l;
 }
 
-sd_char* endswith(const sd_char *s, const sd_char *postfix) {
+sd_char* endswith(const sd_char *s, const sd_char *suffix) {
         size_t sl, pl;
 
         assert(s);
-        assert(postfix);
+        assert(suffix);
 
         sl = strlen(s);
-        pl = strlen(postfix);
+        pl = strlen(suffix);
 
         if (pl == 0)
                 return (sd_char*) s + sl;
@@ -48,20 +48,20 @@ sd_char* endswith(const sd_char *s, const sd_char *postfix) {
         if (sl < pl)
                 return NULL;
 
-        if (strcmp(s + sl - pl, postfix) != 0)
+        if (!streq(s + sl - pl, suffix))
                 return NULL;
 
         return (sd_char*) s + sl - pl;
 }
 
-sd_char* endswith_no_case(const sd_char *s, const sd_char *postfix) {
+sd_char* endswith_no_case(const sd_char *s, const sd_char *suffix) {
         size_t sl, pl;
 
         assert(s);
-        assert(postfix);
+        assert(suffix);
 
         sl = strlen(s);
-        pl = strlen(postfix);
+        pl = strlen(suffix);
 
         if (pl == 0)
                 return (sd_char*) s + sl;
@@ -69,7 +69,7 @@ sd_char* endswith_no_case(const sd_char *s, const sd_char *postfix) {
         if (sl < pl)
                 return NULL;
 
-        if (strcasecmp(s + sl - pl, postfix) != 0)
+        if (!strcaseeq(s + sl - pl, suffix))
                 return NULL;
 
         return (sd_char*) s + sl - pl;
index b537b2e31c738d559c0ebfcc4e5c39e729a0dcbf..419f1cc3da43e9e9bd036fffb24d4133c9b73a9a 100644 (file)
@@ -59,8 +59,8 @@ static inline size_t strlen_ptr(const sd_char *s) {
 
 sd_char *startswith(const sd_char *s, const sd_char *prefix) _pure_;
 sd_char *startswith_no_case(const sd_char *s, const sd_char *prefix) _pure_;
-sd_char *endswith(const sd_char *s, const sd_char *postfix) _pure_;
-sd_char *endswith_no_case(const sd_char *s, const sd_char *postfix) _pure_;
+sd_char *endswith(const sd_char *s, const sd_char *suffix) _pure_;
+sd_char *endswith_no_case(const sd_char *s, const sd_char *suffix) _pure_;
 
 static inline bool isempty(const sd_char *a) {
         return !a || a[0] == '\0';
index a751a0ac4cdad9dc07963c494c40e2321f68bfda..c6db83a9d3b0ca26d6c29f90cacc35ff8310c4e1 100644 (file)
@@ -192,7 +192,7 @@ static int get_efi_hibernate_location(EFIHibernateLocation **ret) {
         if (!e)
                 return log_oom();
 
-        r = json_dispatch(v, dispatch_table, JSON_LOG, e);
+        r = json_dispatch(v, dispatch_table, JSON_LOG|JSON_ALLOW_EXTENSIONS, e);
         if (r < 0)
                 return r;
 
index 5a2eb8f31b590dc52dc71626e730776ba666cd92..222bf36e581b241937a8bfd1e902f4fb2a7827a3 100644 (file)
@@ -42,7 +42,7 @@
 #include "rlimit-util.h"
 #include "spawn-polkit-agent.h"
 #include "terminal-util.h"
-#include "uid-alloc-range.h"
+#include "uid-classification.h"
 #include "user-record.h"
 #include "user-record-password-quality.h"
 #include "user-record-show.h"
index 7673e5043576bc830e9d0272f10a04cbfc0b3a16..ea86dffb5289477e4926c9f1f83a5a2cecbb816a 100644 (file)
@@ -38,7 +38,7 @@
 #include "stat-util.h"
 #include "string-table.h"
 #include "strv.h"
-#include "uid-alloc-range.h"
+#include "uid-classification.h"
 #include "user-record-password-quality.h"
 #include "user-record-sign.h"
 #include "user-record-util.h"
@@ -650,11 +650,17 @@ static int convert_worker_errno(Home *h, int e, sd_bus_error *error) {
         return 0;
 }
 
-static void home_count_bad_authentication(Home *h, bool save) {
+static void home_count_bad_authentication(Home *h, int error, bool save) {
         int r;
 
         assert(h);
 
+        if (!IN_SET(error,
+                    -ENOKEY,       /* Password incorrect */
+                    -EBADSLT,      /* Password incorrect and no token */
+                    -EREMOTEIO))   /* Recovery key incorrect */
+                return;
+
         r = user_record_bad_authentication(h->record);
         if (r < 0) {
                 log_warning_errno(r, "Failed to increase bad authentication counter, ignoring: %m");
@@ -680,8 +686,7 @@ static void home_fixate_finish(Home *h, int ret, UserRecord *hr) {
         secret = TAKE_PTR(h->secret); /* Take possession */
 
         if (ret < 0) {
-                if (ret == -ENOKEY)
-                        (void) home_count_bad_authentication(h, false);
+                (void) home_count_bad_authentication(h, ret, /* save= */ false);
 
                 (void) convert_worker_errno(h, ret, &error);
                 r = log_error_errno(ret, "Fixation failed: %m");
@@ -743,6 +748,27 @@ fail:
         home_set_state(h, HOME_UNFIXATED);
 }
 
+static bool error_is_bad_password(int ret) {
+        /* Tests for the various cases of bad passwords. We generally don't want to log so loudly about
+         * these, since everyone types in a bad password now and then. Moreover we usually try to start out
+         * with an empty set of passwords, so the first authentication will frequently fail, if not token is
+         * inserted. */
+
+        return IN_SET(ret,
+                      -ENOKEY,       /* Bad password, or insufficient */
+                      -EBADSLT,      /* Bad password, and no token */
+                      -EREMOTEIO,    /* Bad recovery key */
+                      -ENOANO,       /* PIN for security token needed */
+                      -ERFKILL,      /* "Protected Authentication Path" for token needed */
+                      -EMEDIUMTYPE,  /* Presence confirmation on token needed */
+                      -ENOCSI,       /* User verification on token needed */
+                      -ENOSTR,       /* Token action timeout */
+                      -EOWNERDEAD,   /* PIN locked of security token */
+                      -ENOLCK,       /* Bad PIN of security token */
+                      -ETOOMANYREFS, /* Bad PIN and few tries left */
+                      -EUCLEAN);     /* Bad PIN and one try left */
+}
+
 static void home_activate_finish(Home *h, int ret, UserRecord *hr) {
         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
         int r;
@@ -751,11 +777,11 @@ static void home_activate_finish(Home *h, int ret, UserRecord *hr) {
         assert(IN_SET(h->state, HOME_ACTIVATING, HOME_ACTIVATING_FOR_ACQUIRE));
 
         if (ret < 0) {
-                if (ret == -ENOKEY)
-                        home_count_bad_authentication(h, true);
+                (void) home_count_bad_authentication(h, ret, /* save= */ true);
 
                 (void) convert_worker_errno(h, ret, &error);
-                r = log_error_errno(ret, "Activation failed: %m");
+                r = log_full_errno(error_is_bad_password(ret) ? LOG_NOTICE : LOG_ERR,
+                                   ret, "Activation failed: %s", bus_error_message(&error, ret));
                 goto finish;
         }
 
@@ -912,11 +938,11 @@ static void home_change_finish(Home *h, int ret, UserRecord *hr) {
         assert(h);
 
         if (ret < 0) {
-                if (ret == -ENOKEY)
-                        (void) home_count_bad_authentication(h, true);
+                (void) home_count_bad_authentication(h, ret, /* save= */ true);
 
                 (void) convert_worker_errno(h, ret, &error);
-                r = log_error_errno(ret, "Change operation failed: %m");
+                r = log_full_errno(error_is_bad_password(ret) ? LOG_NOTICE : LOG_ERR,
+                                   ret, "Change operation failed: %s", bus_error_message(&error, ret));
                 goto finish;
         }
 
@@ -982,11 +1008,11 @@ static void home_unlocking_finish(Home *h, int ret, UserRecord *hr) {
         assert(IN_SET(h->state, HOME_UNLOCKING, HOME_UNLOCKING_FOR_ACQUIRE));
 
         if (ret < 0) {
-                if (ret == -ENOKEY)
-                        (void) home_count_bad_authentication(h, true);
+                (void) home_count_bad_authentication(h, ret, /* save= */ true);
 
                 (void) convert_worker_errno(h, ret, &error);
-                r = log_error_errno(ret, "Unlocking operation failed: %m");
+                r = log_full_errno(error_is_bad_password(ret) ? LOG_NOTICE : LOG_ERR,
+                                   ret, "Unlocking operation failed: %s", bus_error_message(&error, ret));
 
                 /* Revert to locked state */
                 home_set_state(h, HOME_LOCKED);
@@ -1018,11 +1044,11 @@ static void home_authenticating_finish(Home *h, int ret, UserRecord *hr) {
         assert(IN_SET(h->state, HOME_AUTHENTICATING, HOME_AUTHENTICATING_WHILE_ACTIVE, HOME_AUTHENTICATING_FOR_ACQUIRE));
 
         if (ret < 0) {
-                if (ret == -ENOKEY)
-                        (void) home_count_bad_authentication(h, true);
+                (void) home_count_bad_authentication(h, ret, /* save= */ true);
 
                 (void) convert_worker_errno(h, ret, &error);
-                r = log_error_errno(ret, "Authentication failed: %m");
+                r = log_full_errno(error_is_bad_password(ret) ? LOG_NOTICE : LOG_ERR,
+                                   ret, "Authentication failed: %s", bus_error_message(&error, ret));
                 goto finish;
         }
 
index b5dffb2c695af826743c5892f6b8101ee9490e98..dd17857a6ba8556938d28e9b2d7976265c38effe 100644 (file)
@@ -61,6 +61,53 @@ static int property_get_auto_login(
         return sd_bus_message_close_container(reply);
 }
 
+static int lookup_user_name(
+                Manager *m,
+                sd_bus_message *message,
+                const char *user_name,
+                sd_bus_error *error,
+                Home **ret) {
+
+        Home *h;
+        int r;
+
+        assert(m);
+        assert(message);
+        assert(user_name);
+        assert(ret);
+
+        if (isempty(user_name)) {
+                _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
+                uid_t uid;
+
+                /* If an empty user name is specified, then identify caller's EUID and find home by that. */
+
+                r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_EUID, &creds);
+                if (r < 0)
+                        return r;
+
+                r = sd_bus_creds_get_euid(creds, &uid);
+                if (r < 0)
+                        return r;
+
+                h = hashmap_get(m->homes_by_uid, UID_TO_PTR(uid));
+                if (!h)
+                        return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_HOME, "Client's UID " UID_FMT " not managed.", uid);
+
+        } else {
+
+                if (!valid_user_group_name(user_name, 0))
+                        return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "User name %s is not valid", user_name);
+
+                h = hashmap_get(m->homes_by_name, user_name);
+                if (!h)
+                        return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_HOME, "No home for user %s known", user_name);
+        }
+
+        *ret = h;
+        return 0;
+}
+
 static int method_get_home_by_name(
                 sd_bus_message *message,
                 void *userdata,
@@ -77,12 +124,10 @@ static int method_get_home_by_name(
         r = sd_bus_message_read(message, "s", &user_name);
         if (r < 0)
                 return r;
-        if (!valid_user_group_name(user_name, 0))
-                return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "User name %s is not valid", user_name);
 
-        h = hashmap_get(m->homes_by_name, user_name);
-        if (!h)
-                return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_HOME, "No home for user %s known", user_name);
+        r = lookup_user_name(m, message, user_name, error, &h);
+        if (r < 0)
+                return r;
 
         r = bus_home_path(h, &path);
         if (r < 0)
@@ -204,12 +249,10 @@ static int method_get_user_record_by_name(
         r = sd_bus_message_read(message, "s", &user_name);
         if (r < 0)
                 return r;
-        if (!valid_user_group_name(user_name, 0))
-                return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "User name %s is not valid", user_name);
 
-        h = hashmap_get(m->homes_by_name, user_name);
-        if (!h)
-                return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_HOME, "No home for user %s known", user_name);
+        r = lookup_user_name(m, message, user_name, error, &h);
+        if (r < 0)
+                return r;
 
         r = bus_home_get_record_json(h, message, &json, &incomplete);
         if (r < 0)
@@ -274,16 +317,17 @@ static int generic_home_method(
         Home *h;
         int r;
 
+        assert(m);
+        assert(message);
+        assert(handler);
+
         r = sd_bus_message_read(message, "s", &user_name);
         if (r < 0)
                 return r;
 
-        if (!valid_user_group_name(user_name, 0))
-                return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "User name %s is not valid", user_name);
-
-        h = hashmap_get(m->homes_by_name, user_name);
-        if (!h)
-                return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_HOME, "No home for user %s known", user_name);
+        r = lookup_user_name(m, message, user_name, error, &h);
+        if (r < 0)
+                return r;
 
         return handler(message, h, error);
 }
index c4525310fc20ff7944a644d6634edbf5d96a0bed..94b2ea5181a16b25e3872b8836728801142f209d 100644 (file)
@@ -268,7 +268,7 @@ Manager* manager_free(Manager *m) {
                 (void) home_wait_for_worker(h);
 
         m->bus = sd_bus_flush_close_unref(m->bus);
-        m->polkit_registry = bus_verify_polkit_async_registry_free(m->polkit_registry);
+        m->polkit_registry = hashmap_free(m->polkit_registry);
 
         m->device_monitor = sd_device_monitor_unref(m->device_monitor);
 
index ad0b69b021cbd4d087a3bcdf451d73a9242512f5..d737f6db08422fc573a6a7b474a68f1a7e487e85 100644 (file)
@@ -239,10 +239,11 @@ static int fscrypt_setup(
                 if (!e)
                         return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "xattr %s lacks ':' separator: %m", xa);
 
-                r = unbase64mem(value, e - value, &salt, &salt_size);
+                r = unbase64mem_full(value, e - value, /* secure = */ false, &salt, &salt_size);
                 if (r < 0)
                         return log_error_errno(r, "Failed to decode salt of %s: %m", xa);
-                r = unbase64mem(e+1, n - (e - value) - 1, &encrypted, &encrypted_size);
+
+                r = unbase64mem_full(e + 1, n - (e - value) - 1, /* secure = */ false, &encrypted, &encrypted_size);
                 if (r < 0)
                         return log_error_errno(r, "Failed to decode encrypted key of %s: %m", xa);
 
index de1fb93cc0d81cd386683417047e722f6fe1c758..5af1a686074433915c84f416df1ed2647e5af08d 100644 (file)
                        send_interface="org.freedesktop.home1.Manager"
                        send_member="LockAllHomes"/>
 
+                <allow send_destination="org.freedesktop.home1"
+                       send_interface="org.freedesktop.home1.Manager"
+                       send_member="DeactivateAllHomes"/>
+
                 <allow send_destination="org.freedesktop.home1"
                        send_interface="org.freedesktop.home1.Manager"
                        send_member="Rebalance"/>
index ba8d8f605456d69eb5c680873bdec7f16e3c97f2..7460aebc567ef770856535768cf3809b8b15cbe7 100644 (file)
 #include "user-record.h"
 #include "user-util.h"
 
+typedef enum AcquireHomeFlags {
+        ACQUIRE_MUST_AUTHENTICATE = 1 << 0,
+        ACQUIRE_PLEASE_SUSPEND    = 1 << 1,
+} AcquireHomeFlags;
+
 static int parse_argv(
                 pam_handle_t *handle,
                 int argc, const char **argv,
-                bool *please_suspend,
+                AcquireHomeFlags *flags,
                 bool *debug) {
 
         assert(argc >= 0);
@@ -38,8 +43,8 @@ static int parse_argv(
                         k = parse_boolean(v);
                         if (k < 0)
                                 pam_syslog(handle, LOG_WARNING, "Failed to parse suspend= argument, ignoring: %s", v);
-                        else if (please_suspend)
-                                *please_suspend = k;
+                        else if (flags)
+                                SET_FLAG(*flags, ACQUIRE_PLEASE_SUSPEND, k);
 
                 } else if (streq(argv[i], "debug")) {
                         if (debug)
@@ -62,7 +67,7 @@ static int parse_argv(
 
 static int parse_env(
                 pam_handle_t *handle,
-                bool *please_suspend) {
+                AcquireHomeFlags *flags) {
 
         const char *v;
         int r;
@@ -83,8 +88,8 @@ static int parse_env(
         r = parse_boolean(v);
         if (r < 0)
                 pam_syslog(handle, LOG_WARNING, "Failed to parse $SYSTEMD_HOME_SUSPEND argument, ignoring: %s", v);
-        else if (please_suspend)
-                *please_suspend = r;
+        else if (flags)
+                SET_FLAG(*flags, ACQUIRE_PLEASE_SUSPEND, r);
 
         return 0;
 }
@@ -275,21 +280,21 @@ static int handle_generic_user_record_error(
                 const sd_bus_error *error,
                 bool debug) {
 
+        int r;
+
         assert(user_name);
         assert(error);
 
-        int r;
-
         /* Logs about all errors, except for PAM_CONV_ERR, i.e. when requesting more info failed. */
 
         if (sd_bus_error_has_name(error, BUS_ERROR_HOME_ABSENT)) {
-                (void) pam_prompt(handle, PAM_ERROR_MSG, NULL,
+                (void) pam_prompt_graceful(handle, PAM_ERROR_MSG, NULL,
                                   _("Home of user %s is currently absent, please plug in the necessary storage device or backing file system."), user_name);
                 return pam_syslog_pam_error(handle, LOG_ERR, PAM_PERM_DENIED,
                                             "Failed to acquire home for user %s: %s", user_name, bus_error_message(error, ret));
 
         } else if (sd_bus_error_has_name(error, BUS_ERROR_AUTHENTICATION_LIMIT_HIT)) {
-                (void) pam_prompt(handle, PAM_ERROR_MSG, NULL, _("Too frequent login attempts for user %s, try again later."), user_name);
+                (void) pam_prompt_graceful(handle, PAM_ERROR_MSG, NULL, _("Too frequent login attempts for user %s, try again later."), user_name);
                 return pam_syslog_pam_error(handle, LOG_ERR, PAM_MAXTRIES,
                                             "Failed to acquire home for user %s: %s", user_name, bus_error_message(error, ret));
 
@@ -301,10 +306,10 @@ static int handle_generic_user_record_error(
                 /* This didn't work? Ask for an (additional?) password */
 
                 if (strv_isempty(secret->password))
-                        r = pam_prompt(handle, PAM_PROMPT_ECHO_OFF, &newp, _("Password: "));
+                        r = pam_prompt_graceful(handle, PAM_PROMPT_ECHO_OFF, &newp, _("Password: "));
                 else {
-                        (void) pam_prompt(handle, PAM_ERROR_MSG, NULL, _("Password incorrect or not sufficient for authentication of user %s."), user_name);
-                        r = pam_prompt(handle, PAM_PROMPT_ECHO_OFF, &newp, _("Sorry, try again: "));
+                        (void) pam_prompt_graceful(handle, PAM_ERROR_MSG, NULL, _("Password incorrect or not sufficient for authentication of user %s."), user_name);
+                        r = pam_prompt_graceful(handle, PAM_PROMPT_ECHO_OFF, &newp, _("Sorry, try again: "));
                 }
                 if (r != PAM_SUCCESS)
                         return PAM_CONV_ERR; /* no logging here */
@@ -326,10 +331,10 @@ static int handle_generic_user_record_error(
                 /* Hmm, homed asks for recovery key (because no regular password is defined maybe)? Provide it. */
 
                 if (strv_isempty(secret->password))
-                        r = pam_prompt(handle, PAM_PROMPT_ECHO_OFF, &newp, _("Recovery key: "));
+                        r = pam_prompt_graceful(handle, PAM_PROMPT_ECHO_OFF, &newp, _("Recovery key: "));
                 else {
-                        (void) pam_prompt(handle, PAM_ERROR_MSG, NULL, _("Password/recovery key incorrect or not sufficient for authentication of user %s."), user_name);
-                        r = pam_prompt(handle, PAM_PROMPT_ECHO_OFF, &newp, _("Sorry, reenter recovery key: "));
+                        (void) pam_prompt_graceful(handle, PAM_ERROR_MSG, NULL, _("Password/recovery key incorrect or not sufficient for authentication of user %s."), user_name);
+                        r = pam_prompt_graceful(handle, PAM_PROMPT_ECHO_OFF, &newp, _("Sorry, reenter recovery key: "));
                 }
                 if (r != PAM_SUCCESS)
                         return PAM_CONV_ERR; /* no logging here */
@@ -349,11 +354,11 @@ static int handle_generic_user_record_error(
                 assert(secret);
 
                 if (strv_isempty(secret->password)) {
-                        (void) pam_prompt(handle, PAM_ERROR_MSG, NULL, _("Security token of user %s not inserted."), user_name);
-                        r = pam_prompt(handle, PAM_PROMPT_ECHO_OFF, &newp, _("Try again with password: "));
+                        (void) pam_prompt_graceful(handle, PAM_ERROR_MSG, NULL, _("Security token of user %s not inserted."), user_name);
+                        r = pam_prompt_graceful(handle, PAM_PROMPT_ECHO_OFF, &newp, _("Try again with password: "));
                 } else {
-                        (void) pam_prompt(handle, PAM_ERROR_MSG, NULL, _("Password incorrect or not sufficient, and configured security token of user %s not inserted."), user_name);
-                        r = pam_prompt(handle, PAM_PROMPT_ECHO_OFF, &newp, _("Try again with password: "));
+                        (void) pam_prompt_graceful(handle, PAM_ERROR_MSG, NULL, _("Password incorrect or not sufficient, and configured security token of user %s not inserted."), user_name);
+                        r = pam_prompt_graceful(handle, PAM_PROMPT_ECHO_OFF, &newp, _("Try again with password: "));
                 }
                 if (r != PAM_SUCCESS)
                         return PAM_CONV_ERR; /* no logging here */
@@ -363,7 +368,6 @@ static int handle_generic_user_record_error(
                         return PAM_AUTHTOK_ERR;
                 }
 
-
                 r = user_record_set_password(secret, STRV_MAKE(newp), true);
                 if (r < 0)
                         return pam_syslog_errno(handle, LOG_ERR, r, "Failed to store password: %m");
@@ -373,7 +377,7 @@ static int handle_generic_user_record_error(
 
                 assert(secret);
 
-                r = pam_prompt(handle, PAM_PROMPT_ECHO_OFF, &newp, _("Security token PIN: "));
+                r = pam_prompt_graceful(handle, PAM_PROMPT_ECHO_OFF, &newp, _("Security token PIN: "));
                 if (r != PAM_SUCCESS)
                         return PAM_CONV_ERR; /* no logging here */
 
@@ -390,7 +394,7 @@ static int handle_generic_user_record_error(
 
                 assert(secret);
 
-                (void) pam_prompt(handle, PAM_ERROR_MSG, NULL, _("Please authenticate physically on security token of user %s."), user_name);
+                (void) pam_prompt_graceful(handle, PAM_ERROR_MSG, NULL, _("Please authenticate physically on security token of user %s."), user_name);
 
                 r = user_record_set_pkcs11_protected_authentication_path_permitted(secret, true);
                 if (r < 0)
@@ -401,7 +405,7 @@ static int handle_generic_user_record_error(
 
                 assert(secret);
 
-                (void) pam_prompt(handle, PAM_ERROR_MSG, NULL, _("Please confirm presence on security token of user %s."), user_name);
+                (void) pam_prompt_graceful(handle, PAM_ERROR_MSG, NULL, _("Please confirm presence on security token of user %s."), user_name);
 
                 r = user_record_set_fido2_user_presence_permitted(secret, true);
                 if (r < 0)
@@ -412,7 +416,7 @@ static int handle_generic_user_record_error(
 
                 assert(secret);
 
-                (void) pam_prompt(handle, PAM_ERROR_MSG, NULL, _("Please verify user on security token of user %s."), user_name);
+                (void) pam_prompt_graceful(handle, PAM_ERROR_MSG, NULL, _("Please verify user on security token of user %s."), user_name);
 
                 r = user_record_set_fido2_user_verification_permitted(secret, true);
                 if (r < 0)
@@ -421,7 +425,7 @@ static int handle_generic_user_record_error(
 
         } else if (sd_bus_error_has_name(error, BUS_ERROR_TOKEN_PIN_LOCKED)) {
 
-                (void) pam_prompt(handle, PAM_ERROR_MSG, NULL, _("Security token PIN is locked, please unlock it first. (Hint: Removal and re-insertion might suffice.)"));
+                (void) pam_prompt_graceful(handle, PAM_ERROR_MSG, NULL, _("Security token PIN is locked, please unlock it first. (Hint: Removal and re-insertion might suffice.)"));
                 return PAM_SERVICE_ERR;
 
         } else if (sd_bus_error_has_name(error, BUS_ERROR_TOKEN_BAD_PIN)) {
@@ -429,8 +433,8 @@ static int handle_generic_user_record_error(
 
                 assert(secret);
 
-                (void) pam_prompt(handle, PAM_ERROR_MSG, NULL, _("Security token PIN incorrect for user %s."), user_name);
-                r = pam_prompt(handle, PAM_PROMPT_ECHO_OFF, &newp, _("Sorry, retry security token PIN: "));
+                (void) pam_prompt_graceful(handle, PAM_ERROR_MSG, NULL, _("Security token PIN incorrect for user %s."), user_name);
+                r = pam_prompt_graceful(handle, PAM_PROMPT_ECHO_OFF, &newp, _("Sorry, retry security token PIN: "));
                 if (r != PAM_SUCCESS)
                         return PAM_CONV_ERR; /* no logging here */
 
@@ -448,8 +452,8 @@ static int handle_generic_user_record_error(
 
                 assert(secret);
 
-                (void) pam_prompt(handle, PAM_ERROR_MSG, NULL, _("Security token PIN of user %s incorrect (only a few tries left!)"), user_name);
-                r = pam_prompt(handle, PAM_PROMPT_ECHO_OFF, &newp, _("Sorry, retry security token PIN: "));
+                (void) pam_prompt_graceful(handle, PAM_ERROR_MSG, NULL, _("Security token PIN of user %s incorrect (only a few tries left!)"), user_name);
+                r = pam_prompt_graceful(handle, PAM_PROMPT_ECHO_OFF, &newp, _("Sorry, retry security token PIN: "));
                 if (r != PAM_SUCCESS)
                         return PAM_CONV_ERR; /* no logging here */
 
@@ -467,8 +471,8 @@ static int handle_generic_user_record_error(
 
                 assert(secret);
 
-                (void) pam_prompt(handle, PAM_ERROR_MSG, NULL, _("Security token PIN of user %s incorrect (only one try left!)"), user_name);
-                r = pam_prompt(handle, PAM_PROMPT_ECHO_OFF, &newp, _("Sorry, retry security token PIN: "));
+                (void) pam_prompt_graceful(handle, PAM_ERROR_MSG, NULL, _("Security token PIN of user %s incorrect (only one try left!)"), user_name);
+                r = pam_prompt_graceful(handle, PAM_PROMPT_ECHO_OFF, &newp, _("Sorry, retry security token PIN: "));
                 if (r != PAM_SUCCESS)
                         return PAM_CONV_ERR; /* no logging here */
 
@@ -490,13 +494,12 @@ static int handle_generic_user_record_error(
 
 static int acquire_home(
                 pam_handle_t *handle,
-                bool please_authenticate,
-                bool please_suspend,
+                AcquireHomeFlags flags,
                 bool debug,
                 PamBusData **bus_data) {
 
         _cleanup_(user_record_unrefp) UserRecord *ur = NULL, *secret = NULL;
-        bool do_auth = please_authenticate, home_not_active = false, home_locked = false;
+        bool do_auth = FLAGS_SET(flags, ACQUIRE_MUST_AUTHENTICATE), home_not_active = false, home_locked = false;
         _cleanup_(sd_bus_unrefp) sd_bus *bus = NULL;
         _cleanup_close_ int acquired_fd = -EBADF;
         _cleanup_free_ char *fd_field = NULL;
@@ -590,7 +593,7 @@ static int acquire_home(
                                 return pam_bus_log_create_error(handle, r);
                 }
 
-                r = sd_bus_message_append(m, "b", please_suspend);
+                r = sd_bus_message_append(m, "b", FLAGS_SET(flags, ACQUIRE_PLEASE_SUSPEND));
                 if (r < 0)
                         return pam_bus_log_create_error(handle, r);
 
@@ -613,19 +616,18 @@ static int acquire_home(
                                          * failure. */
 
                                         if (home_not_active)
-                                                (void) pam_prompt(handle, PAM_ERROR_MSG, NULL, _("Home of user %s is currently not active, please log in locally first."), ur->user_name);
+                                                (void) pam_prompt_graceful(handle, PAM_ERROR_MSG, NULL, _("Home of user %s is currently not active, please log in locally first."), ur->user_name);
                                         if (home_locked)
-                                                (void) pam_prompt(handle, PAM_ERROR_MSG, NULL, _("Home of user %s is currently locked, please unlock locally first."), ur->user_name);
+                                                (void) pam_prompt_graceful(handle, PAM_ERROR_MSG, NULL, _("Home of user %s is currently locked, please unlock locally first."), ur->user_name);
 
-                                        if (please_authenticate || debug)
-                                                pam_syslog(handle, please_authenticate ? LOG_ERR : LOG_DEBUG, "Failed to prompt for password/prompt.");
+                                        if (FLAGS_SET(flags, ACQUIRE_MUST_AUTHENTICATE) || debug)
+                                                pam_syslog(handle, FLAGS_SET(flags, ACQUIRE_MUST_AUTHENTICATE) ? LOG_ERR : LOG_DEBUG, "Failed to prompt for password/prompt.");
 
                                         return home_not_active || home_locked ? PAM_PERM_DENIED : PAM_CONV_ERR;
                                 }
                                 if (r != PAM_SUCCESS)
                                         return r;
                         }
-
                 } else {
                         int fd;
 
@@ -641,7 +643,7 @@ static int acquire_home(
                 }
 
                 if (++n_attempts >= 5) {
-                        (void) pam_prompt(handle, PAM_ERROR_MSG, NULL,
+                        (void) pam_prompt_graceful(handle, PAM_ERROR_MSG, NULL,
                                           _("Too many unsuccessful login attempts for user %s, refusing."), ur->user_name);
                         return pam_syslog_pam_error(handle, LOG_ERR, PAM_MAXTRIES,
                                                     "Failed to acquire home for user %s: %s", ur->user_name, bus_error_message(&error, r));
@@ -652,7 +654,7 @@ static int acquire_home(
         }
 
         /* Later PAM modules may need the auth token, but only during pam_authenticate. */
-        if (please_authenticate && !strv_isempty(secret->password)) {
+        if (FLAGS_SET(flags, ACQUIRE_MUST_AUTHENTICATE) && !strv_isempty(secret->password)) {
                 r = pam_set_item(handle, PAM_AUTHTOK, *secret->password);
                 if (r != PAM_SUCCESS)
                         return pam_syslog_pam_error(handle, LOG_ERR, r, "Failed to set PAM auth token: @PAMERR@");
@@ -703,53 +705,55 @@ static int release_home_fd(pam_handle_t *handle, const char *username) {
 
 _public_ PAM_EXTERN int pam_sm_authenticate(
                 pam_handle_t *handle,
-                int flags,
+                int sm_flags,
                 int argc, const char **argv) {
 
-        bool debug = false, suspend_please = false;
+        AcquireHomeFlags flags = 0;
+        bool debug = false;
 
-        if (parse_env(handle, &suspend_please) < 0)
+        if (parse_env(handle, &flags) < 0)
                 return PAM_AUTH_ERR;
 
         if (parse_argv(handle,
                        argc, argv,
-                       &suspend_please,
+                       &flags,
                        &debug) < 0)
                 return PAM_AUTH_ERR;
 
         pam_debug_syslog(handle, debug, "pam-systemd-homed authenticating");
 
-        return acquire_home(handle, /* please_authenticate= */ true, suspend_please, debug, NULL);
+        return acquire_home(handle, ACQUIRE_MUST_AUTHENTICATE|flags, debug, /* bus_data= */ NULL);
 }
 
-_public_ PAM_EXTERN int pam_sm_setcred(pam_handle_t *pamh, int flags, int argc, const char **argv) {
+_public_ PAM_EXTERN int pam_sm_setcred(pam_handle_t *pamh, int sm_flags, int argc, const char **argv) {
         return PAM_SUCCESS;
 }
 
 _public_ PAM_EXTERN int pam_sm_open_session(
                 pam_handle_t *handle,
-                int flags,
+                int sm_flags,
                 int argc, const char **argv) {
 
         /* Let's release the D-Bus connection once this function exits, after all the session might live
          * quite a long time, and we are not going to process the bus connection in that time, so let's
          * better close before the daemon kicks us off because we are not processing anything. */
         _cleanup_(pam_bus_data_disconnectp) PamBusData *d = NULL;
-        bool debug = false, suspend_please = false;
+        AcquireHomeFlags flags = 0;
+        bool debug = false;
         int r;
 
-        if (parse_env(handle, &suspend_please) < 0)
+        if (parse_env(handle, &flags) < 0)
                 return PAM_SESSION_ERR;
 
         if (parse_argv(handle,
                        argc, argv,
-                       &suspend_please,
+                       &flags,
                        &debug) < 0)
                 return PAM_SESSION_ERR;
 
         pam_debug_syslog(handle, debug, "pam-systemd-homed session start");
 
-        r = acquire_home(handle, /* please_authenticate = */ false, suspend_please, debug, &d);
+        r = acquire_home(handle, flags, debug, &d);
         if (r == PAM_USER_UNKNOWN) /* Not managed by us? Don't complain. */
                 return PAM_SUCCESS;
         if (r != PAM_SUCCESS)
@@ -760,7 +764,7 @@ _public_ PAM_EXTERN int pam_sm_open_session(
                 return pam_syslog_pam_error(handle, LOG_ERR, r,
                                             "Failed to set PAM environment variable $SYSTEMD_HOME: @PAMERR@");
 
-        r = pam_putenv(handle, suspend_please ? "SYSTEMD_HOME_SUSPEND=1" : "SYSTEMD_HOME_SUSPEND=0");
+        r = pam_putenv(handle, FLAGS_SET(flags, ACQUIRE_PLEASE_SUSPEND) ? "SYSTEMD_HOME_SUSPEND=1" : "SYSTEMD_HOME_SUSPEND=0");
         if (r != PAM_SUCCESS)
                 return pam_syslog_pam_error(handle, LOG_ERR, r,
                                             "Failed to set PAM environment variable $SYSTEMD_HOME_SUSPEND: @PAMERR@");
@@ -770,7 +774,7 @@ _public_ PAM_EXTERN int pam_sm_open_session(
 
 _public_ PAM_EXTERN int pam_sm_close_session(
                 pam_handle_t *handle,
-                int flags,
+                int sm_flags,
                 int argc, const char **argv) {
 
         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
@@ -829,27 +833,28 @@ _public_ PAM_EXTERN int pam_sm_close_session(
 
 _public_ PAM_EXTERN int pam_sm_acct_mgmt(
                 pam_handle_t *handle,
-                int flags,
+                int sm_flags,
                 int argc,
                 const char **argv) {
 
         _cleanup_(user_record_unrefp) UserRecord *ur = NULL;
-        bool debug = false, please_suspend = false;
+        AcquireHomeFlags flags = 0;
+        bool debug = false;
         usec_t t;
         int r;
 
-        if (parse_env(handle, &please_suspend) < 0)
+        if (parse_env(handle, &flags) < 0)
                 return PAM_AUTH_ERR;
 
         if (parse_argv(handle,
                        argc, argv,
-                       &please_suspend,
+                       &flags,
                        &debug) < 0)
                 return PAM_AUTH_ERR;
 
         pam_debug_syslog(handle, debug, "pam-systemd-homed account management");
 
-        r = acquire_home(handle, /* please_authenticate = */ false, please_suspend, debug, NULL);
+        r = acquire_home(handle, flags, debug, NULL);
         if (r != PAM_SUCCESS)
                 return r;
 
@@ -865,20 +870,20 @@ _public_ PAM_EXTERN int pam_sm_acct_mgmt(
                 break;
 
         case -ENOLCK:
-                (void) pam_prompt(handle, PAM_ERROR_MSG, NULL, _("User record is blocked, prohibiting access."));
+                (void) pam_prompt_graceful(handle, PAM_ERROR_MSG, NULL, _("User record is blocked, prohibiting access."));
                 return PAM_ACCT_EXPIRED;
 
         case -EL2HLT:
-                (void) pam_prompt(handle, PAM_ERROR_MSG, NULL, _("User record is not valid yet, prohibiting access."));
+                (void) pam_prompt_graceful(handle, PAM_ERROR_MSG, NULL, _("User record is not valid yet, prohibiting access."));
                 return PAM_ACCT_EXPIRED;
 
         case -EL3HLT:
-                (void) pam_prompt(handle, PAM_ERROR_MSG, NULL, _("User record is not valid anymore, prohibiting access."));
+                (void) pam_prompt_graceful(handle, PAM_ERROR_MSG, NULL, _("User record is not valid anymore, prohibiting access."));
                 return PAM_ACCT_EXPIRED;
 
         default:
                 if (r < 0) {
-                        (void) pam_prompt(handle, PAM_ERROR_MSG, NULL, _("User record not valid, prohibiting access."));
+                        (void) pam_prompt_graceful(handle, PAM_ERROR_MSG, NULL, _("User record not valid, prohibiting access."));
                         return PAM_ACCT_EXPIRED;
                 }
 
@@ -890,7 +895,7 @@ _public_ PAM_EXTERN int pam_sm_acct_mgmt(
                 usec_t n = now(CLOCK_REALTIME);
 
                 if (t > n) {
-                        (void) pam_prompt(handle, PAM_ERROR_MSG, NULL, _("Too many logins, try again in %s."),
+                        (void) pam_prompt_graceful(handle, PAM_ERROR_MSG, NULL, _("Too many logins, try again in %s."),
                                           FORMAT_TIMESPAN(t - n, USEC_PER_SEC));
 
                         return PAM_MAXTRIES;
@@ -901,21 +906,21 @@ _public_ PAM_EXTERN int pam_sm_acct_mgmt(
         switch (r) {
 
         case -EKEYREVOKED:
-                (void) pam_prompt(handle, PAM_ERROR_MSG, NULL, _("Password change required."));
+                (void) pam_prompt_graceful(handle, PAM_ERROR_MSG, NULL, _("Password change required."));
                 return PAM_NEW_AUTHTOK_REQD;
 
         case -EOWNERDEAD:
-                (void) pam_prompt(handle, PAM_ERROR_MSG, NULL, _("Password expired, change required."));
+                (void) pam_prompt_graceful(handle, PAM_ERROR_MSG, NULL, _("Password expired, change required."));
                 return PAM_NEW_AUTHTOK_REQD;
 
         /* Strictly speaking this is only about password expiration, and we might want to allow
          * authentication via PKCS#11 or so, but let's ignore this fine distinction for now. */
         case -EKEYREJECTED:
-                (void) pam_prompt(handle, PAM_ERROR_MSG, NULL, _("Password is expired, but can't change, refusing login."));
+                (void) pam_prompt_graceful(handle, PAM_ERROR_MSG, NULL, _("Password is expired, but can't change, refusing login."));
                 return PAM_AUTHTOK_EXPIRED;
 
         case -EKEYEXPIRED:
-                (void) pam_prompt(handle, PAM_ERROR_MSG, NULL, _("Password will expire soon, please change."));
+                (void) pam_prompt_graceful(handle, PAM_ERROR_MSG, NULL, _("Password will expire soon, please change."));
                 break;
 
         case -ESTALE:
@@ -929,7 +934,7 @@ _public_ PAM_EXTERN int pam_sm_acct_mgmt(
 
         default:
                 if (r < 0) {
-                        (void) pam_prompt(handle, PAM_ERROR_MSG, NULL, _("User record not valid, prohibiting access."));
+                        (void) pam_prompt_graceful(handle, PAM_ERROR_MSG, NULL, _("User record not valid, prohibiting access."));
                         return PAM_AUTHTOK_EXPIRED;
                 }
 
@@ -941,7 +946,7 @@ _public_ PAM_EXTERN int pam_sm_acct_mgmt(
 
 _public_ PAM_EXTERN int pam_sm_chauthtok(
                 pam_handle_t *handle,
-                int flags,
+                int sm_flags,
                 int argc,
                 const char **argv) {
 
@@ -969,13 +974,12 @@ _public_ PAM_EXTERN int pam_sm_chauthtok(
                 return r;
 
         /* Start with cached credentials */
-        r = pam_get_item(handle, PAM_OLDAUTHTOK, (const void**) &old_password);
-        if (!IN_SET(r, PAM_BAD_ITEM, PAM_SUCCESS))
-                return pam_syslog_pam_error(handle, LOG_ERR, r, "Failed to get old password: @PAMERR@");
-
-        r = pam_get_item(handle, PAM_AUTHTOK, (const void**) &new_password);
-        if (!IN_SET(r, PAM_BAD_ITEM, PAM_SUCCESS))
-                return pam_syslog_pam_error(handle, LOG_ERR, r, "Failed to get cached password: @PAMERR@");
+        r = pam_get_item_many(
+                        handle,
+                        PAM_OLDAUTHTOK, &old_password,
+                        PAM_AUTHTOK, &new_password);
+        if (r != PAM_SUCCESS)
+                return pam_syslog_pam_error(handle, LOG_ERR, r, "Failed to get cached passwords: @PAMERR@");
 
         if (isempty(new_password)) {
                 /* No, it's not cached, then let's ask for the password and its verification, and cache
@@ -1000,7 +1004,7 @@ _public_ PAM_EXTERN int pam_sm_chauthtok(
         }
 
         /* Now everything is cached and checked, let's exit from the preliminary check */
-        if (FLAGS_SET(flags, PAM_PRELIM_CHECK))
+        if (FLAGS_SET(sm_flags, PAM_PRELIM_CHECK))
                 return PAM_SUCCESS;
 
         old_secret = user_record_new();
index 14fc16090959549552469a90a59a0349e818bed0..4b92ac2b230349adc343923ca5923b23108bdebf 100644 (file)
@@ -24,6 +24,7 @@
 #include "main-func.h"
 #include "parse-argument.h"
 #include "pretty-print.h"
+#include "socket-util.h"
 #include "spawn-polkit-agent.h"
 #include "terminal-util.h"
 #include "verbs.h"
@@ -58,6 +59,7 @@ typedef struct StatusInfo {
         usec_t firmware_date;
         sd_id128_t machine_id;
         sd_id128_t boot_id;
+        uint32_t vsock_cid;
 } StatusInfo;
 
 static const char* chassis_string_to_glyph(const char *chassis) {
@@ -191,6 +193,14 @@ static int print_status_info(StatusInfo *i) {
                         return table_log_add_error(r);
         }
 
+        if (i->vsock_cid != VMADDR_CID_ANY) {
+                r = table_add_many(table,
+                                   TABLE_FIELD, "AF_VSOCK CID",
+                                   TABLE_UINT32, i->vsock_cid);
+                if (r < 0)
+                        return table_log_add_error(r);
+        }
+
         if (!isempty(i->virtualization)) {
                 r = table_add_many(table,
                                    TABLE_FIELD, "Virtualization",
@@ -332,7 +342,9 @@ static int get_one_name(sd_bus *bus, const char* attr, char **ret) {
 }
 
 static int show_all_names(sd_bus *bus) {
-        StatusInfo info = {};
+        StatusInfo info = {
+                .vsock_cid = VMADDR_CID_ANY,
+        };
 
         static const struct bus_properties_map hostname_map[]  = {
                 { "Hostname",                  "s",  NULL,          offsetof(StatusInfo, hostname)         },
@@ -354,6 +366,7 @@ static int show_all_names(sd_bus *bus) {
                 { "FirmwareDate",              "t",  NULL,          offsetof(StatusInfo, firmware_date)    },
                 { "MachineID",                 "ay", bus_map_id128, offsetof(StatusInfo, machine_id)       },
                 { "BootID",                    "ay", bus_map_id128, offsetof(StatusInfo, boot_id)          },
+                { "VSockCID",                  "u",  NULL,          offsetof(StatusInfo, vsock_cid)        },
                 {}
         }, manager_map[] = {
                 { "Virtualization",            "s",  NULL,          offsetof(StatusInfo, virtualization)   },
index 893eb4cc0f15be02265cdc8f037419fdea805d62..0e06a16a24198a1d7f5234a004319d3992df8bda 100644 (file)
@@ -6,12 +6,15 @@
 #include <sys/types.h>
 #include <unistd.h>
 
+#include "sd-device.h"
+
 #include "alloc-util.h"
 #include "bus-common-errors.h"
 #include "bus-get-properties.h"
 #include "bus-log-control-api.h"
 #include "bus-polkit.h"
 #include "constants.h"
+#include "daemon-util.h"
 #include "env-file-label.h"
 #include "env-file.h"
 #include "env-util.h"
 #include "os-util.h"
 #include "parse-util.h"
 #include "path-util.h"
-#include "sd-device.h"
 #include "selinux-util.h"
 #include "service-util.h"
 #include "signal-util.h"
+#include "socket-util.h"
 #include "stat-util.h"
 #include "string-table.h"
 #include "strv.h"
 #include "user-util.h"
+#include "varlink-io.systemd.Hostname.h"
 #include "virt.h"
 
 #define VALID_DEPLOYMENT_CHARS (DIGITS LETTERS "-.:")
@@ -73,6 +77,9 @@ typedef struct Context {
         struct stat etc_os_release_stat;
         struct stat etc_machine_info_stat;
 
+        sd_event *event;
+        sd_bus *bus;
+        VarlinkServer *varlink_server;
         Hashmap *polkit_registry;
 } Context;
 
@@ -91,7 +98,10 @@ static void context_destroy(Context *c) {
         assert(c);
 
         context_reset(c, UINT64_MAX);
-        bus_verify_polkit_async_registry_free(c->polkit_registry);
+        hashmap_free(c->polkit_registry);
+        sd_event_unref(c->event);
+        sd_bus_flush_close_unref(c->bus);
+        varlink_server_unref(c->varlink_server);
 }
 
 static void context_read_etc_hostname(Context *c) {
@@ -1033,6 +1043,22 @@ static int property_get_boot_id(
         return bus_property_get_id128(bus, path, interface, property, reply, &id, error);
 }
 
+static int property_get_vsock_cid(
+                sd_bus *bus,
+                const char *path,
+                const char *interface,
+                const char *property,
+                sd_bus_message *reply,
+                void *userdata,
+                sd_bus_error *error) {
+
+        unsigned local_cid = VMADDR_CID_ANY;
+
+        (void) vsock_get_local_cid(&local_cid);
+
+        return sd_bus_message_append(reply, "u", (uint32_t) local_cid);
+}
+
 static int method_set_hostname(sd_bus_message *m, void *userdata, sd_bus_error *error) {
         Context *c = ASSERT_PTR(userdata);
         const char *name;
@@ -1325,33 +1351,19 @@ static int method_get_hardware_serial(sd_bus_message *m, void *userdata, sd_bus_
         return sd_bus_send(NULL, reply, NULL);
 }
 
-static int method_describe(sd_bus_message *m, void *userdata, sd_bus_error *error) {
-        _cleanup_free_ char *hn = NULL, *dhn = NULL, *in = NULL, *text = NULL,
+static int build_describe_response(Context *c, bool privileged, JsonVariant **ret) {
+        _cleanup_free_ char *hn = NULL, *dhn = NULL, *in = NULL,
                 *chassis = NULL, *vendor = NULL, *model = NULL, *serial = NULL, *firmware_version = NULL,
                 *firmware_vendor = NULL;
         usec_t firmware_date = USEC_INFINITY, eol = USEC_INFINITY;
-        _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
         _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
         sd_id128_t machine_id, boot_id, product_uuid = SD_ID128_NULL;
-        Context *c = ASSERT_PTR(userdata);
-        bool privileged;
+        unsigned local_cid = VMADDR_CID_ANY;
         struct utsname u;
         int r;
 
-        assert(m);
-
-        r = bus_verify_polkit_async(
-                        m,
-                        "org.freedesktop.hostname1.get-description",
-                        /* details= */ NULL,
-                        &c->polkit_registry,
-                        error);
-        if (r == 0)
-                return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
-
-        /* We ignore all authentication errors here, since most data is unprivileged, the one exception being
-         * the product ID which we'll check explicitly. */
-        privileged = r > 0;
+        assert(c);
+        assert(ret);
 
         context_read_etc_hostname(c);
         context_read_machine_info(c);
@@ -1404,6 +1416,8 @@ static int method_describe(sd_bus_message *m, void *userdata, sd_bus_error *erro
         if (r < 0)
                 return log_error_errno(r, "Failed to get boot ID: %m");
 
+        (void) vsock_get_local_cid(&local_cid);
+
         r = json_build(&v, JSON_BUILD_OBJECT(
                                        JSON_BUILD_PAIR("Hostname", JSON_BUILD_STRING(hn)),
                                        JSON_BUILD_PAIR("StaticHostname", JSON_BUILD_STRING(c->data[PROP_STATIC_HOSTNAME])),
@@ -1430,11 +1444,43 @@ static int method_describe(sd_bus_message *m, void *userdata, sd_bus_error *erro
                                        JSON_BUILD_PAIR_ID128("MachineID", machine_id),
                                        JSON_BUILD_PAIR_ID128("BootID", boot_id),
                                        JSON_BUILD_PAIR_CONDITION(!sd_id128_is_null(product_uuid), "ProductUUID", JSON_BUILD_ID128(product_uuid)),
-                                       JSON_BUILD_PAIR_CONDITION(sd_id128_is_null(product_uuid), "ProductUUID", JSON_BUILD_NULL)));
-
+                                       JSON_BUILD_PAIR_CONDITION(sd_id128_is_null(product_uuid), "ProductUUID", JSON_BUILD_NULL),
+                                       JSON_BUILD_PAIR_CONDITION(local_cid != VMADDR_CID_ANY, "VSockCID", JSON_BUILD_UNSIGNED(local_cid)),
+                                       JSON_BUILD_PAIR_CONDITION(local_cid == VMADDR_CID_ANY, "VSockCID", JSON_BUILD_NULL)));
         if (r < 0)
                 return log_error_errno(r, "Failed to build JSON data: %m");
 
+        *ret = TAKE_PTR(v);
+        return 0;
+}
+
+static int method_describe(sd_bus_message *m, void *userdata, sd_bus_error *error) {
+        _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
+        _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
+        Context *c = ASSERT_PTR(userdata);
+        _cleanup_free_ char *text = NULL;
+        bool privileged;
+        int r;
+
+        assert(m);
+
+        r = bus_verify_polkit_async(
+                        m,
+                        "org.freedesktop.hostname1.get-description",
+                        /* details= */ NULL,
+                        &c->polkit_registry,
+                        error);
+        if (r == 0)
+                return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
+
+        /* We ignore all authentication errors here, since most data is unprivileged, the one exception being
+         * the product ID which we'll check explicitly. */
+        privileged = r > 0;
+
+        r = build_describe_response(c, privileged, &v);
+        if (r < 0)
+                return r;
+
         r = json_variant_format(v, 0, &text);
         if (r < 0)
                 return log_error_errno(r, "Failed to format JSON data: %m");
@@ -1475,6 +1521,7 @@ static const sd_bus_vtable hostname_vtable[] = {
         SD_BUS_PROPERTY("FirmwareDate", "t", property_get_firmware_date, 0, SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("MachineID", "ay", property_get_machine_id, 0, SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("BootID", "ay", property_get_boot_id, 0, SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("VSockCID", "u", property_get_vsock_cid, 0, SD_BUS_VTABLE_PROPERTY_CONST),
 
         SD_BUS_METHOD_WITH_ARGS("SetHostname",
                                 SD_BUS_ARGS("s", hostname, "b", interactive),
@@ -1536,35 +1583,114 @@ static const BusObjectImplementation manager_object = {
         .vtables = BUS_VTABLES(hostname_vtable),
 };
 
-static int connect_bus(Context *c, sd_event *event, sd_bus **ret) {
-        _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
+static int connect_bus(Context *c) {
         int r;
 
         assert(c);
-        assert(event);
-        assert(ret);
+        assert(c->event);
+        assert(!c->bus);
 
-        r = sd_bus_default_system(&bus);
+        r = sd_bus_default_system(&c->bus);
         if (r < 0)
                 return log_error_errno(r, "Failed to get system bus connection: %m");
 
-        r = bus_add_implementation(bus, &manager_object, c);
+        r = bus_add_implementation(c->bus, &manager_object, c);
         if (r < 0)
                 return r;
 
-        r = bus_log_control_api_register(bus);
+        r = bus_log_control_api_register(c->bus);
         if (r < 0)
                 return r;
 
-        r = sd_bus_request_name_async(bus, NULL, "org.freedesktop.hostname1", 0, NULL, NULL);
+        r = sd_bus_request_name_async(c->bus, NULL, "org.freedesktop.hostname1", 0, NULL, NULL);
         if (r < 0)
                 return log_error_errno(r, "Failed to request name: %m");
 
-        r = sd_bus_attach_event(bus, event, 0);
+        r = sd_bus_attach_event(c->bus, c->event, 0);
         if (r < 0)
                 return log_error_errno(r, "Failed to attach bus to event loop: %m");
 
-        *ret = TAKE_PTR(bus);
+        return 0;
+}
+
+static int vl_method_describe(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata) {
+        static const JsonDispatch dispatch_table[] = {
+                VARLINK_DISPATCH_POLKIT_FIELD,
+                {}
+        };
+
+        Context *c = ASSERT_PTR(userdata);
+        bool privileged;
+        int r;
+
+        assert(link);
+        assert(parameters);
+
+        r = varlink_dispatch(link, parameters, dispatch_table, /* userdata= */ NULL);
+        if (r != 0)
+                return r;
+
+        r = varlink_verify_polkit_async(
+                        link,
+                        c->bus,
+                        "org.freedesktop.hostname1.get-hardware-serial",
+                        /* details= */ NULL,
+                        /* good_user= */ UID_INVALID,
+                        &c->polkit_registry);
+        if (r == 0)
+                return 0; /* No authorization for now, but the async polkit stuff will call us again when it has it */
+
+        /* We ignore all authentication errors here, since most data is unprivileged, the one exception being
+         * the product ID which we'll check explicitly. */
+        privileged = r > 0;
+
+        if (json_variant_elements(parameters) > 0)
+                return varlink_error_invalid_parameter(link, parameters);
+
+        _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
+        r = build_describe_response(c, privileged, &v);
+        if (r < 0)
+                return r;
+
+        return varlink_reply(link, v);
+}
+
+static int connect_varlink(Context *c) {
+        int r;
+
+        assert(c);
+        assert(c->event);
+        assert(!c->varlink_server);
+
+        r = varlink_server_new(&c->varlink_server, VARLINK_SERVER_ACCOUNT_UID|VARLINK_SERVER_INHERIT_USERDATA);
+        if (r < 0)
+                return log_error_errno(r, "Failed to allocate Varlink server: %m");
+
+        varlink_server_set_userdata(c->varlink_server, c);
+
+        r = varlink_server_add_interface(c->varlink_server, &vl_interface_io_systemd_Hostname);
+        if (r < 0)
+                return log_error_errno(r, "Failed to add Hostname interface to varlink server: %m");
+
+        r = varlink_server_bind_method_many(
+                        c->varlink_server,
+                        "io.systemd.Hostname.Describe", vl_method_describe);
+        if (r < 0)
+                return log_error_errno(r, "Failed to bind Varlink method calls: %m");
+
+        r = varlink_server_attach_event(c->varlink_server, c->event, SD_EVENT_PRIORITY_NORMAL);
+        if (r < 0)
+                return log_error_errno(r, "Failed to attach Varlink server to event loop: %m");
+
+        r = varlink_server_listen_auto(c->varlink_server);
+        if (r < 0)
+                return log_error_errno(r, "Failed to bind to passed Varlink sockets: %m");
+        if (r == 0) {
+                r = varlink_server_listen_address(c->varlink_server, "/run/systemd/io.systemd.Hostname", 0666);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to bind to Varlink socket: %m");
+        }
+
         return 0;
 }
 
@@ -1572,8 +1698,6 @@ static int run(int argc, char *argv[]) {
         _cleanup_(context_destroy) Context context = {
                 .hostname_source = _HOSTNAME_INVALID, /* appropriate value will be set later */
         };
-        _cleanup_(sd_event_unrefp) sd_event *event = NULL;
-        _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
         int r;
 
         log_setup();
@@ -1592,27 +1716,35 @@ static int run(int argc, char *argv[]) {
         if (r < 0)
                 return r;
 
-        assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, -1) >= 0);
-
-        r = sd_event_default(&event);
+        r = sd_event_default(&context.event);
         if (r < 0)
                 return log_error_errno(r, "Failed to allocate event loop: %m");
 
-        (void) sd_event_set_watchdog(event, true);
+        (void) sd_event_set_watchdog(context.event, true);
 
-        r = sd_event_add_signal(event, NULL, SIGINT, NULL, NULL);
+        r = sd_event_set_signal_exit(context.event, true);
         if (r < 0)
-                return log_error_errno(r, "Failed to install SIGINT handler: %m");
+                return log_error_errno(r, "Failed to install SIGINT/SIGTERM handlers: %m");
 
-        r = sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL);
+        r = connect_bus(&context);
         if (r < 0)
-                return log_error_errno(r, "Failed to install SIGTERM handler: %m");
+                return r;
 
-        r = connect_bus(&context, event, &bus);
+        r = connect_varlink(&context);
         if (r < 0)
                 return r;
 
-        r = bus_event_loop_with_idle(event, bus, "org.freedesktop.hostname1", DEFAULT_EXIT_USEC, NULL, NULL);
+        r = sd_notify(false, NOTIFY_READY);
+        if (r < 0)
+                log_warning_errno(r, "Failed to send readiness notification, ignoring: %m");
+
+        r = bus_event_loop_with_idle(
+                        context.event,
+                        context.bus,
+                        "org.freedesktop.hostname1",
+                        DEFAULT_EXIT_USEC,
+                        /* check_idle= */ NULL,
+                        /* userdata= */ NULL);
         if (r < 0)
                 return log_error_errno(r, "Failed to run event loop: %m");
 
index e9bbbb628dae84697b66a2bfc72d7287a3d07558..3321155e84fa19427b07f708f58e6ff531bb5c15 100644 (file)
@@ -12,6 +12,7 @@
 #include "bus-polkit.h"
 #include "common-signal.h"
 #include "constants.h"
+#include "daemon-util.h"
 #include "env-util.h"
 #include "fd-util.h"
 #include "float.h"
@@ -527,7 +528,7 @@ static Manager *manager_unref(Manager *m) {
 
         hashmap_free(m->transfers);
 
-        bus_verify_polkit_async_registry_free(m->polkit_registry);
+        hashmap_free(m->polkit_registry);
 
         m->bus = sd_bus_flush_close_unref(m->bus);
         sd_event_unref(m->event);
@@ -1332,18 +1333,6 @@ static bool manager_check_idle(void *userdata) {
         return hashmap_isempty(m->transfers);
 }
 
-static int manager_run(Manager *m) {
-        assert(m);
-
-        return bus_event_loop_with_idle(
-                        m->event,
-                        m->bus,
-                        "org.freedesktop.import1",
-                        DEFAULT_EXIT_USEC,
-                        manager_check_idle,
-                        m);
-}
-
 static void manager_parse_env(Manager *m) {
         int r;
 
@@ -1394,7 +1383,17 @@ static int run(int argc, char *argv[]) {
         if (r < 0)
                 return r;
 
-        r = manager_run(m);
+        r = sd_notify(false, NOTIFY_READY);
+        if (r < 0)
+                log_warning_errno(r, "Failed to send readiness notification, ignoring: %m");
+
+        r = bus_event_loop_with_idle(
+                        m->event,
+                        m->bus,
+                        "org.freedesktop.import1",
+                        DEFAULT_EXIT_USEC,
+                        manager_check_idle,
+                        m);
         if (r < 0)
                 return log_error_errno(r, "Failed to run event loop: %m");
 
index 38821b5790f57e05be8908e6644100b0158d706d..10e3f131a1c2ac6806526e3b082b50cff925d1fb 100644 (file)
@@ -353,7 +353,7 @@ static int parse_argv(int argc, char *argv[]) {
                                 /* If this is not a valid verification mode, maybe it's a literally specified
                                  * SHA256 hash? We can handle that too... */
 
-                                r = unhexmem(optarg, (size_t) -1, &h, &n);
+                                r = unhexmem(optarg, &h, &n);
                                 if (r < 0 || n == 0)
                                         return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
                                                                "Invalid verification setting: %s", optarg);
index 79010d048280fcfd8ba6518b5c66256ea99eb3b7..7c5ccbbea6f0cf91717dfecfada33a2d38d013aa 100644 (file)
@@ -525,7 +525,7 @@ static int accept_connection(
                 if (r < 0)
                         return log_error_errno(r, "socket_address_print(): %m");
 
-                r = socknameinfo_pretty(&addr->sockaddr, addr->size, &b);
+                r = socknameinfo_pretty(&addr->sockaddr.sa, addr->size, &b);
                 if (r < 0)
                         return log_error_errno(r, "Resolving hostname failed: %m");
 
index 609ddbaf6b4c37c5bb81755668fe8610dd50c5da..0325add12f4d7f01d3af712b14e1fcb37c2edfad 100644 (file)
@@ -12,6 +12,7 @@
 
 #include "alloc-util.h"
 #include "build.h"
+#include "env-util.h"
 #include "fd-util.h"
 #include "format-util.h"
 #include "main-func.h"
@@ -157,7 +158,6 @@ static int run(int argc, char *argv[]) {
         if (argc <= optind)
                 (void) execl("/bin/cat", "/bin/cat", NULL);
         else {
-                _cleanup_free_ char *s = NULL;
                 struct stat st;
 
                 if (fstat(STDERR_FILENO, &st) < 0)
@@ -165,11 +165,9 @@ static int run(int argc, char *argv[]) {
                                                "Failed to fstat(%s): %m",
                                                FORMAT_PROC_FD_PATH(STDERR_FILENO));
 
-                if (asprintf(&s, DEV_FMT ":" INO_FMT, (dev_t)st.st_dev, st.st_ino) < 0)
-                        return log_oom();
-
-                if (setenv("JOURNAL_STREAM", s, /* overwrite = */ true) < 0)
-                        return log_error_errno(errno, "Failed to set environment variable JOURNAL_STREAM: %m");
+                r = setenvf("JOURNAL_STREAM", /* overwrite = */ true, DEV_FMT ":" INO_FMT, (dev_t) st.st_dev, st.st_ino);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to set environment variable JOURNAL_STREAM: %m");
 
                 (void) execvp(argv[optind], argv + optind);
         }
index fe68ec4310b3dc40358e30d3228bca5d4e69c588..cc476c30db764e918ba4daba5bbe2d69cbb49580 100644 (file)
@@ -28,6 +28,7 @@
 #include "chattr-util.h"
 #include "constants.h"
 #include "devnum-util.h"
+#include "dirent-util.h"
 #include "dissect-image.h"
 #include "fd-util.h"
 #include "fileio.h"
@@ -122,6 +123,7 @@ static bool arg_force = false;
 static usec_t arg_since = 0, arg_until = 0;
 static bool arg_since_set = false, arg_until_set = false;
 static char **arg_syslog_identifier = NULL;
+static char **arg_exclude_identifier = NULL;
 static char **arg_system_units = NULL;
 static char **arg_user_units = NULL;
 static const char *arg_field = NULL;
@@ -146,6 +148,7 @@ STATIC_DESTRUCTOR_REGISTER(arg_file, strv_freep);
 STATIC_DESTRUCTOR_REGISTER(arg_facilities, set_freep);
 STATIC_DESTRUCTOR_REGISTER(arg_verify_key, freep);
 STATIC_DESTRUCTOR_REGISTER(arg_syslog_identifier, strv_freep);
+STATIC_DESTRUCTOR_REGISTER(arg_exclude_identifier, strv_freep);
 STATIC_DESTRUCTOR_REGISTER(arg_system_units, strv_freep);
 STATIC_DESTRUCTOR_REGISTER(arg_user_units, strv_freep);
 STATIC_DESTRUCTOR_REGISTER(arg_root, freep);
@@ -173,6 +176,7 @@ static enum {
         ACTION_ROTATE_AND_VACUUM,
         ACTION_LIST_FIELDS,
         ACTION_LIST_FIELD_NAMES,
+        ACTION_LIST_NAMESPACES,
 } arg_action = ACTION_SHOW;
 
 static int add_matches_for_device(sd_journal *j, const char *devpath) {
@@ -380,6 +384,8 @@ static int help(void) {
                "  -u --unit=UNIT             Show logs from the specified unit\n"
                "     --user-unit=UNIT        Show logs from the specified user unit\n"
                "  -t --identifier=STRING     Show entries with the specified syslog identifier\n"
+               "  -T --exclude-identifier=STRING\n"
+               "                             Hide entries with the specified syslog identifier\n"
                "  -p --priority=RANGE        Show entries with the specified priority\n"
                "     --facility=FACILITY...  Show entries with the specified facilities\n"
                "  -g --grep=PATTERN          Show entries with MESSAGE matching PATTERN\n"
@@ -487,6 +493,7 @@ static int parse_argv(int argc, char *argv[]) {
                 ARG_NO_HOSTNAME,
                 ARG_OUTPUT_FIELDS,
                 ARG_NAMESPACE,
+                ARG_LIST_NAMESPACES,
         };
 
         static const struct option options[] = {
@@ -519,6 +526,7 @@ static int parse_argv(int argc, char *argv[]) {
                 { "image-policy",         required_argument, NULL, ARG_IMAGE_POLICY         },
                 { "header",               no_argument,       NULL, ARG_HEADER               },
                 { "identifier",           required_argument, NULL, 't'                      },
+                { "exclude-identifier",   required_argument, NULL, 'T'                      },
                 { "priority",             required_argument, NULL, 'p'                      },
                 { "facility",             required_argument, NULL, ARG_FACILITY             },
                 { "grep",                 required_argument, NULL, 'g'                      },
@@ -556,6 +564,7 @@ static int parse_argv(int argc, char *argv[]) {
                 { "no-hostname",          no_argument,       NULL, ARG_NO_HOSTNAME          },
                 { "output-fields",        required_argument, NULL, ARG_OUTPUT_FIELDS        },
                 { "namespace",            required_argument, NULL, ARG_NAMESPACE            },
+                { "list-namespaces",      no_argument,       NULL, ARG_LIST_NAMESPACES      },
                 {}
         };
 
@@ -564,7 +573,7 @@ static int parse_argv(int argc, char *argv[]) {
         assert(argc >= 0);
         assert(argv);
 
-        while ((c = getopt_long(argc, argv, "hefo:aln::qmb::kD:p:g:c:S:U:t:u:NF:xrM:i:", options, NULL)) >= 0)
+        while ((c = getopt_long(argc, argv, "hefo:aln::qmb::kD:p:g:c:S:U:t:T:u:NF:xrM:i:", options, NULL)) >= 0)
 
                 switch (c) {
 
@@ -722,6 +731,10 @@ static int parse_argv(int argc, char *argv[]) {
 
                         break;
 
+                case ARG_LIST_NAMESPACES:
+                        arg_action = ACTION_LIST_NAMESPACES;
+                        break;
+
                 case 'D':
                         arg_directory = optarg;
                         break;
@@ -958,6 +971,12 @@ static int parse_argv(int argc, char *argv[]) {
                                 return log_oom();
                         break;
 
+                case 'T':
+                        r = strv_extend(&arg_exclude_identifier, optarg);
+                        if (r < 0)
+                                return log_oom();
+                        break;
+
                 case 'u':
                         r = strv_extend(&arg_system_units, optarg);
                         if (r < 0)
@@ -1218,6 +1237,64 @@ static int add_matches(sd_journal *j, char **args) {
         return 0;
 }
 
+static int list_namespaces(const char *root) {
+        _cleanup_(table_unrefp) Table *table = NULL;
+        sd_id128_t machine;
+        char machine_id[SD_ID128_STRING_MAX];
+        int r;
+
+        r = sd_id128_get_machine(&machine);
+        if (r < 0)
+                return log_error_errno(r, "Failed to get machine ID: %m");
+
+        sd_id128_to_string(machine, machine_id);
+
+        table = table_new("namespace");
+        if (!table)
+                return log_oom();
+
+        (void) table_set_sort(table, (size_t) 0);
+
+        FOREACH_STRING(dir, "/var/log/journal", "/run/log/journal") {
+                _cleanup_free_ char *path = NULL;
+                _cleanup_closedir_ DIR *dirp = NULL;
+
+                path = path_join(root, dir);
+                if (!path)
+                        return log_oom();
+
+                dirp = opendir(path);
+                if (!dirp) {
+                        log_debug_errno(errno, "Failed to open directory %s, ignoring: %m", path);
+                        continue;
+                }
+
+                FOREACH_DIRENT(de, dirp, return log_error_errno(errno, "Failed to iterate through %s: %m", path)) {
+                        char *dot;
+
+                        if (!startswith(de->d_name, machine_id))
+                                continue;
+
+                        dot = strchr(de->d_name, '.');
+                        if (!dot)
+                                continue;
+
+                        if (!log_namespace_name_valid(dot + 1))
+                                continue;
+
+                        r = table_add_cell(table, NULL, TABLE_STRING, dot + 1);
+                        if (r < 0)
+                                return table_log_add_error(r);
+                }
+        }
+
+        r = table_print_with_pager(table, arg_json_format_flags, arg_pager_flags, !arg_quiet);
+        if (r < 0)
+                return table_log_print_error(r);
+
+        return 0;
+}
+
 static int list_boots(sd_journal *j) {
         _cleanup_(table_unrefp) Table *table = NULL;
         _cleanup_free_ BootId *boots = NULL;
@@ -1572,6 +1649,19 @@ static int add_syslog_identifier(sd_journal *j) {
         return 0;
 }
 
+static int add_exclude_identifier(sd_journal *j) {
+        _cleanup_set_free_ Set *excludes = NULL;
+        int r;
+
+        assert(j);
+
+        r = set_put_strdupv(&excludes, arg_exclude_identifier);
+        if (r < 0)
+                return r;
+
+        return set_free_and_replace(j->exclude_syslog_identifiers, excludes);
+}
+
 #if HAVE_GCRYPT
 static int format_journal_url(
                 const void *seed,
@@ -2264,6 +2354,9 @@ static int run(int argc, char *argv[]) {
         case ACTION_ROTATE:
                 return rotate();
 
+        case ACTION_LIST_NAMESPACES:
+                return list_namespaces(arg_root);
+
         case ACTION_SHOW:
         case ACTION_PRINT_HEADER:
         case ACTION_VERIFY:
@@ -2408,6 +2501,10 @@ static int run(int argc, char *argv[]) {
         if (r < 0)
                 return log_error_errno(r, "Failed to add filter for syslog identifiers: %m");
 
+        r = add_exclude_identifier(j);
+        if (r < 0)
+                return log_error_errno(r, "Failed to add exclude filter for syslog identifiers: %m");
+
         r = add_priorities(j);
         if (r < 0)
                 return r;
index 476da317fe764e8a976b17020bd3ccda7f0e490f..2469451fca44f750b3ade2a8d81b6aa6e574f459 100644 (file)
@@ -56,7 +56,7 @@
 #include "string-table.h"
 #include "string-util.h"
 #include "syslog-util.h"
-#include "uid-alloc-range.h"
+#include "uid-classification.h"
 #include "user-util.h"
 #include "varlink-io.systemd.Journal.h"
 
index 45f0c1e7a8cc297f67a751599097d7ae4470fda1..d33a3ddf2f3e57e73839b7ed91d34ae6dee6f8d5 100644 (file)
@@ -49,6 +49,7 @@ static bool arg_legend = true;
 STATIC_DESTRUCTOR_REGISTER(arg_esp_path, freep);
 STATIC_DESTRUCTOR_REGISTER(arg_xbootldr_path, freep);
 STATIC_DESTRUCTOR_REGISTER(arg_root, freep);
+STATIC_DESTRUCTOR_REGISTER(arg_image, freep);
 STATIC_DESTRUCTOR_REGISTER(arg_image_policy, image_policy_freep);
 
 typedef enum Action {
@@ -1181,7 +1182,7 @@ static int verb_add(int argc, char *argv[], void *userdata) {
         assert(argv);
 
         if (arg_root)
-                return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "'add' does not support --root=.");
+                return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "'add' does not support --root= or --image=.");
 
         if (bypass())
                 return 0;
@@ -1220,6 +1221,9 @@ static int verb_add_all(int argc, char *argv[], void *userdata) {
 
         assert(argv);
 
+        if (arg_root)
+                return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "'add-all' does not support --root= or --image=.");
+
         if (bypass())
                 return 0;
 
@@ -1308,7 +1312,7 @@ static int verb_remove(int argc, char *argv[], void *userdata) {
         assert(argv);
 
         if (arg_root)
-                return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "'remove' does not support --root=.");
+                return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "'remove' does not support --root= or --image=.");
 
         if (argc > 2)
                 log_debug("Too many arguments specified. 'kernel-install remove' takes only kernel version. "
@@ -1449,6 +1453,9 @@ static int verb_list(int argc, char *argv[], void *userdata) {
         _cleanup_close_ int fd = -EBADF;
         int r;
 
+        if (arg_root)
+                return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "'list' does not support --root= or --image=.");
+
         fd = open("/usr/lib/modules", O_DIRECTORY|O_RDONLY|O_CLOEXEC);
         if (fd < 0)
                 return log_error_errno(fd, "Failed to open /usr/lib/modules/: %m");
index 6feec25adf08ea891a79ebb5b666f582f00ceb6e..da5fcaa450b635c9ec7d6fb6b48e5a524955f4bb 100644 (file)
@@ -135,6 +135,8 @@ int sd_dhcp_client_id_to_string(const sd_dhcp_client_id *client_id, char **ret)
                         r = asprintf(&t, "IAID:0x%x/DUID", iaid);
                 }
                 break;
+        default:
+                assert_not_reached();
         }
         if (r < 0)
                 return -ENOMEM;
index fd32fbb049028a2be1bc110de9418cb51c1bea5e..401e70823a93237926a72fc7b765dfb89e0061f5 100644 (file)
@@ -1479,7 +1479,7 @@ int dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file) {
                 _cleanup_free_ void *data = NULL;
                 size_t data_size;
 
-                r = unhexmem(client_id_hex, SIZE_MAX, &data, &data_size);
+                r = unhexmem(client_id_hex, &data, &data_size);
                 if (r < 0)
                         log_debug_errno(r, "Failed to parse client ID %s, ignoring: %m", client_id_hex);
 
@@ -1489,7 +1489,7 @@ int dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file) {
         }
 
         if (vendor_specific_hex) {
-                r = unhexmem(vendor_specific_hex, SIZE_MAX, &lease->vendor_specific, &lease->vendor_specific_len);
+                r = unhexmem(vendor_specific_hex, &lease->vendor_specific, &lease->vendor_specific_len);
                 if (r < 0)
                         log_debug_errno(r, "Failed to parse vendor specific data %s, ignoring: %m", vendor_specific_hex);
         }
@@ -1501,7 +1501,7 @@ int dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file) {
                 if (!options[i])
                         continue;
 
-                r = unhexmem(options[i], SIZE_MAX, &data, &len);
+                r = unhexmem(options[i], &data, &len);
                 if (r < 0) {
                         log_debug_errno(r, "Failed to parse private DHCP option %s, ignoring: %m", options[i]);
                         continue;
index 718709f0b24bd9bd7bc91406ff5ea90ea3481290..6dcae2e183a20746bc9bc96d149cbdb6ed01c637 100644 (file)
@@ -266,7 +266,7 @@ static int verify_anonymous_token(sd_bus *b, const char *p, size_t l) {
         if (l % 2 != 0)
                 return 0;
 
-        r = unhexmem(p, l, (void **) &token, &len);
+        r = unhexmem_full(p, l, /* secure = */ false, (void**) &token, &len);
         if (r < 0)
                 return 0;
 
@@ -298,7 +298,7 @@ static int verify_external_token(sd_bus *b, const char *p, size_t l) {
         if (l % 2 != 0)
                 return 0;
 
-        r = unhexmem(p, l, (void**) &token, &len);
+        r = unhexmem_full(p, l, /* secure = */ false, (void**) &token, &len);
         if (r < 0)
                 return 0;
 
index f036a49c644b50d1ea482c953c53f00e1c168523..86b626595e71f386ad219256580e5dfedc6b2e9f 100644 (file)
@@ -1486,9 +1486,15 @@ interpret_port_as_machine_old_syntax:
                         return -ENOMEM;
         }
 
-        a = strjoin("unixexec:path=ssh,argv1=-xT", p ? ",argv2=-p,argv3=" : "", strempty(p),
-                                ",argv", p ? "4" : "2", "=--,argv", p ? "5" : "3", "=", e,
-                                ",argv", p ? "6" : "4", "=systemd-stdio-bridge", c);
+        const char *ssh = secure_getenv("SYSTEMD_SSH") ?: "ssh";
+        _cleanup_free_ char *ssh_escaped = bus_address_escape(ssh);
+        if (!ssh_escaped)
+                return -ENOMEM;
+
+        a = strjoin("unixexec:path=", ssh_escaped, ",argv1=-xT",
+                    p ? ",argv2=-p,argv3=" : "", strempty(p),
+                    ",argv", p ? "4" : "2", "=--,argv", p ? "5" : "3", "=", e,
+                    ",argv", p ? "6" : "4", "=systemd-stdio-bridge", c);
         if (!a)
                 return -ENOMEM;
 
index 49ab56adcbbadc401748b65a8e7423b34a719524..018a68d1dbbe490345962889aa6ab6794004f7ca 100644 (file)
@@ -47,7 +47,7 @@ struct sd_device_monitor {
         union sockaddr_union snl_trusted_sender;
         bool bound;
 
-        UidRange *mapped_userns_uid_range;
+        UIDRange *mapped_userns_uid_range;
 
         Hashmap *subsystem_filter;
         Set *tag_filter;
index a9a9b7ad988165304c092fc974b00c8888a87e50..534a296715b360c97a75eceb104adf277cac0869 100644 (file)
@@ -10,6 +10,7 @@
 #include "alloc-util.h"
 #include "log.h"
 #include "macro.h"
+#include "strv.h"
 
 #define device_unref_and_replace(a, b)                                  \
         unref_and_replace_full(a, b, sd_device_ref, sd_device_unref)
@@ -105,3 +106,10 @@ char** device_make_log_fields(sd_device *device);
 
 bool device_in_subsystem(sd_device *device, const char *subsystem);
 bool device_is_devtype(sd_device *device, const char *devtype);
+
+static inline bool device_property_can_set(const char *property) {
+        return property &&
+                !STR_IN_SET(property,
+                            "ACTION", "DEVLINKS", "DEVNAME", "DEVPATH", "DEVTYPE", "DRIVER",
+                            "IFINDEX", "MAJOR", "MINOR", "SEQNUM", "SUBSYSTEM", "TAGS");
+}
index 56f9ac7fc1d989e3a4331a810aa3c8e040a385e5..338609b186905d81519c382e5070e793377f67b8 100644 (file)
@@ -1976,7 +1976,7 @@ _public_ int sd_event_add_memory_pressure(
 
                 env = secure_getenv("MEMORY_PRESSURE_WRITE");
                 if (env) {
-                        r = unbase64mem(env, SIZE_MAX, &write_buffer, &write_buffer_size);
+                        r = unbase64mem(env, &write_buffer, &write_buffer_size);
                         if (r < 0)
                                 return r;
                 }
index 69fc1bf07e5d52e6596b21c3457b4c1bbaea2439..5808cea7576a9fd827af28c76e202fb4f92cc48a 100644 (file)
@@ -13,6 +13,7 @@
 #include "stdio-util.h"
 #include "string-util.h"
 #include "sync-util.h"
+#include "virt.h"
 
 int id128_from_string_nonzero(const char *s, sd_id128_t *ret) {
         sd_id128_t t;
@@ -223,6 +224,13 @@ int id128_get_product(sd_id128_t *ret) {
         /* Reads the systems product UUID from DMI or devicetree (where it is located on POWER). This is
          * particularly relevant in VM environments, where VM managers typically place a VM uuid there. */
 
+        r = detect_container();
+        if (r < 0)
+                return r;
+        if (r > 0) /* Refuse returning this in containers, as this is not a property of our system then, but
+                    * of the host */
+                return -ENOENT;
+
         r = id128_read("/sys/class/dmi/id/product_uuid", ID128_FORMAT_UUID, &uuid);
         if (r == -ENOENT)
                 r = id128_read("/proc/device-tree/vm,uuid", ID128_FORMAT_UUID, &uuid);
index 5e6ff66e3c5732814a734f8043d66f79fbcd4889..c9ff6abca657330d95c27b347dd0d1bdc8ff6074 100644 (file)
@@ -2757,7 +2757,7 @@ static int generic_array_get(
                 Object **ret_object,    /* The found object. */
                 uint64_t *ret_offset) { /* The offset of the found object. */
 
-        uint64_t a, t = 0, k;
+        uint64_t a, t = 0, k = 0; /* Explicit initialization of k to appease gcc */
         ChainCacheItem *ci;
         Object *o = NULL;
         int r;
index 1b86d3d3c45e6d234e677447606c4f8fab8a9ae2..e57c5208c18c425498b56e85719815a9c00ace61 100644 (file)
@@ -87,6 +87,7 @@ struct sd_journal {
         uint64_t current_field;
 
         Match *level0, *level1, *level2;
+        Set *exclude_syslog_identifiers;
 
         uint64_t origin_id;
 
index 18e62e7ac07f1fe437010f5198210b045fcd7b00..5a8c8a85790bc5afdc03c7ba45ea2a56d0c34ed0 100644 (file)
@@ -43,7 +43,7 @@
 #include "string-util.h"
 #include "strv.h"
 #include "syslog-util.h"
-#include "uid-alloc-range.h"
+#include "uid-classification.h"
 
 #define JOURNAL_FILES_RECHECK_USEC (2 * USEC_PER_SEC)
 
@@ -2327,6 +2327,8 @@ _public_ void sd_journal_close(sd_journal *j) {
 
         hashmap_free_free(j->errors);
 
+        set_free(j->exclude_syslog_identifiers);
+
         free(j->path);
         free(j->prefix);
         free(j->namespace);
index e4e57a0f4a613250d52508fdac700ffe84cdc90d..0489df573b593077547bf1ca3df6324fc4d3e53a 100644 (file)
@@ -304,7 +304,7 @@ void context_clear(Context *c) {
         c->x11_cache = sd_bus_message_unref(c->x11_cache);
         c->vc_cache = sd_bus_message_unref(c->vc_cache);
 
-        c->polkit_registry = bus_verify_polkit_async_registry_free(c->polkit_registry);
+        c->polkit_registry = hashmap_free(c->polkit_registry);
 };
 
 X11Context *context_get_x11_context(Context *c) {
index 8ce8c0d08fc5eff6003fcbe78555ee870c6d0125..cf88da37ad9f7c798c5ee117d40ef34d0a939e57 100644 (file)
@@ -15,6 +15,7 @@
 #include "bus-polkit.h"
 #include "bus-unit-util.h"
 #include "constants.h"
+#include "daemon-util.h"
 #include "kbd-util.h"
 #include "localed-util.h"
 #include "macro.h"
@@ -647,26 +648,24 @@ static int run(int argc, char *argv[]) {
         if (r < 0)
                 return r;
 
-        assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, -1) >= 0);
-
         r = sd_event_default(&event);
         if (r < 0)
                 return log_error_errno(r, "Failed to allocate event loop: %m");
 
         (void) sd_event_set_watchdog(event, true);
 
-        r = sd_event_add_signal(event, NULL, SIGINT, NULL, NULL);
-        if (r < 0)
-                return log_error_errno(r, "Failed to install SIGINT handler: %m");
-
-        r = sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL);
+        r = sd_event_set_signal_exit(event, true);
         if (r < 0)
-                return log_error_errno(r, "Failed to install SIGTERM handler: %m");
+                return log_error_errno(r, "Failed to install SIGINT/SIGTERM handlers: %m");
 
         r = connect_bus(&context, event, &bus);
         if (r < 0)
                 return r;
 
+        r = sd_notify(false, NOTIFY_READY);
+        if (r < 0)
+                log_warning_errno(r, "Failed to send readiness notification, ignoring: %m");
+
         r = bus_event_loop_with_idle(event, bus, "org.freedesktop.locale1", DEFAULT_EXIT_USEC, NULL, NULL);
         if (r < 0)
                 return log_error_errno(r, "Failed to run event loop: %m");
index acef32679a1adce19aa990b23565460f17ba6274..138952da91da82424c6c4b3f5bd8cd20d44864a4 100644 (file)
@@ -133,6 +133,11 @@ const HandleActionData* handle_action_lookup(HandleAction action) {
         return &handle_action_data_table[action];
 }
 
+static bool handle_action_sleep_supported(HandleAction action) {
+        assert(HANDLE_ACTION_IS_SLEEP(action) && action != HANDLE_SLEEP);
+        return sleep_supported(ASSERT_PTR(handle_action_lookup(action))->sleep_operation) > 0;
+}
+
 /* The order in which we try each sleep operation. We should typically prefer operations without a delay,
  * i.e. s2h and suspend, and use hibernation at last since it requires minimum hardware support.
  * hybrid-sleep is disabled by default, and thus should be ordered before suspend if manually chosen by user,
@@ -169,7 +174,7 @@ HandleAction handle_action_sleep_select(HandleActionSleepMask mask) {
                 if (!FLAGS_SET(mask, a))
                         continue;
 
-                if (sleep_supported(ASSERT_PTR(handle_action_lookup(*i))->sleep_operation) > 0)
+                if (handle_action_sleep_supported(*i))
                         return *i;
         }
 
@@ -266,18 +271,7 @@ static int handle_action_sleep_execute(
                 return handle_action_sleep_execute(m, a, ignore_inhibited, is_edge);
         }
 
-        bool supported;
-
-        if (handle == HANDLE_SUSPEND)
-                supported = sleep_supported(SLEEP_SUSPEND) > 0;
-        else if (handle == HANDLE_HIBERNATE)
-                supported = sleep_supported(SLEEP_HIBERNATE) > 0;
-        else if (handle == HANDLE_HYBRID_SLEEP)
-                supported = sleep_supported(SLEEP_HYBRID_SLEEP) > 0;
-        else if (handle == HANDLE_SUSPEND_THEN_HIBERNATE)
-                supported = sleep_supported(SLEEP_SUSPEND_THEN_HIBERNATE) > 0;
-        else
-                assert_not_reached();
+        bool supported = handle_action_sleep_supported(handle);
 
         if (!supported && handle != HANDLE_SUSPEND) {
                 supported = sleep_supported(SLEEP_SUSPEND) > 0;
index 9348ccc4dd0e3f7a65306c12fa6a9febbf66f3ca..15f04c4a66aaeedd08511cc9bd732e9f5579d0a9 100644 (file)
@@ -807,7 +807,6 @@ int session_send_create_reply(Session *s, sd_bus_error *error) {
         _cleanup_(sd_bus_message_unrefp) sd_bus_message *c = NULL;
         _cleanup_close_ int fifo_fd = -EBADF;
         _cleanup_free_ char *p = NULL;
-        int r;
 
         assert(s);
 
@@ -828,10 +827,6 @@ int session_send_create_reply(Session *s, sd_bus_error *error) {
         if (fifo_fd < 0)
                 return fifo_fd;
 
-        r = session_watch_pidfd(s);
-        if (r < 0)
-                return r;
-
         /* Update the session state file before we notify the client about the result. */
         session_save(s);
 
index c1bc88d038473cc4d937920a6a126f9d4b516617..cd5c0ec82dbb7a512e7d4adee67e80adf93dbbb3 100644 (file)
@@ -321,24 +321,25 @@ static int session_device_verify(SessionDevice *sd) {
         return 0;
 }
 
-int session_device_new(Session *s, dev_t dev, bool open_device, SessionDevice **out) {
+int session_device_new(Session *s, dev_t dev, bool open_device, SessionDevice **ret) {
         SessionDevice *sd;
         int r;
 
         assert(s);
-        assert(out);
 
         if (!s->seat)
                 return -EPERM;
 
-        sd = new0(SessionDevice, 1);
+        sd = new(SessionDevice, 1);
         if (!sd)
                 return -ENOMEM;
 
-        sd->session = s;
-        sd->dev = dev;
-        sd->fd = -EBADF;
-        sd->type = DEVICE_TYPE_UNKNOWN;
+        *sd = (SessionDevice) {
+                .session = s,
+                .dev = dev,
+                .fd = -EBADF,
+                .type = DEVICE_TYPE_UNKNOWN,
+        };
 
         r = session_device_verify(sd);
         if (r < 0)
@@ -369,7 +370,9 @@ int session_device_new(Session *s, dev_t dev, bool open_device, SessionDevice **
 
         LIST_PREPEND(sd_by_device, sd->device->session_devices, sd);
 
-        *out = sd;
+        if (ret)
+                *ret = sd;
+
         return 0;
 
 error:
index 04654d12524aaaea684e80f4b00a9141f7bf596a..06a8f80c060c1a3754752b97f6b59ba5a9590c51 100644 (file)
@@ -27,7 +27,7 @@ struct SessionDevice {
         LIST_FIELDS(struct SessionDevice, sd_by_device);
 };
 
-int session_device_new(Session *s, dev_t dev, bool open_device, SessionDevice **out);
+int session_device_new(Session *s, dev_t dev, bool open_device, SessionDevice **ret);
 SessionDevice *session_device_free(SessionDevice *sd);
 DEFINE_TRIVIAL_CLEANUP_FUNC(SessionDevice*, session_device_free);
 
index 3f5decd8d9bd1a62b5ba77e9492378d49e612584..c60811f72efb0a1b8d76082e93aaf283287f7341 100644 (file)
@@ -15,6 +15,7 @@
 #include "audit-util.h"
 #include "bus-error.h"
 #include "bus-util.h"
+#include "daemon-util.h"
 #include "devnum-util.h"
 #include "env-file.h"
 #include "escape.h"
@@ -37,7 +38,7 @@
 #include "strv.h"
 #include "terminal-util.h"
 #include "tmpfile-util.h"
-#include "uid-alloc-range.h"
+#include "uid-classification.h"
 #include "user-util.h"
 
 #define RELEASE_USEC (20*USEC_PER_SEC)
@@ -87,13 +88,54 @@ int session_new(Session **ret, Manager *m, const char *id) {
         return 0;
 }
 
-static void session_reset_leader(Session *s) {
+static int session_dispatch_leader_pidfd(sd_event_source *es, int fd, uint32_t revents, void *userdata) {
+        Session *s = ASSERT_PTR(userdata);
+
+        assert(s->leader.fd == fd);
+        session_stop(s, /* force= */ false);
+
+        return 1;
+}
+
+static int session_watch_pidfd(Session *s) {
+        int r;
+
+        assert(s);
+        assert(s->manager);
+        assert(pidref_is_set(&s->leader));
+
+        if (s->leader.fd < 0)
+                return 0;
+
+        r = sd_event_add_io(s->manager->event, &s->leader_pidfd_event_source, s->leader.fd, EPOLLIN, session_dispatch_leader_pidfd, s);
+        if (r < 0)
+                return r;
+
+        r = sd_event_source_set_priority(s->leader_pidfd_event_source, SD_EVENT_PRIORITY_IMPORTANT);
+        if (r < 0)
+                return r;
+
+        (void) sd_event_source_set_description(s->leader_pidfd_event_source, "session-pidfd");
+
+        return 0;
+}
+
+static void session_reset_leader(Session *s, bool keep_fdstore) {
         assert(s);
 
+        if (!keep_fdstore) {
+                /* Clear fdstore if we're asked to, no matter if s->leader is set or not, so that when
+                 * initially deserializing leader fd we clear the old fd too. */
+                (void) notify_remove_fd_warnf("session-%s-leader-fd", s->id);
+                s->leader_fd_saved = false;
+        }
+
         if (!pidref_is_set(&s->leader))
                 return;
 
-        assert_se(hashmap_remove_value(s->manager->sessions_by_leader, &s->leader, s));
+        s->leader_pidfd_event_source = sd_event_source_disable_unref(s->leader_pidfd_event_source);
+
+        (void) hashmap_remove_value(s->manager->sessions_by_leader, &s->leader, s);
 
         return pidref_done(&s->leader);
 }
@@ -142,7 +184,7 @@ Session* session_free(Session *s) {
 
         free(s->scope_job);
 
-        session_reset_leader(s);
+        session_reset_leader(s, /* keep_fdstore = */ true);
 
         sd_bus_message_unref(s->create_message);
 
@@ -188,15 +230,27 @@ int session_set_leader_consume(Session *s, PidRef _leader) {
         if (pidref_equal(&s->leader, &pidref))
                 return 0;
 
-        session_reset_leader(s);
+        session_reset_leader(s, /* keep_fdstore = */ false);
 
         s->leader = TAKE_PIDREF(pidref);
 
+        r = session_watch_pidfd(s);
+        if (r < 0)
+                return log_error_errno(r, "Failed to watch leader pidfd for session '%s': %m", s->id);
+
         r = hashmap_ensure_put(&s->manager->sessions_by_leader, &pidref_hash_ops, &s->leader, s);
         if (r < 0)
                 return r;
         assert(r > 0);
 
+        if (s->leader.fd >= 0) {
+                r = notify_push_fdf(s->leader.fd, "session-%s-leader-fd", s->id);
+                if (r < 0)
+                        log_warning_errno(r, "Failed to push leader pidfd for session '%s', ignoring: %m", s->id);
+                else
+                        s->leader_fd_saved = true;
+        }
+
         (void) audit_session_from_pid(s->leader.pid, &s->audit_id);
 
         return 1;
@@ -240,16 +294,18 @@ int session_save(Session *s) {
                 "# This is private data. Do not parse.\n"
                 "UID="UID_FMT"\n"
                 "USER=%s\n"
-                "ACTIVE=%i\n"
-                "IS_DISPLAY=%i\n"
+                "ACTIVE=%s\n"
+                "IS_DISPLAY=%s\n"
                 "STATE=%s\n"
-                "REMOTE=%i\n",
+                "REMOTE=%s\n"
+                "LEADER_FD_SAVED=%s\n",
                 s->user->user_record->uid,
                 s->user->user_record->user_name,
-                session_is_active(s),
-                s->user->display == s,
+                one_zero(session_is_active(s)),
+                one_zero(s->user->display == s),
                 session_state_to_string(session_get_state(s)),
-                s->remote);
+                one_zero(s->remote),
+                one_zero(s->leader_fd_saved));
 
         if (s->type >= 0)
                 fprintf(f, "TYPE=%s\n", session_type_to_string(s->type));
@@ -377,33 +433,27 @@ static int session_load_devices(Session *s, const char *devices) {
 
         for (const char *p = devices;;) {
                 _cleanup_free_ char *word = NULL;
-                SessionDevice *sd;
                 dev_t dev;
                 int k;
 
                 k = extract_first_word(&p, &word, NULL, 0);
-                if (k == 0)
-                        break;
-                if (k < 0) {
-                        r = k;
+                if (k <= 0) {
+                        RET_GATHER(r, k);
                         break;
                 }
 
                 k = parse_devnum(word, &dev);
                 if (k < 0) {
-                        r = k;
+                        RET_GATHER(r, k);
                         continue;
                 }
 
                 /* The file descriptors for loaded devices will be reattached later. */
-                k = session_device_new(s, dev, false, &sd);
-                if (k < 0)
-                        r = k;
+                RET_GATHER(r, session_device_new(s, dev, /* open_device = */ false, /* ret = */ NULL));
         }
 
         if (r < 0)
-                log_error_errno(r, "Loading session devices for session %s failed: %m", s->id);
-
+                log_error_errno(r, "Failed to load some session devices for session '%s': %m", s->id);
         return r;
 }
 
@@ -414,7 +464,8 @@ int session_load(Session *s) {
                 *vtnr = NULL,
                 *state = NULL,
                 *position = NULL,
-                *leader = NULL,
+                *leader_pid = NULL,
+                *leader_fd_saved = NULL,
                 *type = NULL,
                 *original_type = NULL,
                 *class = NULL,
@@ -431,32 +482,33 @@ int session_load(Session *s) {
         assert(s);
 
         r = parse_env_file(NULL, s->state_file,
-                           "REMOTE",         &remote,
-                           "SCOPE",          &s->scope,
-                           "SCOPE_JOB",      &s->scope_job,
-                           "FIFO",           &s->fifo_path,
-                           "SEAT",           &seat,
-                           "TTY",            &s->tty,
-                           "TTY_VALIDITY",   &tty_validity,
-                           "DISPLAY",        &s->display,
-                           "REMOTE_HOST",    &s->remote_host,
-                           "REMOTE_USER",    &s->remote_user,
-                           "SERVICE",        &s->service,
-                           "DESKTOP",        &s->desktop,
-                           "VTNR",           &vtnr,
-                           "STATE",          &state,
-                           "POSITION",       &position,
-                           "LEADER",         &leader,
-                           "TYPE",           &type,
-                           "ORIGINAL_TYPE",  &original_type,
-                           "CLASS",          &class,
-                           "UID",            &uid,
-                           "REALTIME",       &realtime,
-                           "MONOTONIC",      &monotonic,
-                           "CONTROLLER",     &controller,
-                           "ACTIVE",         &active,
-                           "DEVICES",        &devices,
-                           "IS_DISPLAY",     &is_display);
+                           "REMOTE",          &remote,
+                           "SCOPE",           &s->scope,
+                           "SCOPE_JOB",       &s->scope_job,
+                           "FIFO",            &s->fifo_path,
+                           "SEAT",            &seat,
+                           "TTY",             &s->tty,
+                           "TTY_VALIDITY",    &tty_validity,
+                           "DISPLAY",         &s->display,
+                           "REMOTE_HOST",     &s->remote_host,
+                           "REMOTE_USER",     &s->remote_user,
+                           "SERVICE",         &s->service,
+                           "DESKTOP",         &s->desktop,
+                           "VTNR",            &vtnr,
+                           "STATE",           &state,
+                           "POSITION",        &position,
+                           "LEADER",          &leader_pid,
+                           "LEADER_FD_SAVED", &leader_fd_saved,
+                           "TYPE",            &type,
+                           "ORIGINAL_TYPE",   &original_type,
+                           "CLASS",           &class,
+                           "UID",             &uid,
+                           "REALTIME",        &realtime,
+                           "MONOTONIC",       &monotonic,
+                           "CONTROLLER",      &controller,
+                           "ACTIVE",          &active,
+                           "DEVICES",         &devices,
+                           "IS_DISPLAY",      &is_display);
         if (r < 0)
                 return log_error_errno(r, "Failed to read %s: %m", s->state_file);
 
@@ -523,19 +575,6 @@ int session_load(Session *s) {
                         s->tty_validity = v;
         }
 
-        if (leader) {
-                _cleanup_(pidref_done) PidRef p = PIDREF_NULL;
-
-                r = pidref_set_pidstr(&p, leader);
-                if (r < 0)
-                        log_debug_errno(r, "Failed to parse leader PID of session: %s", leader);
-                else {
-                        r = session_set_leader_consume(s, TAKE_PIDREF(p));
-                        if (r < 0)
-                                log_warning_errno(r, "Failed to set session leader PID, ignoring: %m");
-                }
-        }
-
         if (type) {
                 SessionType t;
 
@@ -610,6 +649,33 @@ int session_load(Session *s) {
                         session_restore_vt(s);
         }
 
+        if (leader_pid) {
+                assert(!pidref_is_set(&s->leader));
+
+                r = parse_pid(leader_pid, &s->deserialized_pid);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to parse LEADER=%s: %m", leader_pid);
+
+                if (leader_fd_saved) {
+                        r = parse_boolean(leader_fd_saved);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to parse LEADER_FD_SAVED=%s: %m", leader_fd_saved);
+                        s->leader_fd_saved = r > 0;
+
+                        if (s->leader_fd_saved)
+                                /* The leader fd will be acquired from fdstore later */
+                                return 0;
+                }
+
+                _cleanup_(pidref_done) PidRef p = PIDREF_NULL;
+
+                r = pidref_set_pid(&p, s->deserialized_pid);
+                if (r >= 0)
+                        r = session_set_leader_consume(s, TAKE_PIDREF(p));
+                if (r < 0)
+                        log_warning_errno(r, "Failed to set leader PID for session '%s': %m", s->id);
+        }
+
         return r;
 }
 
@@ -677,7 +743,7 @@ static int session_start_scope(Session *s, sd_bus_message *properties, sd_bus_er
                  * of STRV_IGNORE with strv_new() to skip these order constraints when needed. */
                 after = strv_new("systemd-logind.service",
                                  s->user->runtime_dir_service,
-                                 !uid_is_system(s->user->user_record->uid) ? "systemd-user-sessions.service" : STRV_IGNORE,
+                                 SESSION_CLASS_IS_EARLY(s->class) ? STRV_IGNORE : "systemd-user-sessions.service",
                                  s->user->service);
                 if (!after)
                         return log_oom();
@@ -744,7 +810,7 @@ static int session_setup_stop_on_idle_timer(Session *s) {
 
         assert(s);
 
-        if (s->manager->stop_idle_session_usec == USEC_INFINITY)
+        if (s->manager->stop_idle_session_usec == USEC_INFINITY || IN_SET(s->class, SESSION_GREETER, SESSION_LOCK_SCREEN))
                 return 0;
 
         r = sd_event_add_time_relative(
@@ -804,17 +870,17 @@ int session_start(Session *s, sd_bus_message *properties, sd_bus_error *error) {
         user_elect_display(s->user);
 
         /* Save data */
-        session_save(s);
-        user_save(s->user);
+        (void) session_save(s);
+        (void) user_save(s->user);
         if (s->seat)
-                seat_save(s->seat);
+                (void) seat_save(s->seat);
 
         /* Send signals */
         session_send_signal(s, true);
         user_send_changed(s->user, "Display", NULL);
 
         if (s->seat && s->seat->active == s)
-                seat_send_changed(s->seat, "ActiveSession", NULL);
+                (void) seat_send_changed(s->seat, "ActiveSession", NULL);
 
         return 0;
 }
@@ -900,8 +966,8 @@ int session_stop(Session *s, bool force) {
 
         user_elect_display(s->user);
 
-        session_save(s);
-        user_save(s->user);
+        (void) session_save(s);
+        (void) user_save(s->user);
 
         return r;
 }
@@ -923,7 +989,6 @@ int session_finalize(Session *s) {
                            LOG_MESSAGE("Removed session %s.", s->id));
 
         s->timer_event_source = sd_event_source_unref(s->timer_event_source);
-        s->leader_pidfd_event_source = sd_event_source_unref(s->leader_pidfd_event_source);
 
         if (s->seat)
                 seat_evict_position(s->seat, s);
@@ -948,10 +1013,10 @@ int session_finalize(Session *s) {
                 seat_save(s->seat);
         }
 
-        session_reset_leader(s);
+        session_reset_leader(s, /* keep_fdstore = */ false);
 
-        user_save(s->user);
-        user_send_changed(s->user, "Display", NULL);
+        (void) user_save(s->user);
+        (void) user_send_changed(s->user, "Display", NULL);
 
         return 0;
 }
@@ -1042,19 +1107,21 @@ int session_get_idle_hint(Session *s, dual_timestamp *t) {
                 return s->idle_hint;
         }
 
-        /* For sessions with an explicitly configured tty, let's check its atime */
-        if (s->tty) {
-                r = get_tty_atime(s->tty, &atime);
-                if (r >= 0)
-                        goto found_atime;
-        }
+        if (s->type == SESSION_TTY) {
+                /* For sessions with an explicitly configured tty, let's check its atime */
+                if (s->tty) {
+                        r = get_tty_atime(s->tty, &atime);
+                        if (r >= 0)
+                                goto found_atime;
+                }
 
-        /* For sessions with a leader but no explicitly configured tty, let's check the controlling tty of
-         * the leader */
-        if (pidref_is_set(&s->leader)) {
-                r = get_process_ctty_atime(s->leader.pid, &atime);
-                if (r >= 0)
-                        goto found_atime;
+                /* For sessions with a leader but no explicitly configured tty, let's check the controlling tty of
+                 * the leader */
+                if (pidref_is_set(&s->leader)) {
+                        r = get_process_ctty_atime(s->leader.pid, &atime);
+                        if (r >= 0)
+                                goto found_atime;
+                }
         }
 
         if (t)
@@ -1125,9 +1192,8 @@ void session_set_type(Session *s, SessionType t) {
                 return;
 
         s->type = t;
-        session_save(s);
-
-        session_send_changed(s, "Type", NULL);
+        (void) session_save(s);
+        (void) session_send_changed(s, "Type", NULL);
 }
 
 int session_set_display(Session *s, const char *display) {
@@ -1140,9 +1206,8 @@ int session_set_display(Session *s, const char *display) {
         if (r <= 0)  /* 0 means the strings were equal */
                 return r;
 
-        session_save(s);
-
-        session_send_changed(s, "Display", NULL);
+        (void) session_save(s);
+        (void) session_send_changed(s, "Display", NULL);
 
         return 1;
 }
@@ -1157,9 +1222,8 @@ int session_set_tty(Session *s, const char *tty) {
         if (r <= 0)  /* 0 means the strings were equal */
                 return r;
 
-        session_save(s);
-
-        session_send_changed(s, "TTY", NULL);
+        (void) session_save(s);
+        (void) session_send_changed(s, "TTY", NULL);
 
         return 1;
 }
@@ -1224,41 +1288,7 @@ static void session_remove_fifo(Session *s) {
 
         s->fifo_event_source = sd_event_source_unref(s->fifo_event_source);
         s->fifo_fd = safe_close(s->fifo_fd);
-
-        if (s->fifo_path) {
-                (void) unlink(s->fifo_path);
-                s->fifo_path = mfree(s->fifo_path);
-        }
-}
-
-static int session_dispatch_leader_pidfd(sd_event_source *es, int fd, uint32_t revents, void *userdata) {
-        Session *s = ASSERT_PTR(userdata);
-
-        assert(s->leader.fd == fd);
-        session_stop(s, /* force= */ false);
-
-        return 1;
-}
-
-int session_watch_pidfd(Session *s) {
-        int r;
-
-        assert(s);
-
-        if (s->leader.fd < 0)
-                return 0;
-
-        r = sd_event_add_io(s->manager->event, &s->leader_pidfd_event_source, s->leader.fd, EPOLLIN, session_dispatch_leader_pidfd, s);
-        if (r < 0)
-                return r;
-
-        r = sd_event_source_set_priority(s->leader_pidfd_event_source, SD_EVENT_PRIORITY_IMPORTANT);
-        if (r < 0)
-                return r;
-
-        (void) sd_event_source_set_description(s->leader_pidfd_event_source, "session-pidfd");
-
-        return 0;
+        s->fifo_path = unlink_and_free(s->fifo_path);
 }
 
 bool session_may_gc(Session *s, bool drop_not_started) {
@@ -1273,15 +1303,17 @@ bool session_may_gc(Session *s, bool drop_not_started) {
                 return true;
 
         r = pidref_is_alive(&s->leader);
+        if (r == -ESRCH)
+                /* Session has no leader. This is probably because the leader vanished before deserializing
+                 * pidfd from FD store. */
+                return true;
         if (r < 0)
-                log_debug_errno(r, "Unable to determine if leader PID " PID_FMT " is still alive, assuming not.", s->leader.pid);
+                log_debug_errno(r, "Unable to determine if leader PID " PID_FMT " is still alive, assuming not: %m", s->leader.pid);
         if (r > 0)
                 return false;
 
-        if (s->fifo_fd >= 0) {
-                if (pipe_eof(s->fifo_fd) <= 0)
-                        return false;
-        }
+        if (s->fifo_fd >= 0 && pipe_eof(s->fifo_fd) <= 0)
+                return false;
 
         if (s->scope_job) {
                 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
@@ -1561,7 +1593,7 @@ int session_set_controller(Session *s, const char *sender, bool force, bool prep
 
         session_release_controller(s, true);
         s->controller = TAKE_PTR(name);
-        session_save(s);
+        (void) session_save(s);
 
         return 0;
 }
@@ -1575,7 +1607,7 @@ void session_drop_controller(Session *s) {
         s->track = sd_bus_track_unref(s->track);
         session_set_type(s, s->original_type);
         session_release_controller(s, false);
-        session_save(s);
+        (void) session_save(s);
         session_restore_vt(s);
 }
 
@@ -1601,6 +1633,7 @@ DEFINE_STRING_TABLE_LOOKUP(session_type, SessionType);
 
 static const char* const session_class_table[_SESSION_CLASS_MAX] = {
         [SESSION_USER]        = "user",
+        [SESSION_USER_EARLY]  = "user-early",
         [SESSION_GREETER]     = "greeter",
         [SESSION_LOCK_SCREEN] = "lock-screen",
         [SESSION_BACKGROUND]  = "background",
index 8b63843ed8127211750bca0e7b052a305fa53794..a9c44b465f73fe7cf772ccd673ddea088cba2305 100644 (file)
@@ -20,14 +20,19 @@ typedef enum SessionState {
 } SessionState;
 
 typedef enum SessionClass {
-        SESSION_USER,
-        SESSION_GREETER,
-        SESSION_LOCK_SCREEN,
-        SESSION_BACKGROUND,
+        SESSION_USER,               /* A regular user session */
+        SESSION_USER_EARLY,         /* A user session, that is not ordered after systemd-user-sessions.service (i.e. for root) */
+        SESSION_GREETER,            /* A login greeter pseudo-session */
+        SESSION_LOCK_SCREEN,        /* A lock screen */
+        SESSION_BACKGROUND,         /* Things like cron jobs, which are non-interactive */
         _SESSION_CLASS_MAX,
         _SESSION_CLASS_INVALID = -EINVAL,
 } SessionClass;
 
+/* Whether we shall allow sessions of this class to run before 'systemd-user-sessions.service'. For now,
+ * there's only one class we allow this for. It's generally set for root sessions, but no one else. */
+#define SESSION_CLASS_IS_EARLY(class) ((class) == SESSION_USER_EARLY)
+
 typedef enum SessionType {
         SESSION_UNSPECIFIED,
         SESSION_TTY,
@@ -89,6 +94,8 @@ struct Session {
         int vtfd;
 
         PidRef leader;
+        bool leader_fd_saved; /* pidfd of leader uploaded to fdstore */
+        pid_t deserialized_pid; /* PID deserialized from state file (for verification when pidfd is used) */
         uint32_t audit_id;
 
         int fifo_fd;
@@ -144,7 +151,6 @@ void session_set_type(Session *s, SessionType t);
 int session_set_display(Session *s, const char *display);
 int session_set_tty(Session *s, const char *tty);
 int session_create_fifo(Session *s);
-int session_watch_pidfd(Session *s);
 int session_start(Session *s, sd_bus_message *properties, sd_bus_error *error);
 int session_stop(Session *s, bool force);
 int session_finalize(Session *s);
index c6133077f0c174c5390de449ef91b0fcd8a63a3d..4868eb48fb9f3dc33aa737b4a4f91facb09ee2df 100644 (file)
@@ -33,7 +33,7 @@
 #include "string-table.h"
 #include "strv.h"
 #include "tmpfile-util.h"
-#include "uid-alloc-range.h"
+#include "uid-classification.h"
 #include "unit-name.h"
 #include "user-util.h"
 
@@ -412,12 +412,14 @@ static int user_update_slice(User *u) {
                 { "IOWeight",   u->user_record->io_weight   },
         };
 
-        for (size_t i = 0; i < ELEMENTSOF(settings); i++)
-                if (settings[i].value != UINT64_MAX) {
-                        r = sd_bus_message_append(m, "(sv)", settings[i].name, "t", settings[i].value);
-                        if (r < 0)
-                                return bus_log_create_error(r);
-                }
+        FOREACH_ARRAY(st, settings, ELEMENTSOF(settings)) {
+                if (st->value == UINT64_MAX)
+                        continue;
+
+                r = sd_bus_message_append(m, "(sv)", st->name, "t", st->value);
+                if (r < 0)
+                        return bus_log_create_error(r);
+        }
 
         r = sd_bus_message_close_container(m);
         if (r < 0)
@@ -780,6 +782,9 @@ static int elect_display_compare(Session *s1, Session *s2) {
         if ((s1->class != SESSION_USER) != (s2->class != SESSION_USER))
                 return (s1->class != SESSION_USER) - (s2->class != SESSION_USER);
 
+        if ((s1->class != SESSION_USER_EARLY) != (s2->class != SESSION_USER_EARLY))
+                return (s1->class != SESSION_USER_EARLY) - (s2->class != SESSION_USER_EARLY);
+
         if ((s1->type == _SESSION_TYPE_INVALID) != (s2->type == _SESSION_TYPE_INVALID))
                 return (s1->type == _SESSION_TYPE_INVALID) - (s2->type == _SESSION_TYPE_INVALID);
 
index 965e2a4aefe9cfcb251b3fbdf5204f74a878dd01..7b6467d63a040175b9f50bf5c62b6ed5ceda44bc 100644 (file)
@@ -18,6 +18,7 @@
 #include "constants.h"
 #include "daemon-util.h"
 #include "device-util.h"
+#include "devnum-util.h"
 #include "dirent-util.h"
 #include "escape.h"
 #include "fd-util.h"
@@ -154,7 +155,7 @@ static Manager* manager_free(Manager *m) {
         if (m->unlink_nologin)
                 (void) unlink_or_warn("/run/nologin");
 
-        bus_verify_polkit_async_registry_free(m->polkit_registry);
+        hashmap_free(m->polkit_registry);
 
         sd_bus_flush_close_unref(m->bus);
         sd_event_unref(m->event);
@@ -191,13 +192,10 @@ static int manager_enumerate_devices(Manager *m) {
         if (r < 0)
                 return r;
 
-        FOREACH_DEVICE(e, d) {
-                int k;
+        r = 0;
 
-                k = manager_process_seat_device(m, d);
-                if (k < 0)
-                        r = k;
-        }
+        FOREACH_DEVICE(e, d)
+                RET_GATHER(r, manager_process_seat_device(m, d));
 
         return r;
 }
@@ -225,13 +223,10 @@ static int manager_enumerate_buttons(Manager *m) {
         if (r < 0)
                 return r;
 
-        FOREACH_DEVICE(e, d) {
-                int k;
+        r = 0;
 
-                k = manager_process_button_device(m, d);
-                if (k < 0)
-                        r = k;
-        }
+        FOREACH_DEVICE(e, d)
+                RET_GATHER(r, manager_process_button_device(m, d));
 
         return r;
 }
@@ -251,12 +246,11 @@ static int manager_enumerate_seats(Manager *m) {
                 if (errno == ENOENT)
                         return 0;
 
-                return log_error_errno(errno, "Failed to open /run/systemd/seats: %m");
+                return log_error_errno(errno, "Failed to open /run/systemd/seats/: %m");
         }
 
         FOREACH_DIRENT(de, d, return -errno) {
                 Seat *s;
-                int k;
 
                 if (!dirent_is_file(de))
                         continue;
@@ -269,9 +263,7 @@ static int manager_enumerate_seats(Manager *m) {
                         continue;
                 }
 
-                k = seat_load(s);
-                if (k < 0)
-                        r = k;
+                RET_GATHER(r, seat_load(s));
         }
 
         return r;
@@ -292,19 +284,21 @@ static int manager_enumerate_linger_users(Manager *m) {
         }
 
         FOREACH_DIRENT(de, d, return -errno) {
-                int k;
                 _cleanup_free_ char *n = NULL;
+                int k;
 
                 if (!dirent_is_file(de))
                         continue;
+
                 k = cunescape(de->d_name, 0, &n);
                 if (k < 0) {
-                        r = log_warning_errno(k, "Failed to unescape username '%s', ignoring: %m", de->d_name);
+                        RET_GATHER(r, log_warning_errno(k, "Failed to unescape username '%s', ignoring: %m", de->d_name));
                         continue;
                 }
+
                 k = manager_add_user_by_name(m, n, NULL);
                 if (k < 0)
-                        r = log_warning_errno(k, "Couldn't add lingering user %s, ignoring: %m", de->d_name);
+                        RET_GATHER(r, log_warning_errno(k, "Couldn't add lingering user %s, ignoring: %m", de->d_name));
         }
 
         return r;
@@ -312,7 +306,7 @@ static int manager_enumerate_linger_users(Manager *m) {
 
 static int manager_enumerate_users(Manager *m) {
         _cleanup_closedir_ DIR *d = NULL;
-        int r, k;
+        int r;
 
         assert(m);
 
@@ -325,148 +319,205 @@ static int manager_enumerate_users(Manager *m) {
                 if (errno == ENOENT)
                         return 0;
 
-                return log_error_errno(errno, "Failed to open /run/systemd/users: %m");
+                return log_error_errno(errno, "Failed to open /run/systemd/users/: %m");
         }
 
         FOREACH_DIRENT(de, d, return -errno) {
                 User *u;
                 uid_t uid;
+                int k;
 
                 if (!dirent_is_file(de))
                         continue;
 
                 k = parse_uid(de->d_name, &uid);
                 if (k < 0) {
-                        r = log_warning_errno(k, "Failed to parse filename /run/systemd/users/%s as UID.", de->d_name);
+                        RET_GATHER(r, log_warning_errno(k, "Failed to parse filename /run/systemd/users/%s as UID, ignoring: %m", de->d_name));
                         continue;
                 }
 
                 k = manager_add_user_by_uid(m, uid, &u);
                 if (k < 0) {
-                        r = log_warning_errno(k, "Failed to add user by file name %s, ignoring: %m", de->d_name);
+                        RET_GATHER(r, log_warning_errno(k, "Failed to add user by filename %s, ignoring: %m", de->d_name));
                         continue;
                 }
 
                 user_add_to_gc_queue(u);
 
-                k = user_load(u);
-                if (k < 0)
-                        r = k;
+                RET_GATHER(r, user_load(u));
         }
 
         return r;
 }
 
-static int parse_fdname(const char *fdname, char **session_id, dev_t *dev) {
+static int parse_fdname(const char *fdname, char **ret_session_id, dev_t *ret_devno) {
         _cleanup_strv_free_ char **parts = NULL;
         _cleanup_free_ char *id = NULL;
-        unsigned major, minor;
         int r;
 
+        assert(ret_session_id);
+        assert(ret_devno);
+
         parts = strv_split(fdname, "-");
         if (!parts)
                 return -ENOMEM;
-        if (strv_length(parts) != 5)
-                return -EINVAL;
 
-        if (!streq(parts[0], "session"))
+        if (_unlikely_(!streq(parts[0], "session")))
                 return -EINVAL;
 
         id = strdup(parts[1]);
         if (!id)
                 return -ENOMEM;
 
-        if (!streq(parts[2], "device"))
+        if (streq(parts[2], "leader")) {
+                *ret_session_id = TAKE_PTR(id);
+                *ret_devno = 0;
+
+                return 0;
+        }
+
+        if (_unlikely_(!streq(parts[2], "device")))
                 return -EINVAL;
 
+        unsigned major, minor;
+
         r = safe_atou(parts[3], &major);
         if (r < 0)
                 return r;
+
         r = safe_atou(parts[4], &minor);
         if (r < 0)
                 return r;
 
-        *dev = makedev(major, minor);
-        *session_id = TAKE_PTR(id);
+        *ret_session_id = TAKE_PTR(id);
+        *ret_devno = makedev(major, minor);
 
         return 0;
 }
 
-static int deliver_fd(Manager *m, const char *fdname, int fd) {
-        _cleanup_free_ char *id = NULL;
+static int deliver_session_device_fd(Session *s, const char *fdname, int fd, dev_t devno) {
         SessionDevice *sd;
         struct stat st;
-        Session *s;
-        dev_t dev;
-        int r;
 
-        assert(m);
+        assert(s);
+        assert(fdname);
         assert(fd >= 0);
-
-        r = parse_fdname(fdname, &id, &dev);
-        if (r < 0)
-                return log_debug_errno(r, "Failed to parse fd name %s: %m", fdname);
-
-        s = hashmap_get(m->sessions, id);
-        if (!s)
-                /* If the session doesn't exist anymore, the associated session device attached to this fd
-                 * doesn't either. Let's simply close this fd. */
-                return log_debug_errno(SYNTHETIC_ERRNO(ENXIO), "Failed to attach fd for unknown session: %s", id);
+        assert(devno > 0);
 
         if (fstat(fd, &st) < 0)
                 /* The device is allowed to go away at a random point, in which case fstat() failing is
                  * expected. */
-                return log_debug_errno(errno, "Failed to stat device fd for session %s: %m", id);
+                return log_debug_errno(errno, "Failed to stat device fd '%s' for session '%s': %m",
+                                       fdname, s->id);
 
-        if (!S_ISCHR(st.st_mode) || st.st_rdev != dev)
-                return log_debug_errno(SYNTHETIC_ERRNO(ENODEV), "Device fd doesn't point to the expected character device node");
+        if (!S_ISCHR(st.st_mode) || st.st_rdev != devno)
+                return log_debug_errno(SYNTHETIC_ERRNO(ENODEV),
+                                       "Device fd '%s' doesn't point to the expected character device node.",
+                                       fdname);
 
-        sd = hashmap_get(s->devices, &dev);
+        sd = hashmap_get(s->devices, &devno);
         if (!sd)
                 /* Weird, we got an fd for a session device which wasn't recorded in the session state
                  * file... */
-                return log_warning_errno(SYNTHETIC_ERRNO(ENODEV), "Got fd for missing session device [%u:%u] in session %s",
-                                         major(dev), minor(dev), s->id);
+                return log_warning_errno(SYNTHETIC_ERRNO(ENODEV),
+                                         "Got session device fd '%s' [" DEVNUM_FORMAT_STR "], but not present in session state.",
+                                         fdname, DEVNUM_FORMAT_VAL(devno));
 
-        log_debug("Attaching fd to session device [%u:%u] for session %s",
-                  major(dev), minor(dev), s->id);
+        log_debug("Attaching session device fd '%s' [" DEVNUM_FORMAT_STR "] to session '%s'.",
+                  fdname, DEVNUM_FORMAT_VAL(devno), s->id);
 
         session_device_attach_fd(sd, fd, s->was_active);
         return 0;
 }
 
-static int manager_attach_fds(Manager *m) {
-        _cleanup_strv_free_ char **fdnames = NULL;
-        int n;
+static int deliver_session_leader_fd_consume(Session *s, const char *fdname, int fd) {
+        _cleanup_(pidref_done) PidRef leader_fdstore = PIDREF_NULL;
+        int r;
 
-        /* Upon restart, PID1 will send us back all fds of session devices that we previously opened. Each
-         * file descriptor is associated with a given session. The session ids are passed through FDNAMES. */
+        assert(s);
+        assert(fdname);
+        assert(fd >= 0);
 
-        assert(m);
+        if (!pid_is_valid(s->deserialized_pid)) {
+                r = log_warning_errno(SYNTHETIC_ERRNO(EOWNERDEAD),
+                                      "Got leader pidfd for session '%s', but LEADER= is not set, refusing.",
+                                      s->id);
+                goto fail_close;
+        }
 
-        n = sd_listen_fds_with_names(true, &fdnames);
-        if (n < 0)
-                return log_warning_errno(n, "Failed to acquire passed fd list: %m");
-        if (n == 0)
-                return 0;
+        if (!s->leader_fd_saved)
+                log_warning("Got leader pidfd for session '%s', but not recorded in session state, proceeding anyway.",
+                            s->id);
+        else
+                assert(!pidref_is_set(&s->leader));
+
+        r = pidref_set_pidfd_take(&leader_fdstore, fd);
+        if (r < 0) {
+                if (r == -ESRCH)
+                        log_debug_errno(r, "Leader of session '%s' is gone while deserializing.", s->id);
+                else
+                        log_warning_errno(r, "Failed to create reference to leader of session '%s': %m", s->id);
+                goto fail_close;
+        }
 
-        for (int i = 0; i < n; i++) {
-                int fd = SD_LISTEN_FDS_START + i;
+        if (leader_fdstore.pid != s->deserialized_pid)
+                log_warning("Leader from pidfd (" PID_FMT ") doesn't match with LEADER=" PID_FMT " for session '%s', proceeding anyway.",
+                            leader_fdstore.pid, s->deserialized_pid, s->id);
 
-                if (deliver_fd(m, fdnames[i], fd) >= 0)
-                        continue;
+        r = session_set_leader_consume(s, TAKE_PIDREF(leader_fdstore));
+        if (r < 0)
+                return log_warning_errno(r, "Failed to attach leader pidfd for session '%s': %m", s->id);
+
+        return 0;
+
+fail_close:
+        close_and_notify_warn(fd, fdname);
+        return r;
+}
+
+static int manager_attach_session_fd_one_consume(Manager *m, const char *fdname, int fd) {
+        _cleanup_free_ char *id = NULL;
+        dev_t devno = 0; /* Explicit initialization to appease gcc */
+        Session *s;
+        int r;
 
-                /* Hmm, we couldn't deliver the fd to any session device object? If so, let's close the fd
-                 * and remove it from fdstore. */
-                close_and_notify_warn(fd, fdnames[i]);
+        assert(m);
+        assert(fdname);
+        assert(fd >= 0);
+
+        r = parse_fdname(fdname, &id, &devno);
+        if (r < 0) {
+                log_warning_errno(r, "Failed to parse stored fd name '%s': %m", fdname);
+                goto fail_close;
         }
 
-        return 0;
+        s = hashmap_get(m->sessions, id);
+        if (!s) {
+                /* If the session doesn't exist anymore, let's simply close this fd. */
+                r = log_debug_errno(SYNTHETIC_ERRNO(ENXIO),
+                                    "Cannot attach fd '%s' to unknown session '%s', ignoring.", fdname, id);
+                goto fail_close;
+        }
+
+        if (devno > 0) {
+                r = deliver_session_device_fd(s, fdname, fd, devno);
+                if (r < 0)
+                        goto fail_close;
+                return 0;
+        }
+
+        /* Takes ownership of fd on both success and failure */
+        return deliver_session_leader_fd_consume(s, fdname, fd);
+
+fail_close:
+        close_and_notify_warn(fd, fdname);
+        return r;
 }
 
 static int manager_enumerate_sessions(Manager *m) {
+        _cleanup_strv_free_ char **fdnames = NULL;
         _cleanup_closedir_ DIR *d = NULL;
-        int r = 0, k;
+        int r = 0, n;
 
         assert(m);
 
@@ -476,18 +527,19 @@ static int manager_enumerate_sessions(Manager *m) {
                 if (errno == ENOENT)
                         return 0;
 
-                return log_error_errno(errno, "Failed to open /run/systemd/sessions: %m");
+                return log_error_errno(errno, "Failed to open /run/systemd/sessions/: %m");
         }
 
         FOREACH_DIRENT(de, d, return -errno) {
-                struct Session *s;
+                Session *s;
+                int k;
 
                 if (!dirent_is_file(de))
                         continue;
 
                 k = manager_add_session(m, de->d_name, &s);
                 if (k < 0) {
-                        r = log_warning_errno(k, "Failed to add session by file name %s, ignoring: %m", de->d_name);
+                        RET_GATHER(r, log_warning_errno(k, "Failed to add session by filename %s, ignoring: %m", de->d_name));
                         continue;
                 }
 
@@ -495,12 +547,18 @@ static int manager_enumerate_sessions(Manager *m) {
 
                 k = session_load(s);
                 if (k < 0)
-                        r = k;
+                        RET_GATHER(r, log_warning_errno(k, "Failed to deserialize session '%s', ignoring: %m", s->id));
         }
 
-        /* We might be restarted and PID1 could have sent us back the session device fds we previously
-         * saved. */
-        (void) manager_attach_fds(m);
+        n = sd_listen_fds_with_names(/* unset_environment = */ true, &fdnames);
+        if (n < 0)
+                return log_error_errno(n, "Failed to acquire passed fd list: %m");
+
+        for (int i = 0; i < n; i++) {
+                int fd = SD_LISTEN_FDS_START + i;
+
+                RET_GATHER(r, manager_attach_session_fd_one_consume(m, fdnames[i], fd));
+        }
 
         return r;
 }
@@ -516,25 +574,23 @@ static int manager_enumerate_inhibitors(Manager *m) {
                 if (errno == ENOENT)
                         return 0;
 
-                return log_error_errno(errno, "Failed to open /run/systemd/inhibit: %m");
+                return log_error_errno(errno, "Failed to open /run/systemd/inhibit/: %m");
         }
 
         FOREACH_DIRENT(de, d, return -errno) {
-                int k;
                 Inhibitor *i;
+                int k;
 
                 if (!dirent_is_file(de))
                         continue;
 
                 k = manager_add_inhibitor(m, de->d_name, &i);
                 if (k < 0) {
-                        r = log_warning_errno(k, "Couldn't add inhibitor %s, ignoring: %m", de->d_name);
+                        RET_GATHER(r, log_warning_errno(k, "Couldn't add inhibitor %s, ignoring: %m", de->d_name));
                         continue;
                 }
 
-                k = inhibitor_load(i);
-                if (k < 0)
-                        r = k;
+                RET_GATHER(r, inhibitor_load(i));
         }
 
         return r;
@@ -987,7 +1043,7 @@ static int manager_dispatch_idle_action(sd_event_source *s, uint64_t t, void *us
                                 m->event,
                                 &m->idle_action_event_source,
                                 CLOCK_MONOTONIC,
-                                elapse, USEC_PER_SEC*30,
+                                elapse, MIN(USEC_PER_SEC*30, m->idle_action_usec), /* accuracy of 30s, but don't have an accuracy lower than the idle action timeout */
                                 manager_dispatch_idle_action, m);
                 if (r < 0)
                         return log_error_errno(r, "Failed to add idle event source: %m");
index b8da266e277cc21d486e77c47d06a0ddea2359ac..297689203b0a5d6a5318cc60278f2bb7f46f5070 100644 (file)
@@ -926,15 +926,20 @@ _public_ PAM_EXTERN int pam_sm_open_session(
         if (!logind_running())
                 goto success;
 
-        /* Make sure we don't enter a loop by talking to
-         * systemd-logind when it is actually waiting for the
-         * background to finish start-up. If the service is
-         * "systemd-user" we simply set XDG_RUNTIME_DIR and
+        r = pam_get_item_many(
+                        handle,
+                        PAM_SERVICE, &service,
+                        PAM_XDISPLAY, &display,
+                        PAM_TTY, &tty,
+                        PAM_RUSER, &remote_user,
+                        PAM_RHOST, &remote_host);
+        if (r != PAM_SUCCESS)
+                return pam_syslog_pam_error(handle, LOG_ERR, r, "Failed to get PAM items: @PAMERR@");
+
+        /* Make sure we don't enter a loop by talking to systemd-logind when it is actually waiting for the
+         * background to finish start-up. If the service is "systemd-user" we simply set XDG_RUNTIME_DIR and
          * leave. */
 
-        r = pam_get_item(handle, PAM_SERVICE, (const void**) &service);
-        if (!IN_SET(r, PAM_BAD_ITEM, PAM_SUCCESS))
-                return pam_syslog_pam_error(handle, LOG_ERR, r, "Failed to get PAM service: @PAMERR@");
         if (streq_ptr(service, "systemd-user")) {
                 char rt[STRLEN("/run/user/") + DECIMAL_STR_MAX(uid_t)];
 
@@ -948,35 +953,20 @@ _public_ PAM_EXTERN int pam_sm_open_session(
 
         /* Otherwise, we ask logind to create a session for us */
 
-        r = pam_get_item(handle, PAM_XDISPLAY, (const void**) &display);
-        if (!IN_SET(r, PAM_BAD_ITEM, PAM_SUCCESS))
-                return pam_syslog_pam_error(handle, LOG_ERR, r, "Failed to get PAM_XDISPLAY: @PAMERR@");
-        r = pam_get_item(handle, PAM_TTY, (const void**) &tty);
-        if (!IN_SET(r, PAM_BAD_ITEM, PAM_SUCCESS))
-                return pam_syslog_pam_error(handle, LOG_ERR, r, "Failed to get PAM_TTY: @PAMERR@");
-        r = pam_get_item(handle, PAM_RUSER, (const void**) &remote_user);
-        if (!IN_SET(r, PAM_BAD_ITEM, PAM_SUCCESS))
-                return pam_syslog_pam_error(handle, LOG_ERR, r, "Failed to get PAM_RUSER: @PAMERR@");
-        r = pam_get_item(handle, PAM_RHOST, (const void**) &remote_host);
-        if (!IN_SET(r, PAM_BAD_ITEM, PAM_SUCCESS))
-                return pam_syslog_pam_error(handle, LOG_ERR, r, "Failed to get PAM_RHOST: @PAMERR@");
-
         seat = getenv_harder(handle, "XDG_SEAT", NULL);
         cvtnr = getenv_harder(handle, "XDG_VTNR", NULL);
         type = getenv_harder(handle, "XDG_SESSION_TYPE", type_pam);
         class = getenv_harder(handle, "XDG_SESSION_CLASS", class_pam);
         desktop = getenv_harder(handle, "XDG_SESSION_DESKTOP", desktop_pam);
 
-        tty = strempty(tty);
-
-        if (strchr(tty, ':')) {
+        if (tty && strchr(tty, ':')) {
                 /* A tty with a colon is usually an X11 display, placed there to show up in utmp. We rearrange things
                  * and don't pretend that an X display was a tty. */
                 if (isempty(display))
                         display = tty;
                 tty = NULL;
 
-        } else if (streq(tty, "cron")) {
+        } else if (streq_ptr(tty, "cron")) {
                 /* cron is setting PAM_TTY to "cron" for some reason (the commit carries no information why, but
                  * probably because it wants to set it to something as pam_time/pam_access/… require PAM_TTY to be set
                  * (as they otherwise even try to update it!) — but cron doesn't actually allocate a TTY for its forked
@@ -985,10 +975,10 @@ _public_ PAM_EXTERN int pam_sm_open_session(
                 class = "background";
                 tty = NULL;
 
-        } else if (streq(tty, "ssh")) {
+        } else if (streq_ptr(tty, "ssh")) {
                 /* ssh has been setting PAM_TTY to "ssh" (for the same reason as cron does this, see above. For further
                  * details look for "PAM_TTY_KLUDGE" in the openssh sources). */
-                type ="tty";
+                type = "tty";
                 class = "user";
                 tty = NULL; /* This one is particularly sad, as this means that ssh sessions — even though usually
                              * associated with a pty — won't be tracked by their tty in logind. This is because ssh
@@ -996,7 +986,7 @@ _public_ PAM_EXTERN int pam_sm_open_session(
                              * much later (this is because it doesn't know yet if it needs one at all, as whether to
                              * register a pty or not is negotiated much later in the protocol). */
 
-        } else
+        } else if (tty)
                 /* Chop off leading /dev prefix that some clients specify, but others do not. */
                 tty = skip_dev_prefix(tty);
 
@@ -1021,7 +1011,9 @@ _public_ PAM_EXTERN int pam_sm_open_session(
                            !isempty(tty) ? "tty" : "unspecified";
 
         if (isempty(class))
-                class = streq(type, "unspecified") ? "background" : "user";
+                class = streq(type, "unspecified") ? "background" :
+                        ((IN_SET(user_record_disposition(ur), USER_INTRINSIC, USER_SYSTEM, USER_DYNAMIC) &&
+                         streq(type, "tty")) ? "user-early" : "user");
 
         remote = !isempty(remote_host) && !is_localhost(remote_host);
 
index 58a407d4513f0d5f215ea50009c718828806ac10..108fe0327b5ff0c95a0f5da6afb5473f2f4884e3 100644 (file)
@@ -96,7 +96,7 @@ static Manager* manager_unref(Manager *m) {
         sd_event_source_unref(m->nscd_cache_flush_event);
 #endif
 
-        bus_verify_polkit_async_registry_free(m->polkit_registry);
+        hashmap_free(m->polkit_registry);
 
         manager_varlink_done(m);
 
@@ -320,17 +320,6 @@ static bool check_idle(void *userdata) {
         return hashmap_isempty(m->machines);
 }
 
-static int manager_run(Manager *m) {
-        assert(m);
-
-        return bus_event_loop_with_idle(
-                        m->event,
-                        m->bus,
-                        "org.freedesktop.machine1",
-                        DEFAULT_EXIT_USEC,
-                        check_idle, m);
-}
-
 static int run(int argc, char *argv[]) {
         _cleanup_(manager_unrefp) Manager *m = NULL;
         int r;
@@ -363,16 +352,20 @@ static int run(int argc, char *argv[]) {
         if (r < 0)
                 return log_error_errno(r, "Failed to fully start up daemon: %m");
 
-        log_debug("systemd-machined running as pid "PID_FMT, getpid_cached());
         r = sd_notify(false, NOTIFY_READY);
         if (r < 0)
                 log_warning_errno(r, "Failed to send readiness notification, ignoring: %m");
 
-        r = manager_run(m);
+        r = bus_event_loop_with_idle(
+                        m->event,
+                        m->bus,
+                        "org.freedesktop.machine1",
+                        DEFAULT_EXIT_USEC,
+                        check_idle, m);
+        if (r < 0)
+                return log_error_errno(r, "Failed to run main loop: %m");
 
-        (void) sd_notify(false, NOTIFY_STOPPING);
-        log_debug("systemd-machined stopped as pid "PID_FMT, getpid_cached());
-        return r;
+        return 0;
 }
 
 DEFINE_MAIN_FUNCTION(run);
index 0439a9d7697ea7b35cb2858f89884d136263b355..540b6df4fc55b824411a45e8e44690c7726178af 100644 (file)
@@ -3,6 +3,8 @@
 #include <getopt.h>
 
 #include "build.h"
+#include "copy.h"
+#include "creds-util.h"
 #include "fd-util.h"
 #include "fs-util.h"
 #include "generator.h"
@@ -12,6 +14,7 @@
 #include "network-generator.h"
 #include "path-util.h"
 #include "proc-cmdline.h"
+#include "recurse-dir.h"
 
 #define NETWORKD_UNIT_DIRECTORY "/run/systemd/network"
 
@@ -122,6 +125,76 @@ static int context_save(Context *context) {
         return r;
 }
 
+static int pick_up_credentials(void) {
+        _cleanup_close_ int credential_dir_fd = -EBADF;
+        int r, ret = 0;
+
+        credential_dir_fd = open_credentials_dir();
+        if (IN_SET(credential_dir_fd, -ENXIO, -ENOENT)) /* Credential env var not set, or dir doesn't exist. */
+                return 0;
+        if (credential_dir_fd < 0)
+                return log_error_errno(credential_dir_fd, "Failed to open credentials directory: %m");
+
+        _cleanup_free_ DirectoryEntries *des = NULL;
+        r = readdir_all(credential_dir_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) {
+                static const struct {
+                        const char *credential_prefix;
+                        const char *filename_suffix;
+                } table[] = {
+                        { "network.link.",    ".link"    },
+                        { "network.netdev.",  ".netdev"  },
+                        { "network.network.", ".network" },
+                };
+
+                _cleanup_free_ char *fn = NULL;
+                struct dirent *de = *i;
+
+                if (de->d_type != DT_REG)
+                        continue;
+
+                FOREACH_ARRAY(t, table, ELEMENTSOF(table)) {
+                        const char *e = startswith(de->d_name, t->credential_prefix);
+
+                        if (e) {
+                                fn = strjoin(e, t->filename_suffix);
+                                if (!fn)
+                                        return log_oom();
+
+                                break;
+                        }
+                }
+
+                if (!fn)
+                        continue;
+
+                if (!filename_is_valid(fn)) {
+                        log_warning("Passed credential '%s' would result in invalid filename '%s', ignoring.", de->d_name, fn);
+                        continue;
+                }
+
+                _cleanup_free_ char *output = path_join(NETWORKD_UNIT_DIRECTORY, fn);
+                if (!output)
+                        return log_oom();
+
+                r = copy_file_at(
+                                credential_dir_fd, de->d_name,
+                                AT_FDCWD, output,
+                                /* open_flags= */ 0,
+                                0644,
+                                /* flags= */ 0);
+                if (r < 0)
+                        RET_GATHER(ret, log_warning_errno(r, "Failed to copy credential %s → file %s: %m", de->d_name, output));
+                else
+                        log_info("Installed %s from credential.", output);
+        }
+
+        return ret;
+}
+
 static int help(void) {
         printf("%s [OPTIONS...] [-- KERNEL_CMDLINE]\n"
                "  -h --help                       Show this help\n"
@@ -174,7 +247,7 @@ static int parse_argv(int argc, char *argv[]) {
 
 static int run(int argc, char *argv[]) {
         _cleanup_(context_clear) Context context = {};
-        int r;
+        int r, ret = 0;
 
         log_setup();
 
@@ -212,7 +285,10 @@ static int run(int argc, char *argv[]) {
         if (r < 0)
                 return log_warning_errno(r, "Failed to merge multiple command line options: %m");
 
-        return context_save(&context);
+        RET_GATHER(ret, context_save(&context));
+        RET_GATHER(ret, pick_up_credentials());
+
+        return ret;
 }
 
 DEFINE_MAIN_FUNCTION(run);
index 5f06948752beb292f70a7f7bb2eddb695dc98b6d..2708b3a5bf4aa39974fe0e72fea3970aa399d515 100644 (file)
@@ -67,8 +67,10 @@ sources = files(
         'networkd-nexthop.c',
         'networkd-queue.c',
         'networkd-radv.c',
-        'networkd-route-util.c',
         'networkd-route.c',
+        'networkd-route-metric.c',
+        'networkd-route-nexthop.c',
+        'networkd-route-util.c',
         'networkd-routing-policy-rule.c',
         'networkd-setlink.c',
         'networkd-speed-meter.c',
index 877898c06d468d470a4c0754d946c9940a56c332..4b9f19cc95cf6dc9079ddf796469d217ae54621a 100644 (file)
@@ -712,7 +712,7 @@ int config_parse_macsec_key(
 
         dest = a ? &a->sa : &b->sa;
 
-        r = unhexmem_full(rvalue, strlen(rvalue), true, &p, &l);
+        r = unhexmem_full(rvalue, SIZE_MAX, /* secure = */ true, &p, &l);
         if (r < 0) {
                 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse key. Ignoring assignment: %m");
                 return 0;
@@ -820,7 +820,7 @@ int config_parse_macsec_key_id(
         if (r < 0)
                 return log_oom();
 
-        r = unhexmem(rvalue, strlen(rvalue), &p, &l);
+        r = unhexmem(rvalue, &p, &l);
         if (r == -ENOMEM)
                 return log_oom();
         if (r < 0) {
index 57127a861abad48aa330f81655e8069105c3d4bd..894cec44cbb574923d9d4a3cf64041ec4604b280 100644 (file)
@@ -198,14 +198,6 @@ static void netdev_detach_from_manager(NetDev *netdev) {
 static NetDev *netdev_free(NetDev *netdev) {
         assert(netdev);
 
-        netdev_detach_from_manager(netdev);
-
-        free(netdev->filename);
-
-        free(netdev->description);
-        free(netdev->ifname);
-        condition_free_list(netdev->conditions);
-
         /* Invoke the per-kind done() destructor, but only if the state field is initialized. We conditionalize that
          * because we parse .netdev files twice: once to determine the kind (with a short, minimal NetDev structure
          * allocation, with no room for per-kind fields), and once to read the kind's properties (with a full,
@@ -218,6 +210,13 @@ static NetDev *netdev_free(NetDev *netdev) {
             NETDEV_VTABLE(netdev)->done)
                 NETDEV_VTABLE(netdev)->done(netdev);
 
+        netdev_detach_from_manager(netdev);
+
+        condition_free_list(netdev->conditions);
+        free(netdev->filename);
+        free(netdev->description);
+        free(netdev->ifname);
+
         return mfree(netdev);
 }
 
index b11fdbbd0de05741c015da6def203f4c7fc500c4..f333abc6c04f6aba61f052af2cb6f49b90a97a77 100644 (file)
@@ -289,7 +289,7 @@ int config_parse_port_range(
         VxLan *v = ASSERT_PTR(userdata);
         int r;
 
-        r = parse_ip_port_range(rvalue, &v->port_range.low, &v->port_range.high);
+        r = parse_ip_port_range(rvalue, &v->port_range.low, &v->port_range.high, /* allow_zero = */ false);
         if (r < 0)
                 log_syntax(unit, LOG_WARNING, filename, line, r,
                            "Failed to parse VXLAN port range '%s'. Port should be greater than 0 and less than 65535.", rvalue);
index 4c7d837c412c76609029830888c37707cd8c0ee1..57c3923c1b1764b755ba77c8ce64f010fe8be59c 100644 (file)
@@ -12,6 +12,7 @@
 #include "sd-resolve.h"
 
 #include "alloc-util.h"
+#include "creds-util.h"
 #include "dns-domain.h"
 #include "event-util.h"
 #include "fd-util.h"
@@ -25,6 +26,7 @@
 #include "networkd-util.h"
 #include "parse-helpers.h"
 #include "parse-util.h"
+#include "path-util.h"
 #include "random-util.h"
 #include "resolve-private.h"
 #include "string-util.h"
@@ -480,6 +482,8 @@ static int wireguard_decode_key_and_warn(
                 const char *lvalue) {
 
         _cleanup_(erase_and_freep) void *key = NULL;
+        _cleanup_(erase_and_freep) char *cred = NULL;
+        const char *cred_name;
         size_t len;
         int r;
 
@@ -493,10 +497,22 @@ static int wireguard_decode_key_and_warn(
                 return 0;
         }
 
-        if (!streq(lvalue, "PublicKey"))
+        cred_name = startswith(rvalue, "@");
+        if (cred_name) {
+                r = read_credential(cred_name, (void**) &cred, /* ret_size = */ NULL);
+                if (r == -ENOMEM)
+                        return log_oom();
+                if (r < 0) {
+                        log_syntax(unit, LOG_WARNING, filename, line, r,
+                                   "Failed to read credential for wireguard key (%s=), ignoring assignment: %m",
+                                   lvalue);
+                        return 0;
+                }
+
+        } else if (!streq(lvalue, "PublicKey"))
                 (void) warn_file_is_world_accessible(filename, NULL, unit, line);
 
-        r = unbase64mem_full(rvalue, strlen(rvalue), true, &key, &len);
+        r = unbase64mem_full(cred ?: rvalue, SIZE_MAX, /* secure = */ true, &key, &len);
         if (r == -ENOMEM)
                 return log_oom();
         if (r < 0) {
@@ -721,23 +737,39 @@ int config_parse_wireguard_endpoint(
                 void *data,
                 void *userdata) {
 
-        assert(filename);
-        assert(rvalue);
-        assert(userdata);
-
         Wireguard *w = WIREGUARD(userdata);
         _cleanup_(wireguard_peer_free_or_set_invalidp) WireguardPeer *peer = NULL;
-        _cleanup_free_ char *host = NULL;
-        union in_addr_union addr;
-        const char *p;
+        _cleanup_free_ char *cred = NULL;
+        const char *cred_name, *endpoint;
         uint16_t port;
-        int family, r;
+        int r;
+
+        assert(filename);
+        assert(rvalue);
 
         r = wireguard_peer_new_static(w, filename, section_line, &peer);
         if (r < 0)
                 return log_oom();
 
-        r = in_addr_port_ifindex_name_from_string_auto(rvalue, &family, &addr, &port, NULL, NULL);
+        cred_name = startswith(rvalue, "@");
+        if (cred_name) {
+                r = read_credential(cred_name, (void**) &cred, /* ret_size = */ NULL);
+                if (r == -ENOMEM)
+                        return log_oom();
+                if (r < 0) {
+                        log_syntax(unit, LOG_WARNING, filename, line, r,
+                                   "Failed to read credential for wireguard endpoint, ignoring assignment: %m");
+                        return 0;
+                }
+
+                endpoint = strstrip(cred);
+        } else
+                endpoint = rvalue;
+
+        union in_addr_union addr;
+        int family;
+
+        r = in_addr_port_ifindex_name_from_string_auto(endpoint, &family, &addr, &port, NULL, NULL);
         if (r >= 0) {
                 if (family == AF_INET)
                         peer->endpoint.in = (struct sockaddr_in) {
@@ -761,17 +793,23 @@ int config_parse_wireguard_endpoint(
                 return 0;
         }
 
-        p = strrchr(rvalue, ':');
+        _cleanup_free_ char *host = NULL;
+        const char *p;
+
+        p = strrchr(endpoint, ':');
         if (!p) {
                 log_syntax(unit, LOG_WARNING, filename, line, 0,
                            "Unable to find port of endpoint, ignoring assignment: %s",
-                           rvalue);
+                           rvalue); /* We log the original assignment instead of resolved credential here,
+                                       as the latter might be previously encrypted and we'd expose them in
+                                       unprotected logs otherwise. */
                 return 0;
         }
 
-        host = strndup(rvalue, p - rvalue);
+        host = strndup(endpoint, p - endpoint);
         if (!host)
                 return log_oom();
+        p++;
 
         if (!dns_name_is_valid(host)) {
                 log_syntax(unit, LOG_WARNING, filename, line, 0,
@@ -780,7 +818,6 @@ int config_parse_wireguard_endpoint(
                 return 0;
         }
 
-        p++;
         r = parse_ip_port(p, &port);
         if (r < 0) {
                 log_syntax(unit, LOG_WARNING, filename, line, r,
@@ -1078,6 +1115,55 @@ static int wireguard_peer_verify(WireguardPeer *peer) {
         return 0;
 }
 
+static int wireguard_read_default_key_cred(NetDev *netdev, const char *filename) {
+        Wireguard *w = WIREGUARD(netdev);
+        _cleanup_free_ char *config_name = NULL;
+        int r;
+
+        assert(filename);
+
+        r = path_extract_filename(filename, &config_name);
+        if (r < 0)
+                return log_netdev_error_errno(netdev, r,
+                                              "%s: Failed to extract config name, ignoring network device: %m",
+                                              filename);
+
+        char *p = endswith(config_name, ".netdev");
+        if (!p)
+                /* Fuzzer run? Then we just ignore this device. */
+                return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL),
+                                              "%s: Invalid netdev config name, refusing default key lookup.",
+                                              filename);
+        *p = '\0';
+
+        _cleanup_(erase_and_freep) char *cred = NULL;
+
+        r = read_credential(strjoina("network.wireguard.private.", config_name), (void**) &cred, /* ret_size = */ NULL);
+        if (r < 0)
+                return log_netdev_error_errno(netdev, r,
+                                              "%s: No private key specified and default key isn't available, "
+                                              "ignoring network device: %m",
+                                              filename);
+
+        _cleanup_(erase_and_freep) void *key = NULL;
+        size_t len;
+
+        r = unbase64mem_full(cred, SIZE_MAX, /* secure = */ true, &key, &len);
+        if (r < 0)
+                return log_netdev_error_errno(netdev, r,
+                                              "%s: No private key specified and default key cannot be parsed, "
+                                              "ignoring network device: %m",
+                                              filename);
+        if (len != WG_KEY_LEN)
+                return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL),
+                                              "%s: No private key specified and default key is invalid. "
+                                              "Ignoring network device.",
+                                              filename);
+
+        memcpy(w->private_key, key, WG_KEY_LEN);
+        return 0;
+}
+
 static int wireguard_verify(NetDev *netdev, const char *filename) {
         Wireguard *w = WIREGUARD(netdev);
         int r;
@@ -1088,10 +1174,11 @@ static int wireguard_verify(NetDev *netdev, const char *filename) {
                                               "Failed to read private key from %s. Ignoring network device.",
                                               w->private_key_file);
 
-        if (eqzero(w->private_key))
-                return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL),
-                                              "%s: Missing PrivateKey= or PrivateKeyFile=, "
-                                              "Ignoring network device.", filename);
+        if (eqzero(w->private_key)) {
+                r = wireguard_read_default_key_cred(netdev, filename);
+                if (r < 0)
+                        return r;
+        }
 
         LIST_FOREACH(peers, peer, w->peers) {
                 if (wireguard_peer_verify(peer) < 0) {
index 8e677cc01085b4b0ac706994cc2b071f942d9d4c..95e22cd9e3981181e60a9611e20307b0472146a6 100644 (file)
@@ -113,7 +113,7 @@ static int check_netns_match(void) {
                 {},
         };
 
-        r = json_dispatch(reply, dispatch_table, JSON_LOG, &id);
+        r = json_dispatch(reply, dispatch_table, JSON_LOG|JSON_ALLOW_EXTENSIONS, &id);
         if (r < 0)
                 return r;
 
index 8aee30e7266435f7f9ec4b7bfc41945caa28a739..64c84f42625a04b49d5ed812b6671b0e7737ba34 100644 (file)
@@ -380,14 +380,18 @@ static int dhcp4_request_route(Route *in, Link *link) {
                 route->priority = link->network->dhcp_route_metric;
         if (!route->table_set)
                 route->table = link_get_dhcp4_route_table(link);
-        if (route->mtu == 0)
-                route->mtu = link->network->dhcp_route_mtu;
-        if (route->quickack < 0)
-                route->quickack = link->network->dhcp_quickack;
-        if (route->initcwnd == 0)
-                route->initcwnd = link->network->dhcp_initial_congestion_window;
-        if (route->initrwnd == 0)
-                route->initrwnd = link->network->dhcp_advertised_receive_window;
+        r = route_metric_set(&route->metric, RTAX_MTU, link->network->dhcp_route_mtu);
+        if (r < 0)
+                return r;
+        r = route_metric_set(&route->metric, RTAX_INITCWND, link->network->dhcp_initial_congestion_window);
+        if (r < 0)
+                return r;
+        r = route_metric_set(&route->metric, RTAX_INITRWND, link->network->dhcp_advertised_receive_window);
+        if (r < 0)
+                return r;
+        r = route_metric_set(&route->metric, RTAX_QUICKACK, link->network->dhcp_quickack);
+        if (r < 0)
+                return r;
 
         if (route_get(NULL, link, route, &existing) < 0) /* This is a new route. */
                 link->dhcp4_configured = false;
index d972588b3ea0d5017ad8d93634ebb4699da7da0b..e62e57b7652d4587a8119f132f1d2bddef7312a0 100644 (file)
@@ -251,7 +251,7 @@ static int route_append_json(Route *route, JsonVariant **array) {
                                 JSON_BUILD_PAIR_UNSIGNED("Priority", route->priority),
                                 JSON_BUILD_PAIR_UNSIGNED("Table", route->table),
                                 JSON_BUILD_PAIR_STRING("TableString", table),
-                                JSON_BUILD_PAIR_UNSIGNED_NON_ZERO("MTU", route->mtu),
+                                JSON_BUILD_PAIR_UNSIGNED_NON_ZERO("MTU", route_metric_get(&route->metric, RTAX_MTU)),
                                 JSON_BUILD_PAIR_UNSIGNED("Preference", route->pref),
                                 JSON_BUILD_PAIR_UNSIGNED("Flags", route->flags),
                                 JSON_BUILD_PAIR_STRING("FlagsString", strempty(flags)),
index 26fa8a115d48441e48baf409badd616198e18036..ca3e4fbc9f7eaeaf1c5176235222d24579db331c 100644 (file)
@@ -1665,7 +1665,7 @@ static int link_carrier_lost(Link *link) {
                 usec = 5 * USEC_PER_SEC;
 
         else
-                /* Otherwise, use the currently set value. */
+                /* Otherwise, use the implied default value. */
                 usec = link->network->ignore_carrier_loss_usec;
 
         if (usec == USEC_INFINITY)
@@ -2000,20 +2000,18 @@ static int link_update_master(Link *link, sd_netlink_message *message) {
         if (master_ifindex == link->ifindex)
                 master_ifindex = 0;
 
-        if (master_ifindex == link->master_ifindex)
-                return 0;
-
-        if (link->master_ifindex == 0)
-                log_link_debug(link, "Attached to master interface: %i", master_ifindex);
-        else if (master_ifindex == 0)
-                log_link_debug(link, "Detached from master interface: %i", link->master_ifindex);
-        else
-                log_link_debug(link, "Master interface changed: %i %s %i", link->master_ifindex,
-                               special_glyph(SPECIAL_GLYPH_ARROW_RIGHT), master_ifindex);
-
-        link_drop_from_master(link);
+        if (master_ifindex != link->master_ifindex) {
+                if (link->master_ifindex == 0)
+                        log_link_debug(link, "Attached to master interface: %i", master_ifindex);
+                else if (master_ifindex == 0)
+                        log_link_debug(link, "Detached from master interface: %i", link->master_ifindex);
+                else
+                        log_link_debug(link, "Master interface changed: %i %s %i", link->master_ifindex,
+                                       special_glyph(SPECIAL_GLYPH_ARROW_RIGHT), master_ifindex);
 
-        link->master_ifindex = master_ifindex;
+                link_drop_from_master(link);
+                link->master_ifindex = master_ifindex;
+        }
 
         r = link_append_to_master(link);
         if (r < 0)
index e81905ad98a99deedde2a34a08e8199290b6c943..8933fc49776d6053b37c412ab6f9d90d293d3daf 100644 (file)
@@ -638,8 +638,7 @@ Manager* manager_free(Manager *m) {
         sd_device_monitor_unref(m->device_monitor);
 
         manager_varlink_done(m);
-
-        bus_verify_polkit_async_registry_free(m->polkit_registry);
+        hashmap_free(m->polkit_registry);
         sd_bus_flush_close_unref(m->bus);
 
         free(m->dynamic_timezone);
index 3f3569f44d5a2e27f5824bc52ae8ba04ae47526d..a2edfd0e79c0c8c807bca728e1e243271b6e8f4a 100644 (file)
@@ -100,6 +100,7 @@ struct Manager {
 
         FirewallContext *fw_ctx;
 
+        bool request_queued;
         OrderedSet *request_queue;
 
         Hashmap *tuntap_fds_by_name;
index 369e205a99a229b49c346772c1ac8a1dac761bf3..43c0cd3ad4aa355162711d1f2526e3c22483d528 100644 (file)
@@ -208,12 +208,15 @@ static int ndisc_request_route(Route *in, Link *link, sd_ndisc_router *rt) {
         ndisc_set_route_priority(link, route);
         if (!route->protocol_set)
                 route->protocol = RTPROT_RA;
-        if (route->quickack < 0)
-                route->quickack = link->network->ipv6_accept_ra_quickack;
-        if (route->mtu == 0)
-                route->mtu = mtu;
-        if (route->hop_limit == 0)
-                route->hop_limit = hop_limit;
+        r = route_metric_set(&route->metric, RTAX_MTU, mtu);
+        if (r < 0)
+                return r;
+        r = route_metric_set(&route->metric, RTAX_HOPLIMIT, hop_limit);
+        if (r < 0)
+                return r;
+        r = route_metric_set(&route->metric, RTAX_QUICKACK, link->network->ipv6_accept_ra_quickack);
+        if (r < 0)
+                return r;
 
         is_new = route_get(NULL, link, route, NULL) < 0;
 
index ab11047f7e807266233aad2a6adeb5179bcd00d0..fd1f26c79803c015d4a9abdb3b5062d9b9ba725e 100644 (file)
@@ -192,23 +192,23 @@ Route.Metric,                                config_parse_route_priority,
 Route.Scope,                                 config_parse_route_scope,                                 0,                             0
 Route.PreferredSource,                       config_parse_preferred_src,                               0,                             0
 Route.Table,                                 config_parse_route_table,                                 0,                             0
-Route.MTUBytes,                              config_parse_route_mtu,                                   AF_UNSPEC,                     0
-Route.GatewayOnLink,                         config_parse_route_boolean,                               0,                             0
-Route.GatewayOnlink,                         config_parse_route_boolean,                               0,                             0
+Route.GatewayOnLink,                         config_parse_route_gateway_onlink,                        0,                             0
+Route.GatewayOnlink,                         config_parse_route_gateway_onlink,                        0,                             0
 Route.IPv6Preference,                        config_parse_ipv6_route_preference,                       0,                             0
 Route.Protocol,                              config_parse_route_protocol,                              0,                             0
 Route.Type,                                  config_parse_route_type,                                  0,                             0
-Route.TCPRetransmissionTimeoutSec,           config_parse_route_tcp_rto,                               0,                             0
-Route.HopLimit,                              config_parse_route_hop_limit,                             0,                             0
-Route.InitialCongestionWindow,               config_parse_route_tcp_window,                            0,                             0
-Route.InitialAdvertisedReceiveWindow,        config_parse_route_tcp_window,                            0,                             0
-Route.TCPAdvertisedMaximumSegmentSize,       config_parse_tcp_advmss,                                  0,                             0
-Route.TCPCongestionControlAlgorithm,         config_parse_tcp_congestion,                              0,                             0
-Route.QuickAck,                              config_parse_route_boolean,                               0,                             0
-Route.FastOpenNoCookie,                      config_parse_route_boolean,                               0,                             0
-Route.TTLPropagate,                          config_parse_warn_compat,                                 DISABLED_LEGACY,               0
 Route.MultiPathRoute,                        config_parse_multipath_route,                             0,                             0
 Route.NextHop,                               config_parse_route_nexthop,                               0,                             0
+Route.MTUBytes,                              config_parse_route_metric_mtu,                            RTAX_MTU,                      0
+Route.TCPAdvertisedMaximumSegmentSize,       config_parse_route_metric_advmss,                         RTAX_ADVMSS,                   0
+Route.HopLimit,                              config_parse_route_metric_hop_limit,                      RTAX_HOPLIMIT,                 0
+Route.InitialCongestionWindow,               config_parse_route_metric_tcp_window,                     RTAX_INITCWND,                 0
+Route.TCPRetransmissionTimeoutSec,           config_parse_route_metric_tcp_rto,                        RTAX_RTO_MIN,                  0
+Route.InitialAdvertisedReceiveWindow,        config_parse_route_metric_tcp_window,                     RTAX_INITRWND,                 0
+Route.QuickAck,                              config_parse_route_metric_boolean,                        RTAX_QUICKACK,                 0
+Route.TCPCongestionControlAlgorithm,         config_parse_route_metric_tcp_congestion,                 RTAX_CC_ALGO,                  0
+Route.FastOpenNoCookie,                      config_parse_route_metric_boolean,                        RTAX_FASTOPEN_NO_COOKIE,       0
+Route.TTLPropagate,                          config_parse_warn_compat,                                 DISABLED_LEGACY,               0
 NextHop.Id,                                  config_parse_nexthop_id,                                  0,                             0
 NextHop.Gateway,                             config_parse_nexthop_gateway,                             0,                             0
 NextHop.Family,                              config_parse_nexthop_family,                              0,                             0
index a2b3580ced134802f72c08c88fe4d91927e18a1a..7606a2a95cd86f77ba757fb71444b0155c8ffdd7 100644 (file)
@@ -274,10 +274,8 @@ int network_verify(Network *network) {
                 network->ignore_carrier_loss_usec = USEC_INFINITY;
         }
 
-        if (!network->ignore_carrier_loss_set) {
-                network->ignore_carrier_loss_set = true;
+        if (!network->ignore_carrier_loss_set) /* Set implied default. */
                 network->ignore_carrier_loss_usec = network->configure_without_carrier ? USEC_INFINITY : 0;
-        }
 
         if (IN_SET(network->activation_policy, ACTIVATION_POLICY_DOWN, ACTIVATION_POLICY_ALWAYS_DOWN, ACTIVATION_POLICY_MANUAL)) {
                 if (network->required_for_online < 0 ||
index 1ab56a8ffdb79e8f43db748a52f99eefb1428818..8c963f03a32227f3e2099ddbceebae9e369e85f3 100644 (file)
@@ -269,6 +269,9 @@ static int nexthop_get_request_by_id(Manager *manager, uint32_t id, Request **re
 
         assert(manager);
 
+        if (id == 0)
+                return -EINVAL;
+
         req = ordered_set_get(
                         manager->request_queue,
                         &(Request) {
@@ -549,8 +552,38 @@ static int static_nexthop_handler(sd_netlink *rtnl, sd_netlink_message *m, Reque
         return 1;
 }
 
+int nexthop_is_ready(Manager *manager, uint32_t id, NextHop **ret) {
+        NextHop *nexthop;
+
+        assert(manager);
+
+        if (id == 0)
+                return -EINVAL;
+
+        if (nexthop_get_request_by_id(manager, id, NULL) >= 0)
+                goto not_ready;
+
+        if (nexthop_get_by_id(manager, id, &nexthop) < 0)
+                goto not_ready;
+
+        if (!nexthop_exists(nexthop))
+                goto not_ready;
+
+        if (ret)
+                *ret = nexthop;
+
+        return true;
+
+not_ready:
+        if (ret)
+                *ret = NULL;
+
+        return false;
+}
+
 static bool nexthop_is_ready_to_configure(Link *link, const NextHop *nexthop) {
         struct nexthop_grp *nhg;
+        int r;
 
         assert(link);
         assert(nexthop);
@@ -573,13 +606,9 @@ static bool nexthop_is_ready_to_configure(Link *link, const NextHop *nexthop) {
 
         /* All group members must be configured first. */
         HASHMAP_FOREACH(nhg, nexthop->group) {
-                NextHop *g;
-
-                if (nexthop_get_by_id(link->manager, nhg->id, &g) < 0)
-                        return false;
-
-                if (!nexthop_exists(g))
-                        return false;
+                r = nexthop_is_ready(link->manager, nhg->id, NULL);
+                if (r <= 0)
+                        return r;
         }
 
         return gateway_is_ready(link, FLAGS_SET(nexthop->flags, RTNH_F_ONLINK), nexthop->family, &nexthop->gw);
index 564b52532fa72fb7aff3a75b14f420f256bc5e20..62429670e7908fccfcb708d55f6f28079291a4f4 100644 (file)
@@ -51,6 +51,7 @@ void link_foreignize_nexthops(Link *link);
 int link_request_static_nexthops(Link *link, bool only_ipv4);
 
 int nexthop_get_by_id(Manager *manager, uint32_t id, NextHop **ret);
+int nexthop_is_ready(Manager *manager, uint32_t id, NextHop **ret);
 int manager_rtnl_process_nexthop(sd_netlink *rtnl, sd_netlink_message *message, Manager *m);
 int manager_build_nexthop_ids(Manager *manager);
 
index 6fafe42c0f857b9c76bdb21698bd332953f653c7..1678510d522950007028c6086c7d728677c65848 100644 (file)
@@ -168,6 +168,10 @@ static int request_new(
         if (req->counter)
                 (*req->counter)++;
 
+        /* If this is called in the ORDERED_SET_FOREACH() loop of manager_process_requests(), we need to
+         * exit from the loop, due to the limitation of the iteration on OrderedSet. */
+        manager->request_queued = true;
+
         if (ret)
                 *ret = req;
 
@@ -215,51 +219,49 @@ int link_queue_request_full(
 }
 
 int manager_process_requests(Manager *manager) {
+        Request *req;
         int r;
 
         assert(manager);
 
-        for (;;) {
-                bool processed = false;
-                Request *req;
-
-                ORDERED_SET_FOREACH(req, manager->request_queue) {
-                        _cleanup_(link_unrefp) Link *link = link_ref(req->link);
+        manager->request_queued = false;
 
-                        assert(req->process);
+        ORDERED_SET_FOREACH(req, manager->request_queue) {
+                _cleanup_(link_unrefp) Link *link = link_ref(req->link);
 
-                        if (req->waiting_reply)
-                                continue; /* Waiting for netlink reply. */
+                assert(req->process);
 
-                        /* Typically, requests send netlink message asynchronously. If there are many requests
-                         * queued, then this event may make reply callback queue in sd-netlink full. */
-                        if (netlink_get_reply_callback_count(manager->rtnl) >= REPLY_CALLBACK_COUNT_THRESHOLD ||
-                            netlink_get_reply_callback_count(manager->genl) >= REPLY_CALLBACK_COUNT_THRESHOLD ||
-                            fw_ctx_get_reply_callback_count(manager->fw_ctx) >= REPLY_CALLBACK_COUNT_THRESHOLD)
-                                return 0;
+                if (req->waiting_reply)
+                        continue; /* Waiting for netlink reply. */
 
-                        r = req->process(req, link, req->userdata);
-                        if (r == 0)
-                                continue;
+                /* Typically, requests send netlink message asynchronously. If there are many requests
+                 * queued, then this event may make reply callback queue in sd-netlink full. */
+                if (netlink_get_reply_callback_count(manager->rtnl) >= REPLY_CALLBACK_COUNT_THRESHOLD ||
+                    netlink_get_reply_callback_count(manager->genl) >= REPLY_CALLBACK_COUNT_THRESHOLD ||
+                    fw_ctx_get_reply_callback_count(manager->fw_ctx) >= REPLY_CALLBACK_COUNT_THRESHOLD)
+                        return 0;
 
-                        processed = true;
+                r = req->process(req, link, req->userdata);
+                if (r == 0) { /* The request is not ready. */
+                        if (manager->request_queued)
+                                break; /* a new request is queued during processing the request. */
+                        continue;
+                }
 
-                        /* If the request sends netlink message, e.g. for Address or so, the Request object
-                         * is referenced by the netlink slot, and will be detached later by its destroy callback.
-                         * Otherwise, e.g. for DHCP client or so, detach the request from queue now. */
-                        if (!req->waiting_reply)
-                                request_detach(manager, req);
+                /* If the request sends netlink message, e.g. for Address or so, the Request object is
+                 * referenced by the netlink slot, and will be detached later by its destroy callback.
+                 * Otherwise, e.g. for DHCP client or so, detach the request from queue now. */
+                if (!req->waiting_reply)
+                        request_detach(manager, req);
 
-                        if (r < 0 && link) {
-                                link_enter_failed(link);
-                                /* link_enter_failed() may remove multiple requests,
-                                 * hence we need to exit from the loop. */
-                                break;
-                        }
+                if (r < 0 && link) {
+                        link_enter_failed(link);
+                        /* link_enter_failed() may remove multiple requests,
+                         * hence we need to exit from the loop. */
+                        break;
                 }
 
-                /* When at least one request is processed, then another request may be ready now. */
-                if (!processed)
+                if (manager->request_queued)
                         break;
         }
 
diff --git a/src/network/networkd-route-metric.c b/src/network/networkd-route-metric.c
new file mode 100644 (file)
index 0000000..67841cb
--- /dev/null
@@ -0,0 +1,461 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "alloc-util.h"
+#include "netlink-util.h"
+#include "networkd-route.h"
+#include "networkd-route-metric.h"
+#include "parse-util.h"
+#include "string-util.h"
+
+void route_metric_done(RouteMetric *metric) {
+        assert(metric);
+
+        free(metric->metrics);
+        free(metric->metrics_set);
+        free(metric->tcp_congestion_control_algo);
+}
+
+int route_metric_copy(const RouteMetric *src, RouteMetric *dest) {
+        assert(src);
+        assert(dest);
+
+        dest->n_metrics = src->n_metrics;
+        if (src->n_metrics > 0) {
+                assert(src->n_metrics != 1);
+
+                dest->metrics = newdup(uint32_t, src->metrics, src->n_metrics);
+                if (!dest->metrics)
+                        return -ENOMEM;
+        } else
+                dest->metrics = NULL;
+
+        dest->n_metrics_set = src->n_metrics_set;
+        if (src->n_metrics_set > 0) {
+                assert(src->n_metrics_set != 1);
+
+                dest->metrics_set = newdup(bool, src->metrics_set, src->n_metrics_set);
+                if (!dest->metrics_set)
+                        return -ENOMEM;
+        } else
+                dest->metrics_set = NULL;
+
+        return free_and_strdup(&dest->tcp_congestion_control_algo, src->tcp_congestion_control_algo);
+}
+
+void route_metric_hash_func(const RouteMetric *metric, struct siphash *state) {
+        assert(metric);
+
+        siphash24_compress_typesafe(metric->n_metrics, state);
+        siphash24_compress_safe(metric->metrics, sizeof(uint32_t) * metric->n_metrics, state);
+        siphash24_compress_string(metric->tcp_congestion_control_algo, state);
+}
+
+int route_metric_compare_func(const RouteMetric *a, const RouteMetric *b) {
+        int r;
+
+        assert(a);
+        assert(b);
+
+        r = memcmp_nn(a->metrics, a->n_metrics * sizeof(uint32_t), b->metrics, b->n_metrics * sizeof(uint32_t));
+        if (r != 0)
+                return r;
+
+        return strcmp_ptr(a->tcp_congestion_control_algo, b->tcp_congestion_control_algo);
+}
+
+int route_metric_set_full(RouteMetric *metric, uint16_t attr, uint32_t value, bool force) {
+        assert(metric);
+
+        if (force) {
+                if (!GREEDY_REALLOC0(metric->metrics_set, attr + 1))
+                        return -ENOMEM;
+
+                metric->metrics_set[attr] = true;
+                metric->n_metrics_set = MAX(metric->n_metrics_set, (size_t) (attr + 1));
+        } else {
+                /* Do not override the values specified in conf parsers. */
+                if (metric->n_metrics_set > attr && metric->metrics_set[attr])
+                        return 0;
+        }
+
+        if (value != 0) {
+                if (!GREEDY_REALLOC0(metric->metrics, attr + 1))
+                        return -ENOMEM;
+
+                metric->metrics[attr] = value;
+                metric->n_metrics = MAX(metric->n_metrics, (size_t) (attr + 1));
+                return 0;
+        }
+
+        if (metric->n_metrics <= attr)
+                return 0;
+
+        metric->metrics[attr] = 0;
+
+        for (size_t i = metric->n_metrics; i > 0; i--)
+                if (metric->metrics[i-1] != 0) {
+                        metric->n_metrics = i;
+                        return 0;
+                }
+
+        metric->n_metrics = 0;
+        return 0;
+}
+
+static void route_metric_unset(RouteMetric *metric, uint16_t attr) {
+        assert(metric);
+
+        if (metric->n_metrics_set > attr)
+                metric->metrics_set[attr] = false;
+
+        assert_se(route_metric_set_full(metric, attr, 0, /* force = */ false) >= 0);
+}
+
+uint32_t route_metric_get(const RouteMetric *metric, uint16_t attr) {
+        assert(metric);
+
+        if (metric->n_metrics <= attr)
+                return 0;
+
+        return metric->metrics[attr];
+}
+
+int route_metric_set_netlink_message(const RouteMetric *metric, sd_netlink_message *m) {
+        int r;
+
+        assert(metric);
+        assert(m);
+
+        if (metric->n_metrics <= 0 && isempty(metric->tcp_congestion_control_algo))
+                return 0;
+
+        r = sd_netlink_message_open_container(m, RTA_METRICS);
+        if (r < 0)
+                return r;
+
+        for (size_t i = 0; i < metric->n_metrics; i++) {
+                if (i == RTAX_CC_ALGO)
+                        continue;
+
+                if (metric->metrics[i] == 0)
+                        continue;
+
+                r = sd_netlink_message_append_u32(m, i, metric->metrics[i]);
+                if (r < 0)
+                        return r;
+        }
+
+        if (!isempty(metric->tcp_congestion_control_algo)) {
+                r = sd_netlink_message_append_string(m, RTAX_CC_ALGO, metric->tcp_congestion_control_algo);
+                if (r < 0)
+                        return r;
+        }
+
+        r = sd_netlink_message_close_container(m);
+        if (r < 0)
+                return r;
+
+        return 0;
+}
+
+int route_metric_read_netlink_message(RouteMetric *metric, sd_netlink_message *m) {
+        _cleanup_free_ void *data = NULL;
+        size_t len;
+        int r;
+
+        assert(metric);
+        assert(m);
+
+        r = sd_netlink_message_read_data(m, RTA_METRICS, &len, &data);
+        if (r == -ENODATA)
+                return 0;
+        if (r < 0)
+                return log_warning_errno(r, "rtnl: Could not read RTA_METRICS attribute, ignoring: %m");
+
+        for (struct rtattr *rta = data; RTA_OK(rta, len); rta = RTA_NEXT(rta, len)) {
+                size_t rta_type = RTA_TYPE(rta);
+
+                if (rta_type == RTAX_CC_ALGO) {
+                        char *p = memdup_suffix0(RTA_DATA(rta), RTA_PAYLOAD(rta));
+                        if (!p)
+                                return log_oom();
+
+                        free_and_replace(metric->tcp_congestion_control_algo, p);
+
+                } else {
+                        if (RTA_PAYLOAD(rta) != sizeof(uint32_t))
+                                continue;
+
+                        r = route_metric_set(metric, rta_type, *(uint32_t*) RTA_DATA(rta));
+                        if (r < 0)
+                                return log_oom();
+                }
+        }
+
+        return 0;
+}
+
+static int config_parse_route_metric_advmss_impl(
+                const char *unit,
+                const char *filename,
+                unsigned line,
+                const char *section,
+                unsigned section_line,
+                const char *lvalue,
+                int ltype,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
+
+        uint32_t *val = ASSERT_PTR(data);
+        uint64_t u;
+        int r;
+
+        assert(rvalue);
+
+        r = parse_size(rvalue, 1024, &u);
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "Could not parse %s=, ignoring assignment: %s", lvalue, rvalue);
+                return 0;
+        }
+
+        if (u == 0 || u > UINT32_MAX) {
+                log_syntax(unit, LOG_WARNING, filename, line, 0,
+                           "Invalid %s=, ignoring assignment: %s", lvalue, rvalue);
+                return 0;
+        }
+
+        *val = (uint32_t) u;
+        return 1;
+}
+
+static int config_parse_route_metric_hop_limit_impl(
+                const char *unit,
+                const char *filename,
+                unsigned line,
+                const char *section,
+                unsigned section_line,
+                const char *lvalue,
+                int ltype,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
+
+        uint32_t k, *val = ASSERT_PTR(data);
+        int r;
+
+        assert(rvalue);
+
+        r = safe_atou32(rvalue, &k);
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "Could not parse %s=, ignoring assignment: %s", lvalue, rvalue);
+                return 0;
+        }
+        if (k == 0 || k > 255) {
+                log_syntax(unit, LOG_WARNING, filename, line, 0,
+                           "Invalid %s=, ignoring assignment: %s", lvalue, rvalue);
+                return 0;
+        }
+
+        *val = k;
+        return 1;
+}
+
+int config_parse_tcp_window(
+                const char *unit,
+                const char *filename,
+                unsigned line,
+                const char *section,
+                unsigned section_line,
+                const char *lvalue,
+                int ltype,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
+
+        uint32_t k, *val = ASSERT_PTR(data);
+        int r;
+
+        assert(rvalue);
+
+        r = safe_atou32(rvalue, &k);
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "Could not parse %s=, ignoring assignment: %s", lvalue, rvalue);
+                return 0;
+        }
+        if (k == 0 || k >= 1024) {
+                log_syntax(unit, LOG_WARNING, filename, line, 0,
+                           "Invalid %s=, ignoring assignment: %s", lvalue, rvalue);
+                return 0;
+        }
+
+        *val = k;
+        return 1;
+}
+
+static int config_parse_route_metric_tcp_rto_impl(
+                const char *unit,
+                const char *filename,
+                unsigned line,
+                const char *section,
+                unsigned section_line,
+                const char *lvalue,
+                int ltype,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
+
+        uint32_t *val = ASSERT_PTR(data);
+        usec_t usec;
+        int r;
+
+        assert(rvalue);
+
+        r = parse_sec(rvalue, &usec);
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "Failed to parse %s=, ignoring assignment: %s", lvalue, rvalue);
+                return 0;
+        }
+
+        if (!timestamp_is_set(usec) ||
+            DIV_ROUND_UP(usec, USEC_PER_MSEC) > UINT32_MAX) {
+                log_syntax(unit, LOG_WARNING, filename, line, 0,
+                           "Invalid %s=, ignoring assignment: %s", lvalue, rvalue);
+                return 0;
+        }
+
+        *val = (uint32_t) DIV_ROUND_UP(usec, USEC_PER_MSEC);
+        return 1;
+}
+
+static int config_parse_route_metric_boolean_impl(
+                const char *unit,
+                const char *filename,
+                unsigned line,
+                const char *section,
+                unsigned section_line,
+                const char *lvalue,
+                int ltype,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
+
+        uint32_t *val = ASSERT_PTR(data);
+        int r;
+
+        assert(rvalue);
+
+        r = parse_boolean(rvalue);
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "Could not parse %s=, ignoring assignment: %s", lvalue, rvalue);
+                return 0;
+        }
+
+        *val = r;
+        return 1;
+}
+
+#define DEFINE_CONFIG_PARSE_ROUTE_METRIC(name, parser)                  \
+        int config_parse_route_metric_##name(                           \
+                        const char *unit,                               \
+                        const char *filename,                           \
+                        unsigned line,                                  \
+                        const char *section,                            \
+                        unsigned section_line,                          \
+                        const char *lvalue,                             \
+                        int ltype,                                      \
+                        const char *rvalue,                             \
+                        void *data,                                     \
+                        void *userdata) {                               \
+                                                                        \
+                Network *network = ASSERT_PTR(userdata);                \
+                _cleanup_(route_free_or_set_invalidp) Route *route = NULL; \
+                uint16_t attr_type = ltype;                             \
+                int r;                                                  \
+                                                                        \
+                assert(filename);                                       \
+                assert(section);                                        \
+                assert(lvalue);                                         \
+                assert(rvalue);                                         \
+                                                                        \
+                r = route_new_static(network, filename, section_line, &route); \
+                if (r == -ENOMEM)                                       \
+                        return log_oom();                               \
+                if (r < 0) {                                            \
+                        log_syntax(unit, LOG_WARNING, filename, line, r, \
+                                   "Failed to allocate route, ignoring assignment: %m"); \
+                        return 0;                                       \
+                }                                                       \
+                                                                        \
+                if (isempty(rvalue)) {                                  \
+                        route_metric_unset(&route->metric, attr_type);  \
+                        TAKE_PTR(route);                                \
+                        return 0;                                       \
+                }                                                       \
+                                                                        \
+                uint32_t k;                                             \
+                r = parser(unit, filename, line, section, section_line, \
+                           lvalue, /* ltype = */ 0, rvalue,             \
+                           &k, userdata);                               \
+                if (r <= 0)                                             \
+                        return r;                                       \
+                                                                        \
+                if (route_metric_set_full(                              \
+                                &route->metric,                         \
+                                attr_type,                              \
+                                k,                                      \
+                                /* force = */ true) < 0)                \
+                        return log_oom();                               \
+                                                                        \
+                TAKE_PTR(route);                                        \
+                return 0;                                               \
+        }
+
+DEFINE_CONFIG_PARSE_ROUTE_METRIC(mtu, config_parse_mtu);
+DEFINE_CONFIG_PARSE_ROUTE_METRIC(advmss, config_parse_route_metric_advmss_impl);
+DEFINE_CONFIG_PARSE_ROUTE_METRIC(hop_limit, config_parse_route_metric_hop_limit_impl);
+DEFINE_CONFIG_PARSE_ROUTE_METRIC(tcp_window, config_parse_tcp_window);
+DEFINE_CONFIG_PARSE_ROUTE_METRIC(tcp_rto, config_parse_route_metric_tcp_rto_impl);
+DEFINE_CONFIG_PARSE_ROUTE_METRIC(boolean, config_parse_route_metric_boolean_impl);
+
+int config_parse_route_metric_tcp_congestion(
+                const char *unit,
+                const char *filename,
+                unsigned line,
+                const char *section,
+                unsigned section_line,
+                const char *lvalue,
+                int ltype,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
+
+        Network *network = ASSERT_PTR(userdata);
+        _cleanup_(route_free_or_set_invalidp) Route *route = NULL;
+        int r;
+
+        assert(filename);
+        assert(rvalue);
+
+        r = route_new_static(network, filename, section_line, &route);
+        if (r == -ENOMEM)
+                return log_oom();
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "Failed to allocate route, ignoring assignment: %m");
+                return 0;
+        }
+
+        r = config_parse_string(unit, filename, line, section, section_line, lvalue, 0,
+                                rvalue, &route->metric.tcp_congestion_control_algo, userdata);
+        if (r <= 0)
+                return r;
+
+        TAKE_PTR(route);
+        return 0;
+}
diff --git a/src/network/networkd-route-metric.h b/src/network/networkd-route-metric.h
new file mode 100644 (file)
index 0000000..1a3aaf2
--- /dev/null
@@ -0,0 +1,46 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include <inttypes.h>
+#include <stdbool.h>
+
+#include "sd-netlink.h"
+
+#include "conf-parser.h"
+#include "hash-funcs.h"
+
+typedef struct RouteMetric {
+        size_t n_metrics; /* maximum metric attr type with non-zero value */
+        uint32_t *metrics; /* RTAX_*, except for RTAX_CC_ALGO */
+
+        size_t n_metrics_set;
+        bool *metrics_set; /* used by conf parsers */
+
+        char *tcp_congestion_control_algo; /* RTAX_CC_ALGO */
+} RouteMetric;
+
+#define ROUTE_METRIC_NULL ((const RouteMetric) {})
+
+void route_metric_done(RouteMetric *metric);
+int route_metric_copy(const RouteMetric *src, RouteMetric *dest);
+
+void route_metric_hash_func(const RouteMetric *metric, struct siphash *state);
+int route_metric_compare_func(const RouteMetric *a, const RouteMetric *b);
+
+int route_metric_set_full(RouteMetric *metric, uint16_t attr, uint32_t value, bool force);
+static inline int route_metric_set(RouteMetric *metric, uint16_t attr, uint32_t value) {
+        return route_metric_set_full(metric, attr, value, false);
+}
+uint32_t route_metric_get(const RouteMetric *metric, uint16_t attr);
+
+int route_metric_set_netlink_message(const RouteMetric *metric, sd_netlink_message *m);
+int route_metric_read_netlink_message(RouteMetric *metric, sd_netlink_message *message);
+
+CONFIG_PARSER_PROTOTYPE(config_parse_route_metric_mtu);
+CONFIG_PARSER_PROTOTYPE(config_parse_route_metric_advmss);
+CONFIG_PARSER_PROTOTYPE(config_parse_route_metric_hop_limit);
+CONFIG_PARSER_PROTOTYPE(config_parse_route_metric_tcp_window);
+CONFIG_PARSER_PROTOTYPE(config_parse_route_metric_tcp_rto);
+CONFIG_PARSER_PROTOTYPE(config_parse_route_metric_boolean);
+CONFIG_PARSER_PROTOTYPE(config_parse_route_metric_tcp_congestion);
+CONFIG_PARSER_PROTOTYPE(config_parse_tcp_window);
diff --git a/src/network/networkd-route-nexthop.c b/src/network/networkd-route-nexthop.c
new file mode 100644 (file)
index 0000000..fbb8ee2
--- /dev/null
@@ -0,0 +1,182 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <linux/nexthop.h>
+
+#include "alloc-util.h"
+#include "extract-word.h"
+#include "netlink-util.h"
+#include "networkd-route.h"
+#include "networkd-route-nexthop.h"
+#include "parse-util.h"
+#include "string-util.h"
+
+int config_parse_route_nexthop(
+                const char *unit,
+                const char *filename,
+                unsigned line,
+                const char *section,
+                unsigned section_line,
+                const char *lvalue,
+                int ltype,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
+
+        Network *network = userdata;
+        _cleanup_(route_free_or_set_invalidp) Route *route = NULL;
+        uint32_t id;
+        int r;
+
+        assert(filename);
+        assert(section);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        r = route_new_static(network, filename, section_line, &route);
+        if (r == -ENOMEM)
+                return log_oom();
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "Failed to allocate route, ignoring assignment: %m");
+                return 0;
+        }
+
+        if (isempty(rvalue)) {
+                route->nexthop_id = 0;
+                TAKE_PTR(route);
+                return 0;
+        }
+
+        r = safe_atou32(rvalue, &id);
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse nexthop ID, ignoring assignment: %s", rvalue);
+                return 0;
+        }
+        if (id == 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid nexthop ID, ignoring assignment: %s", rvalue);
+                return 0;
+        }
+
+        route->nexthop_id = id;
+        TAKE_PTR(route);
+        return 0;
+}
+
+int config_parse_multipath_route(
+                const char *unit,
+                const char *filename,
+                unsigned line,
+                const char *section,
+                unsigned section_line,
+                const char *lvalue,
+                int ltype,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
+
+        _cleanup_(multipath_route_freep) MultipathRoute *m = NULL;
+        _cleanup_(route_free_or_set_invalidp) Route *route = NULL;
+        _cleanup_free_ char *word = NULL;
+        Network *network = userdata;
+        union in_addr_union a;
+        int family, r;
+        const char *p;
+        char *dev;
+
+        assert(filename);
+        assert(section);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        r = route_new_static(network, filename, section_line, &route);
+        if (r == -ENOMEM)
+                return log_oom();
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "Failed to allocate route, ignoring assignment: %m");
+                return 0;
+        }
+
+        if (isempty(rvalue)) {
+                route->multipath_routes = ordered_set_free_with_destructor(route->multipath_routes, multipath_route_free);
+                TAKE_PTR(route);
+                return 0;
+        }
+
+        m = new0(MultipathRoute, 1);
+        if (!m)
+                return log_oom();
+
+        p = rvalue;
+        r = extract_first_word(&p, &word, NULL, 0);
+        if (r == -ENOMEM)
+                return log_oom();
+        if (r <= 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "Invalid multipath route option, ignoring assignment: %s", rvalue);
+                return 0;
+        }
+
+        dev = strchr(word, '@');
+        if (dev) {
+                *dev++ = '\0';
+
+                r = parse_ifindex(dev);
+                if (r > 0)
+                        m->ifindex = r;
+                else {
+                        if (!ifname_valid_full(dev, IFNAME_VALID_ALTERNATIVE)) {
+                                log_syntax(unit, LOG_WARNING, filename, line, 0,
+                                           "Invalid interface name '%s' in %s=, ignoring: %s", dev, lvalue, rvalue);
+                                return 0;
+                        }
+
+                        m->ifname = strdup(dev);
+                        if (!m->ifname)
+                                return log_oom();
+                }
+        }
+
+        r = in_addr_from_string_auto(word, &family, &a);
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "Invalid multipath route gateway '%s', ignoring assignment: %m", rvalue);
+                return 0;
+        }
+        m->gateway.address = a;
+        m->gateway.family = family;
+
+        if (!isempty(p)) {
+                r = safe_atou32(p, &m->weight);
+                if (r < 0) {
+                        log_syntax(unit, LOG_WARNING, filename, line, r,
+                                   "Invalid multipath route weight, ignoring assignment: %s", p);
+                        return 0;
+                }
+                /* ip command takes weight in the range 1…255, while kernel takes the value in the
+                 * range 0…254. MultiPathRoute= setting also takes weight in the same range which ip
+                 * command uses, then networkd decreases by one and stores it to match the range which
+                 * kernel uses. */
+                if (m->weight == 0 || m->weight > 256) {
+                        log_syntax(unit, LOG_WARNING, filename, line, 0,
+                                   "Invalid multipath route weight, ignoring assignment: %s", p);
+                        return 0;
+                }
+                m->weight--;
+        }
+
+        r = ordered_set_ensure_put(&route->multipath_routes, NULL, m);
+        if (r == -ENOMEM)
+                return log_oom();
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "Failed to store multipath route, ignoring assignment: %m");
+                return 0;
+        }
+
+        TAKE_PTR(m);
+        TAKE_PTR(route);
+        return 0;
+}
diff --git a/src/network/networkd-route-nexthop.h b/src/network/networkd-route-nexthop.h
new file mode 100644 (file)
index 0000000..ae07696
--- /dev/null
@@ -0,0 +1,7 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include "conf-parser.h"
+
+CONFIG_PARSER_PROTOTYPE(config_parse_route_nexthop);
+CONFIG_PARSER_PROTOTYPE(config_parse_multipath_route);
index e7cc252243b7f76bcbb61e99c6acb49f062cfa28..0553a6634381f0e34b183b9d1ae3083ffc2e7925 100644 (file)
@@ -35,8 +35,6 @@ int route_new(Route **ret) {
                 .type = RTN_UNICAST,
                 .table = RT_TABLE_MAIN,
                 .lifetime_usec = USEC_INFINITY,
-                .quickack = -1,
-                .fast_open_no_cookie = -1,
                 .gateway_onlink = -1,
         };
 
@@ -45,7 +43,7 @@ int route_new(Route **ret) {
         return 0;
 }
 
-static int route_new_static(Network *network, const char *filename, unsigned section_line, Route **ret) {
+int route_new_static(Network *network, const char *filename, unsigned section_line, Route **ret) {
         _cleanup_(config_section_freep) ConfigSection *n = NULL;
         _cleanup_(route_freep) Route *route = NULL;
         int r;
@@ -103,11 +101,9 @@ Route *route_free(Route *route) {
                 set_remove(route->manager->routes, route);
 
         ordered_set_free_with_destructor(route->multipath_routes, multipath_route_free);
-
+        route_metric_done(&route->metric);
         sd_event_source_disable_unref(route->expire);
 
-        free(route->tcp_congestion_control_algo);
-
         return mfree(route);
 }
 
@@ -139,11 +135,7 @@ static void route_hash_func(const Route *route, struct siphash *state) {
                 siphash24_compress_typesafe(route->protocol, state);
                 siphash24_compress_typesafe(route->scope, state);
                 siphash24_compress_typesafe(route->type, state);
-
-                siphash24_compress_typesafe(route->initcwnd, state);
-                siphash24_compress_typesafe(route->initrwnd, state);
-
-                siphash24_compress_typesafe(route->advmss, state);
+                route_metric_hash_func(&route->metric, state);
                 siphash24_compress_typesafe(route->nexthop_id, state);
 
                 break;
@@ -221,15 +213,7 @@ static int route_compare_func(const Route *a, const Route *b) {
                 if (r != 0)
                         return r;
 
-                r = CMP(a->initcwnd, b->initcwnd);
-                if (r != 0)
-                        return r;
-
-                r = CMP(a->initrwnd, b->initrwnd);
-                if (r != 0)
-                        return r;
-
-                r = CMP(a->advmss, b->advmss);
+                r = route_metric_compare_func(&a->metric, &b->metric);
                 if (r != 0)
                         return r;
 
@@ -337,10 +321,10 @@ int route_dup(const Route *src, Route **ret) {
         dest->link = NULL;
         dest->manager = NULL;
         dest->multipath_routes = NULL;
+        dest->metric = ROUTE_METRIC_NULL;
         dest->expire = NULL;
-        dest->tcp_congestion_control_algo = NULL;
 
-        r = free_and_strdup(&dest->tcp_congestion_control_algo, src->tcp_congestion_control_algo);
+        r = route_metric_copy(&src->metric, &dest->metric);
         if (r < 0)
                 return r;
 
@@ -1187,65 +1171,8 @@ static int route_configure(const Route *route, uint32_t lifetime_sec, Link *link
                         return r;
         }
 
-        r = sd_netlink_message_open_container(m, RTA_METRICS);
-        if (r < 0)
-                return r;
-
-        if (route->mtu > 0) {
-                r = sd_netlink_message_append_u32(m, RTAX_MTU, route->mtu);
-                if (r < 0)
-                        return r;
-        }
-
-        if (route->initcwnd > 0) {
-                r = sd_netlink_message_append_u32(m, RTAX_INITCWND, route->initcwnd);
-                if (r < 0)
-                        return r;
-        }
-
-        if (route->initrwnd > 0) {
-                r = sd_netlink_message_append_u32(m, RTAX_INITRWND, route->initrwnd);
-                if (r < 0)
-                        return r;
-        }
-
-        if (route->quickack >= 0) {
-                r = sd_netlink_message_append_u32(m, RTAX_QUICKACK, route->quickack);
-                if (r < 0)
-                        return r;
-        }
-
-        if (route->fast_open_no_cookie >= 0) {
-                r = sd_netlink_message_append_u32(m, RTAX_FASTOPEN_NO_COOKIE, route->fast_open_no_cookie);
-                if (r < 0)
-                        return r;
-        }
-
-        if (route->advmss > 0) {
-                r = sd_netlink_message_append_u32(m, RTAX_ADVMSS, route->advmss);
-                if (r < 0)
-                        return r;
-        }
-
-        if (!isempty(route->tcp_congestion_control_algo)) {
-                r = sd_netlink_message_append_string(m, RTAX_CC_ALGO, route->tcp_congestion_control_algo);
-                if (r < 0)
-                        return r;
-        }
-
-        if (route->hop_limit > 0) {
-                r = sd_netlink_message_append_u32(m, RTAX_HOPLIMIT, route->hop_limit);
-                if (r < 0)
-                        return r;
-        }
-
-        if (route->tcp_rto_usec > 0) {
-                r = sd_netlink_message_append_u32(m, RTAX_RTO_MIN, DIV_ROUND_UP(route->tcp_rto_usec, USEC_PER_MSEC));
-                if (r < 0)
-                        return r;
-        }
-
-        r = sd_netlink_message_close_container(m);
+        /* metrics */
+        r = route_metric_set_netlink_message(&route->metric, m);
         if (r < 0)
                 return r;
 
@@ -1277,20 +1204,14 @@ static int route_is_ready_to_configure(const Route *route, Link *link) {
                 struct nexthop_grp *nhg;
                 NextHop *nh;
 
-                if (nexthop_get_by_id(link->manager, route->nexthop_id, &nh) < 0)
-                        return false;
-
-                if (!nexthop_exists(nh))
-                        return false;
+                r = nexthop_is_ready(link->manager, route->nexthop_id, &nh);
+                if (r <= 0)
+                        return r;
 
                 HASHMAP_FOREACH(nhg, nh->group) {
-                        NextHop *g;
-
-                        if (nexthop_get_by_id(link->manager, nhg->id, &g) < 0)
-                                return false;
-
-                        if (!nexthop_exists(g))
-                                return false;
+                        r = nexthop_is_ready(link->manager, nhg->id, NULL);
+                        if (r <= 0)
+                                return r;
                 }
         }
 
@@ -1857,36 +1778,9 @@ int manager_rtnl_process_route(sd_netlink *rtnl, sd_netlink_message *message, Ma
                 return 0;
         }
 
-        r = sd_netlink_message_enter_container(message, RTA_METRICS);
-        if (r < 0 && r != -ENODATA) {
-                log_link_error_errno(link, r, "rtnl: Could not enter RTA_METRICS container, ignoring: %m");
+        /* metrics */
+        if (route_metric_read_netlink_message(&tmp->metric, message) < 0)
                 return 0;
-        }
-        if (r >= 0) {
-                r = sd_netlink_message_read_u32(message, RTAX_INITCWND, &tmp->initcwnd);
-                if (r < 0 && r != -ENODATA) {
-                        log_link_warning_errno(link, r, "rtnl: received route message with invalid initcwnd, ignoring: %m");
-                        return 0;
-                }
-
-                r = sd_netlink_message_read_u32(message, RTAX_INITRWND, &tmp->initrwnd);
-                if (r < 0 && r != -ENODATA) {
-                        log_link_warning_errno(link, r, "rtnl: received route message with invalid initrwnd, ignoring: %m");
-                        return 0;
-                }
-
-                r = sd_netlink_message_read_u32(message, RTAX_ADVMSS, &tmp->advmss);
-                if (r < 0 && r != -ENODATA) {
-                        log_link_warning_errno(link, r, "rtnl: received route message with invalid advmss, ignoring: %m");
-                        return 0;
-                }
-
-                r = sd_netlink_message_exit_container(message);
-                if (r < 0) {
-                        log_link_error_errno(link, r, "rtnl: Could not exit from RTA_METRICS container, ignoring: %m");
-                        return 0;
-                }
-        }
 
         r = sd_netlink_message_read_data(message, RTA_MULTIPATH, &rta_len, &rta_multipath);
         if (r < 0 && r != -ENODATA) {
@@ -1936,7 +1830,7 @@ int manager_rtnl_process_route(sd_netlink *rtnl, sd_netlink_message *message, Ma
 }
 
 int network_add_ipv4ll_route(Network *network) {
-        _cleanup_(route_free_or_set_invalidp) Route *n = NULL;
+        _cleanup_(route_free_or_set_invalidp) Route *route = NULL;
         unsigned section_line;
         int r;
 
@@ -1950,28 +1844,28 @@ int network_add_ipv4ll_route(Network *network) {
                 return r;
 
         /* IPv4LLRoute= is in [Network] section. */
-        r = route_new_static(network, network->filename, section_line, &n);
+        r = route_new_static(network, network->filename, section_line, &route);
         if (r < 0)
                 return r;
 
-        r = in_addr_from_string(AF_INET, "169.254.0.0", &n->dst);
+        r = in_addr_from_string(AF_INET, "169.254.0.0", &route->dst);
         if (r < 0)
                 return r;
 
-        n->family = AF_INET;
-        n->dst_prefixlen = 16;
-        n->scope = RT_SCOPE_LINK;
-        n->scope_set = true;
-        n->table_set = true;
-        n->priority = IPV4LL_ROUTE_METRIC;
-        n->protocol = RTPROT_STATIC;
+        route->family = AF_INET;
+        route->dst_prefixlen = 16;
+        route->scope = RT_SCOPE_LINK;
+        route->scope_set = true;
+        route->table_set = true;
+        route->priority = IPV4LL_ROUTE_METRIC;
+        route->protocol = RTPROT_STATIC;
 
-        TAKE_PTR(n);
+        TAKE_PTR(route);
         return 0;
 }
 
 int network_add_default_route_on_device(Network *network) {
-        _cleanup_(route_free_or_set_invalidp) Route *n = NULL;
+        _cleanup_(route_free_or_set_invalidp) Route *route = NULL;
         unsigned section_line;
         int r;
 
@@ -1985,16 +1879,16 @@ int network_add_default_route_on_device(Network *network) {
                 return r;
 
         /* DefaultRouteOnDevice= is in [Network] section. */
-        r = route_new_static(network, network->filename, section_line, &n);
+        r = route_new_static(network, network->filename, section_line, &route);
         if (r < 0)
                 return r;
 
-        n->family = AF_INET;
-        n->scope = RT_SCOPE_LINK;
-        n->scope_set = true;
-        n->protocol = RTPROT_STATIC;
+        route->family = AF_INET;
+        route->scope = RT_SCOPE_LINK;
+        route->scope_set = true;
+        route->protocol = RTPROT_STATIC;
 
-        TAKE_PTR(n);
+        TAKE_PTR(route);
         return 0;
 }
 
@@ -2011,7 +1905,7 @@ int config_parse_gateway(
                 void *userdata) {
 
         Network *network = userdata;
-        _cleanup_(route_free_or_set_invalidp) Route *n = NULL;
+        _cleanup_(route_free_or_set_invalidp) Route *route = NULL;
         int r;
 
         assert(filename);
@@ -2022,7 +1916,7 @@ int config_parse_gateway(
 
         if (streq(section, "Network")) {
                 /* we are not in an Route section, so use line number instead */
-                r = route_new_static(network, filename, line, &n);
+                r = route_new_static(network, filename, line, &route);
                 if (r == -ENOMEM)
                         return log_oom();
                 if (r < 0) {
@@ -2031,7 +1925,7 @@ int config_parse_gateway(
                         return 0;
                 }
         } else {
-                r = route_new_static(network, filename, section_line, &n);
+                r = route_new_static(network, filename, section_line, &route);
                 if (r == -ENOMEM)
                         return log_oom();
                 if (r < 0) {
@@ -2041,43 +1935,43 @@ int config_parse_gateway(
                 }
 
                 if (isempty(rvalue)) {
-                        n->gateway_from_dhcp_or_ra = false;
-                        n->gw_family = AF_UNSPEC;
-                        n->gw = IN_ADDR_NULL;
-                        TAKE_PTR(n);
+                        route->gateway_from_dhcp_or_ra = false;
+                        route->gw_family = AF_UNSPEC;
+                        route->gw = IN_ADDR_NULL;
+                        TAKE_PTR(route);
                         return 0;
                 }
 
                 if (streq(rvalue, "_dhcp")) {
-                        n->gateway_from_dhcp_or_ra = true;
-                        TAKE_PTR(n);
+                        route->gateway_from_dhcp_or_ra = true;
+                        TAKE_PTR(route);
                         return 0;
                 }
 
                 if (streq(rvalue, "_dhcp4")) {
-                        n->gw_family = AF_INET;
-                        n->gateway_from_dhcp_or_ra = true;
-                        TAKE_PTR(n);
+                        route->gw_family = AF_INET;
+                        route->gateway_from_dhcp_or_ra = true;
+                        TAKE_PTR(route);
                         return 0;
                 }
 
                 if (streq(rvalue, "_ipv6ra")) {
-                        n->gw_family = AF_INET6;
-                        n->gateway_from_dhcp_or_ra = true;
-                        TAKE_PTR(n);
+                        route->gw_family = AF_INET6;
+                        route->gateway_from_dhcp_or_ra = true;
+                        TAKE_PTR(route);
                         return 0;
                 }
         }
 
-        r = in_addr_from_string_auto(rvalue, &n->gw_family, &n->gw);
+        r = in_addr_from_string_auto(rvalue, &route->gw_family, &route->gw);
         if (r < 0) {
                 log_syntax(unit, LOG_WARNING, filename, line, r,
                            "Invalid %s='%s', ignoring assignment: %m", lvalue, rvalue);
                 return 0;
         }
 
-        n->gateway_from_dhcp_or_ra = false;
-        TAKE_PTR(n);
+        route->gateway_from_dhcp_or_ra = false;
+        TAKE_PTR(route);
         return 0;
 }
 
@@ -2094,7 +1988,7 @@ int config_parse_preferred_src(
                 void *userdata) {
 
         Network *network = userdata;
-        _cleanup_(route_free_or_set_invalidp) Route *n = NULL;
+        _cleanup_(route_free_or_set_invalidp) Route *route = NULL;
         int r;
 
         assert(filename);
@@ -2103,7 +1997,7 @@ int config_parse_preferred_src(
         assert(rvalue);
         assert(data);
 
-        r = route_new_static(network, filename, section_line, &n);
+        r = route_new_static(network, filename, section_line, &route);
         if (r == -ENOMEM)
                 return log_oom();
         if (r < 0) {
@@ -2112,17 +2006,17 @@ int config_parse_preferred_src(
                 return 0;
         }
 
-        if (n->family == AF_UNSPEC)
-                r = in_addr_from_string_auto(rvalue, &n->family, &n->prefsrc);
+        if (route->family == AF_UNSPEC)
+                r = in_addr_from_string_auto(rvalue, &route->family, &route->prefsrc);
         else
-                r = in_addr_from_string(n->family, rvalue, &n->prefsrc);
+                r = in_addr_from_string(route->family, rvalue, &route->prefsrc);
         if (r < 0) {
                 log_syntax(unit, LOG_WARNING, filename, line, EINVAL,
                            "Invalid %s='%s', ignoring assignment: %m", lvalue, rvalue);
                 return 0;
         }
 
-        TAKE_PTR(n);
+        TAKE_PTR(route);
         return 0;
 }
 
@@ -2139,7 +2033,7 @@ int config_parse_destination(
                 void *userdata) {
 
         Network *network = userdata;
-        _cleanup_(route_free_or_set_invalidp) Route *n = NULL;
+        _cleanup_(route_free_or_set_invalidp) Route *route = NULL;
         union in_addr_union *buffer;
         unsigned char *prefixlen;
         int r;
@@ -2150,7 +2044,7 @@ int config_parse_destination(
         assert(rvalue);
         assert(data);
 
-        r = route_new_static(network, filename, section_line, &n);
+        r = route_new_static(network, filename, section_line, &route);
         if (r == -ENOMEM)
                 return log_oom();
         if (r < 0) {
@@ -2160,27 +2054,27 @@ int config_parse_destination(
         }
 
         if (streq(lvalue, "Destination")) {
-                buffer = &n->dst;
-                prefixlen = &n->dst_prefixlen;
+                buffer = &route->dst;
+                prefixlen = &route->dst_prefixlen;
         } else if (streq(lvalue, "Source")) {
-                buffer = &n->src;
-                prefixlen = &n->src_prefixlen;
+                buffer = &route->src;
+                prefixlen = &route->src_prefixlen;
         } else
                 assert_not_reached();
 
-        if (n->family == AF_UNSPEC)
-                r = in_addr_prefix_from_string_auto(rvalue, &n->family, buffer, prefixlen);
+        if (route->family == AF_UNSPEC)
+                r = in_addr_prefix_from_string_auto(rvalue, &route->family, buffer, prefixlen);
         else
-                r = in_addr_prefix_from_string(rvalue, n->family, buffer, prefixlen);
+                r = in_addr_prefix_from_string(rvalue, route->family, buffer, prefixlen);
         if (r < 0) {
                 log_syntax(unit, LOG_WARNING, filename, line, EINVAL,
                            "Invalid %s='%s', ignoring assignment: %m", lvalue, rvalue);
                 return 0;
         }
 
-        (void) in_addr_mask(n->family, buffer, *prefixlen);
+        (void) in_addr_mask(route->family, buffer, *prefixlen);
 
-        TAKE_PTR(n);
+        TAKE_PTR(route);
         return 0;
 }
 
@@ -2197,7 +2091,7 @@ int config_parse_route_priority(
                 void *userdata) {
 
         Network *network = userdata;
-        _cleanup_(route_free_or_set_invalidp) Route *n = NULL;
+        _cleanup_(route_free_or_set_invalidp) Route *route = NULL;
         int r;
 
         assert(filename);
@@ -2206,7 +2100,7 @@ int config_parse_route_priority(
         assert(rvalue);
         assert(data);
 
-        r = route_new_static(network, filename, section_line, &n);
+        r = route_new_static(network, filename, section_line, &route);
         if (r == -ENOMEM)
                 return log_oom();
         if (r < 0) {
@@ -2215,15 +2109,15 @@ int config_parse_route_priority(
                 return 0;
         }
 
-        r = safe_atou32(rvalue, &n->priority);
+        r = safe_atou32(rvalue, &route->priority);
         if (r < 0) {
                 log_syntax(unit, LOG_WARNING, filename, line, r,
                            "Could not parse route priority \"%s\", ignoring assignment: %m", rvalue);
                 return 0;
         }
 
-        n->priority_set = true;
-        TAKE_PTR(n);
+        route->priority_set = true;
+        TAKE_PTR(route);
         return 0;
 }
 
@@ -2240,7 +2134,7 @@ int config_parse_route_scope(
                 void *userdata) {
 
         Network *network = userdata;
-        _cleanup_(route_free_or_set_invalidp) Route *n = NULL;
+        _cleanup_(route_free_or_set_invalidp) Route *route = NULL;
         int r;
 
         assert(filename);
@@ -2249,7 +2143,7 @@ int config_parse_route_scope(
         assert(rvalue);
         assert(data);
 
-        r = route_new_static(network, filename, section_line, &n);
+        r = route_new_static(network, filename, section_line, &route);
         if (r == -ENOMEM)
                 return log_oom();
         if (r < 0) {
@@ -2264,62 +2158,9 @@ int config_parse_route_scope(
                 return 0;
         }
 
-        n->scope = r;
-        n->scope_set = true;
-        TAKE_PTR(n);
-        return 0;
-}
-
-int config_parse_route_nexthop(
-                const char *unit,
-                const char *filename,
-                unsigned line,
-                const char *section,
-                unsigned section_line,
-                const char *lvalue,
-                int ltype,
-                const char *rvalue,
-                void *data,
-                void *userdata) {
-
-        Network *network = userdata;
-        _cleanup_(route_free_or_set_invalidp) Route *n = NULL;
-        uint32_t id;
-        int r;
-
-        assert(filename);
-        assert(section);
-        assert(lvalue);
-        assert(rvalue);
-        assert(data);
-
-        r = route_new_static(network, filename, section_line, &n);
-        if (r == -ENOMEM)
-                return log_oom();
-        if (r < 0) {
-                log_syntax(unit, LOG_WARNING, filename, line, r,
-                           "Failed to allocate route, ignoring assignment: %m");
-                return 0;
-        }
-
-        if (isempty(rvalue)) {
-                n->nexthop_id = 0;
-                TAKE_PTR(n);
-                return 0;
-        }
-
-        r = safe_atou32(rvalue, &id);
-        if (r < 0) {
-                log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse nexthop ID, ignoring assignment: %s", rvalue);
-                return 0;
-        }
-        if (id == 0) {
-                log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid nexthop ID, ignoring assignment: %s", rvalue);
-                return 0;
-        }
-
-        n->nexthop_id = id;
-        TAKE_PTR(n);
+        route->scope = r;
+        route->scope_set = true;
+        TAKE_PTR(route);
         return 0;
 }
 
@@ -2335,7 +2176,7 @@ int config_parse_route_table(
                 void *data,
                 void *userdata) {
 
-        _cleanup_(route_free_or_set_invalidp) Route *n = NULL;
+        _cleanup_(route_free_or_set_invalidp) Route *route = NULL;
         Network *network = userdata;
         int r;
 
@@ -2345,7 +2186,7 @@ int config_parse_route_table(
         assert(rvalue);
         assert(data);
 
-        r = route_new_static(network, filename, section_line, &n);
+        r = route_new_static(network, filename, section_line, &route);
         if (r == -ENOMEM)
                 return log_oom();
         if (r < 0) {
@@ -2354,19 +2195,19 @@ int config_parse_route_table(
                 return 0;
         }
 
-        r = manager_get_route_table_from_string(network->manager, rvalue, &n->table);
+        r = manager_get_route_table_from_string(network->manager, rvalue, &route->table);
         if (r < 0) {
                 log_syntax(unit, LOG_WARNING, filename, line, r,
                            "Could not parse route table \"%s\", ignoring assignment: %m", rvalue);
                 return 0;
         }
 
-        n->table_set = true;
-        TAKE_PTR(n);
+        route->table_set = true;
+        TAKE_PTR(route);
         return 0;
 }
 
-int config_parse_route_boolean(
+int config_parse_route_gateway_onlink(
                 const char *unit,
                 const char *filename,
                 unsigned line,
@@ -2379,7 +2220,7 @@ int config_parse_route_boolean(
                 void *userdata) {
 
         Network *network = userdata;
-        _cleanup_(route_free_or_set_invalidp) Route *n = NULL;
+        _cleanup_(route_free_or_set_invalidp) Route *route = NULL;
         int r;
 
         assert(filename);
@@ -2388,7 +2229,7 @@ int config_parse_route_boolean(
         assert(rvalue);
         assert(data);
 
-        r = route_new_static(network, filename, section_line, &n);
+        r = route_new_static(network, filename, section_line, &route);
         if (r == -ENOMEM)
                 return log_oom();
         if (r < 0) {
@@ -2404,16 +2245,9 @@ int config_parse_route_boolean(
                 return 0;
         }
 
-        if (STR_IN_SET(lvalue, "GatewayOnLink", "GatewayOnlink"))
-                n->gateway_onlink = r;
-        else if (streq(lvalue, "QuickAck"))
-                n->quickack = r;
-        else if (streq(lvalue, "FastOpenNoCookie"))
-                n->fast_open_no_cookie = r;
-        else
-                assert_not_reached();
+        route->gateway_onlink = r;
 
-        TAKE_PTR(n);
+        TAKE_PTR(route);
         return 0;
 }
 
@@ -2430,10 +2264,10 @@ int config_parse_ipv6_route_preference(
                 void *userdata) {
 
         Network *network = userdata;
-        _cleanup_(route_free_or_set_invalidp) Route *n = NULL;
+        _cleanup_(route_free_or_set_invalidp) Route *route = NULL;
         int r;
 
-        r = route_new_static(network, filename, section_line, &n);
+        r = route_new_static(network, filename, section_line, &route);
         if (r == -ENOMEM)
                 return log_oom();
         if (r < 0) {
@@ -2443,18 +2277,18 @@ int config_parse_ipv6_route_preference(
         }
 
         if (streq(rvalue, "low"))
-                n->pref = ICMPV6_ROUTER_PREF_LOW;
+                route->pref = ICMPV6_ROUTER_PREF_LOW;
         else if (streq(rvalue, "medium"))
-                n->pref = ICMPV6_ROUTER_PREF_MEDIUM;
+                route->pref = ICMPV6_ROUTER_PREF_MEDIUM;
         else if (streq(rvalue, "high"))
-                n->pref = ICMPV6_ROUTER_PREF_HIGH;
+                route->pref = ICMPV6_ROUTER_PREF_HIGH;
         else {
                 log_syntax(unit, LOG_WARNING, filename, line, 0, "Unknown route preference: %s", rvalue);
                 return 0;
         }
 
-        n->pref_set = true;
-        TAKE_PTR(n);
+        route->pref_set = true;
+        TAKE_PTR(route);
         return 0;
 }
 
@@ -2471,10 +2305,10 @@ int config_parse_route_protocol(
                 void *userdata) {
 
         Network *network = userdata;
-        _cleanup_(route_free_or_set_invalidp) Route *n = NULL;
+        _cleanup_(route_free_or_set_invalidp) Route *route = NULL;
         int r;
 
-        r = route_new_static(network, filename, section_line, &n);
+        r = route_new_static(network, filename, section_line, &route);
         if (r == -ENOMEM)
                 return log_oom();
         if (r < 0) {
@@ -2490,9 +2324,9 @@ int config_parse_route_protocol(
                 return 0;
         }
 
-        n->protocol = r;
+        route->protocol = r;
 
-        TAKE_PTR(n);
+        TAKE_PTR(route);
         return 0;
 }
 
@@ -2509,10 +2343,10 @@ int config_parse_route_type(
                 void *userdata) {
 
         Network *network = userdata;
-        _cleanup_(route_free_or_set_invalidp) Route *n = NULL;
+        _cleanup_(route_free_or_set_invalidp) Route *route = NULL;
         int t, r;
 
-        r = route_new_static(network, filename, section_line, &n);
+        r = route_new_static(network, filename, section_line, &route);
         if (r == -ENOMEM)
                 return log_oom();
         if (r < 0) {
@@ -2528,465 +2362,9 @@ int config_parse_route_type(
                 return 0;
         }
 
-        n->type = (unsigned char) t;
-
-        TAKE_PTR(n);
-        return 0;
-}
-
-int config_parse_route_hop_limit(
-                const char *unit,
-                const char *filename,
-                unsigned line,
-                const char *section,
-                unsigned section_line,
-                const char *lvalue,
-                int ltype,
-                const char *rvalue,
-                void *data,
-                void *userdata) {
-
-        _cleanup_(route_free_or_set_invalidp) Route *n = NULL;
-        Network *network = userdata;
-        uint32_t k;
-        int r;
-
-        assert(filename);
-        assert(section);
-        assert(lvalue);
-        assert(rvalue);
-        assert(data);
-
-        r = route_new_static(network, filename, section_line, &n);
-        if (r == -ENOMEM)
-                return log_oom();
-        if (r < 0) {
-                log_syntax(unit, LOG_WARNING, filename, line, r,
-                           "Failed to allocate route, ignoring assignment: %m");
-                return 0;
-        }
-
-        if (isempty(rvalue)) {
-                n->hop_limit = 0;
-                TAKE_PTR(n);
-                return 0;
-        }
-
-        r = safe_atou32(rvalue, &k);
-        if (r < 0) {
-                log_syntax(unit, LOG_WARNING, filename, line, r,
-                           "Could not parse per route hop limit, ignoring assignment: %s", rvalue);
-                return 0;
-        }
-        if (k > 255) {
-                log_syntax(unit, LOG_WARNING, filename, line, 0,
-                           "Specified per route hop limit \"%s\" is too large, ignoring assignment: %m", rvalue);
-                return 0;
-        }
-        if (k == 0) {
-                log_syntax(unit, LOG_WARNING, filename, line, 0,
-                           "Invalid per route hop limit \"%s\", ignoring assignment: %m", rvalue);
-                return 0;
-        }
-
-        n->hop_limit = k;
-
-        TAKE_PTR(n);
-        return 0;
-}
-
-int config_parse_tcp_congestion(
-                const char *unit,
-                const char *filename,
-                unsigned line,
-                const char *section,
-                unsigned section_line,
-                const char *lvalue,
-                int ltype,
-                const char *rvalue,
-                void *data,
-                void *userdata) {
-
-        Network *network = userdata;
-        _cleanup_(route_free_or_set_invalidp) Route *n = NULL;
-        int r;
-
-        assert(filename);
-        assert(section);
-        assert(lvalue);
-        assert(rvalue);
-        assert(data);
-
-        r = route_new_static(network, filename, section_line, &n);
-        if (r == -ENOMEM)
-                return log_oom();
-        if (r < 0) {
-                log_syntax(unit, LOG_WARNING, filename, line, r,
-                           "Failed to allocate route, ignoring assignment: %m");
-                return 0;
-        }
-
-        r = config_parse_string(unit, filename, line, section, section_line, lvalue, ltype,
-                                rvalue, &n->tcp_congestion_control_algo, userdata);
-        if (r < 0)
-                return r;
-
-        TAKE_PTR(n);
-        return 0;
-}
-
-int config_parse_tcp_advmss(
-                const char *unit,
-                const char *filename,
-                unsigned line,
-                const char *section,
-                unsigned section_line,
-                const char *lvalue,
-                int ltype,
-                const char *rvalue,
-                void *data,
-                void *userdata) {
-
-        _cleanup_(route_free_or_set_invalidp) Route *n = NULL;
-        Network *network = userdata;
-        uint64_t u;
-        int r;
-
-        assert(filename);
-        assert(section);
-        assert(lvalue);
-        assert(rvalue);
-        assert(data);
-
-        r = route_new_static(network, filename, section_line, &n);
-        if (r == -ENOMEM)
-                return log_oom();
-        if (r < 0) {
-                log_syntax(unit, LOG_WARNING, filename, line, r,
-                           "Failed to allocate route, ignoring assignment: %m");
-                return 0;
-        }
-
-        if (isempty(rvalue)) {
-                n->advmss = 0;
-                TAKE_PTR(n);
-                return 0;
-        }
-
-        r = parse_size(rvalue, 1024, &u);
-        if (r < 0) {
-                log_syntax(unit, LOG_WARNING, filename, line, r,
-                           "Could not parse TCPAdvertisedMaximumSegmentSize= \"%s\", ignoring assignment: %m", rvalue);
-                return 0;
-        }
-
-        if (u == 0 || u > UINT32_MAX) {
-                log_syntax(unit, LOG_WARNING, filename, line, 0,
-                           "Invalid TCPAdvertisedMaximumSegmentSize= \"%s\", ignoring assignment: %m", rvalue);
-                return 0;
-        }
-
-        n->advmss = u;
-
-        TAKE_PTR(n);
-        return 0;
-}
-
-int config_parse_tcp_window(
-                const char *unit,
-                const char *filename,
-                unsigned line,
-                const char *section,
-                unsigned section_line,
-                const char *lvalue,
-                int ltype,
-                const char *rvalue,
-                void *data,
-                void *userdata) {
-
-        uint32_t *window = ASSERT_PTR(data);
-        uint32_t k;
-        int r;
-
-        assert(filename);
-        assert(section);
-        assert(lvalue);
-        assert(rvalue);
-        assert(data);
-
-        r = safe_atou32(rvalue, &k);
-        if (r < 0) {
-                log_syntax(unit, LOG_WARNING, filename, line, r,
-                           "Could not parse TCP %s \"%s\", ignoring assignment: %m", lvalue, rvalue);
-                return 0;
-        }
-        if (k >= 1024) {
-                log_syntax(unit, LOG_WARNING, filename, line, 0,
-                           "Specified TCP %s \"%s\" is too large, ignoring assignment: %m", lvalue, rvalue);
-                return 0;
-        }
-        if (k == 0) {
-                log_syntax(unit, LOG_WARNING, filename, line, 0,
-                           "Invalid TCP %s \"%s\", ignoring assignment: %m", lvalue, rvalue);
-                return 0;
-        }
-
-        *window = k;
-        return 0;
-}
-
-int config_parse_route_tcp_window(
-                const char *unit,
-                const char *filename,
-                unsigned line,
-                const char *section,
-                unsigned section_line,
-                const char *lvalue,
-                int ltype,
-                const char *rvalue,
-                void *data,
-                void *userdata) {
-
-        _cleanup_(route_free_or_set_invalidp) Route *n = NULL;
-        Network *network = userdata;
-        uint32_t *d;
-        int r;
-
-        assert(filename);
-        assert(section);
-        assert(lvalue);
-        assert(rvalue);
-        assert(data);
-
-        r = route_new_static(network, filename, section_line, &n);
-        if (r == -ENOMEM)
-                return log_oom();
-        if (r < 0) {
-                log_syntax(unit, LOG_WARNING, filename, line, r,
-                           "Failed to allocate route, ignoring assignment: %m");
-                return 0;
-        }
-
-        if (streq(lvalue, "InitialCongestionWindow"))
-                d = &n->initcwnd;
-        else if (streq(lvalue, "InitialAdvertisedReceiveWindow"))
-                d = &n->initrwnd;
-        else
-                assert_not_reached();
-
-        r = config_parse_tcp_window(unit, filename, line, section, section_line, lvalue, ltype, rvalue, d, userdata);
-        if (r < 0)
-                return r;
-
-        TAKE_PTR(n);
-        return 0;
-}
-
-int config_parse_route_mtu(
-                const char *unit,
-                const char *filename,
-                unsigned line,
-                const char *section,
-                unsigned section_line,
-                const char *lvalue,
-                int ltype,
-                const char *rvalue,
-                void *data,
-                void *userdata) {
-
-        Network *network = userdata;
-        _cleanup_(route_free_or_set_invalidp) Route *n = NULL;
-        int r;
-
-        assert(filename);
-        assert(section);
-        assert(lvalue);
-        assert(rvalue);
-        assert(data);
-
-        r = route_new_static(network, filename, section_line, &n);
-        if (r == -ENOMEM)
-                return log_oom();
-        if (r < 0) {
-                log_syntax(unit, LOG_WARNING, filename, line, r,
-                           "Failed to allocate route, ignoring assignment: %m");
-                return 0;
-        }
-
-        r = config_parse_mtu(unit, filename, line, section, section_line, lvalue, ltype, rvalue, &n->mtu, userdata);
-        if (r < 0)
-                return r;
-
-        TAKE_PTR(n);
-        return 0;
-}
-
-int config_parse_route_tcp_rto(
-                const char *unit,
-                const char *filename,
-                unsigned line,
-                const char *section,
-                unsigned section_line,
-                const char *lvalue,
-                int ltype,
-                const char *rvalue,
-                void *data,
-                void *userdata) {
-
-        Network *network = userdata;
-        _cleanup_(route_free_or_set_invalidp) Route *n = NULL;
-        usec_t usec;
-        int r;
-
-        assert(filename);
-        assert(section);
-        assert(lvalue);
-        assert(rvalue);
-        assert(data);
-
-        r = route_new_static(network, filename, section_line, &n);
-        if (r == -ENOMEM)
-                return log_oom();
-        if (r < 0) {
-                log_syntax(unit, LOG_WARNING, filename, line, r,
-                           "Failed to allocate route, ignoring assignment: %m");
-                return 0;
-        }
-
-        r = parse_sec(rvalue, &usec);
-        if (r < 0) {
-                log_syntax(unit, LOG_WARNING, filename, line, r,
-                           "Failed to parse route TCP retransmission timeout (RTO), ignoring assignment: %s", rvalue);
-                return 0;
-        }
-
-        if (!timestamp_is_set(usec) ||
-            DIV_ROUND_UP(usec, USEC_PER_MSEC) > UINT32_MAX) {
-                log_syntax(unit, LOG_WARNING, filename, line, 0,
-                           "Route TCP retransmission timeout (RTO) must be in the range 0…%"PRIu32"ms, ignoring assignment: %s", UINT32_MAX, rvalue);
-                return 0;
-        }
-
-        n->tcp_rto_usec = usec;
-
-        TAKE_PTR(n);
-        return 0;
-}
-
-int config_parse_multipath_route(
-                const char *unit,
-                const char *filename,
-                unsigned line,
-                const char *section,
-                unsigned section_line,
-                const char *lvalue,
-                int ltype,
-                const char *rvalue,
-                void *data,
-                void *userdata) {
-
-        _cleanup_(multipath_route_freep) MultipathRoute *m = NULL;
-        _cleanup_(route_free_or_set_invalidp) Route *n = NULL;
-        _cleanup_free_ char *word = NULL;
-        Network *network = userdata;
-        union in_addr_union a;
-        int family, r;
-        const char *p;
-        char *dev;
-
-        assert(filename);
-        assert(section);
-        assert(lvalue);
-        assert(rvalue);
-        assert(data);
-
-        r = route_new_static(network, filename, section_line, &n);
-        if (r == -ENOMEM)
-                return log_oom();
-        if (r < 0) {
-                log_syntax(unit, LOG_WARNING, filename, line, r,
-                           "Failed to allocate route, ignoring assignment: %m");
-                return 0;
-        }
-
-        if (isempty(rvalue)) {
-                n->multipath_routes = ordered_set_free_with_destructor(n->multipath_routes, multipath_route_free);
-                return 0;
-        }
-
-        m = new0(MultipathRoute, 1);
-        if (!m)
-                return log_oom();
-
-        p = rvalue;
-        r = extract_first_word(&p, &word, NULL, 0);
-        if (r == -ENOMEM)
-                return log_oom();
-        if (r <= 0) {
-                log_syntax(unit, LOG_WARNING, filename, line, r,
-                           "Invalid multipath route option, ignoring assignment: %s", rvalue);
-                return 0;
-        }
-
-        dev = strchr(word, '@');
-        if (dev) {
-                *dev++ = '\0';
-
-                r = parse_ifindex(dev);
-                if (r > 0)
-                        m->ifindex = r;
-                else {
-                        if (!ifname_valid_full(dev, IFNAME_VALID_ALTERNATIVE)) {
-                                log_syntax(unit, LOG_WARNING, filename, line, 0,
-                                           "Invalid interface name '%s' in %s=, ignoring: %s", dev, lvalue, rvalue);
-                                return 0;
-                        }
-
-                        m->ifname = strdup(dev);
-                        if (!m->ifname)
-                                return log_oom();
-                }
-        }
-
-        r = in_addr_from_string_auto(word, &family, &a);
-        if (r < 0) {
-                log_syntax(unit, LOG_WARNING, filename, line, r,
-                           "Invalid multipath route gateway '%s', ignoring assignment: %m", rvalue);
-                return 0;
-        }
-        m->gateway.address = a;
-        m->gateway.family = family;
-
-        if (!isempty(p)) {
-                r = safe_atou32(p, &m->weight);
-                if (r < 0) {
-                        log_syntax(unit, LOG_WARNING, filename, line, r,
-                                   "Invalid multipath route weight, ignoring assignment: %s", p);
-                        return 0;
-                }
-                /* ip command takes weight in the range 1…255, while kernel takes the value in the
-                 * range 0…254. MultiPathRoute= setting also takes weight in the same range which ip
-                 * command uses, then networkd decreases by one and stores it to match the range which
-                 * kernel uses. */
-                if (m->weight == 0 || m->weight > 256) {
-                        log_syntax(unit, LOG_WARNING, filename, line, 0,
-                                   "Invalid multipath route weight, ignoring assignment: %s", p);
-                        return 0;
-                }
-                m->weight--;
-        }
-
-        r = ordered_set_ensure_put(&n->multipath_routes, NULL, m);
-        if (r == -ENOMEM)
-                return log_oom();
-        if (r < 0) {
-                log_syntax(unit, LOG_WARNING, filename, line, r,
-                           "Failed to store multipath route, ignoring assignment: %m");
-                return 0;
-        }
+        route->type = (unsigned char) t;
 
-        TAKE_PTR(m);
-        TAKE_PTR(n);
+        TAKE_PTR(route);
         return 0;
 }
 
index 2e7f6290630dcb6fe51a6ec3ebf3e3e40ddbefe3..33d1e643cc49e92ae6cf36045d8bbedf028a44d7 100644 (file)
@@ -9,6 +9,8 @@
 #include "conf-parser.h"
 #include "in-addr-util.h"
 #include "networkd-link.h"
+#include "networkd-route-metric.h"
+#include "networkd-route-nexthop.h"
 #include "networkd-util.h"
 
 typedef struct Manager Manager;
@@ -34,8 +36,6 @@ struct Route {
         int family;
         int gw_family;
         uint32_t gw_weight;
-        int quickack;
-        int fast_open_no_cookie;
 
         unsigned char dst_prefixlen;
         unsigned char src_prefixlen;
@@ -45,17 +45,13 @@ struct Route {
         unsigned char tos;
         uint32_t priority; /* note that ip(8) calls this 'metric' */
         uint32_t table;
-        uint32_t mtu;
-        uint32_t initcwnd;
-        uint32_t initrwnd;
-        uint32_t advmss;
-        uint32_t hop_limit;
-        char *tcp_congestion_control_algo;
         unsigned char pref;
         unsigned flags;
         int gateway_onlink; /* Only used in conf parser and route_section_verify(). */
         uint32_t nexthop_id;
-        usec_t tcp_rto_usec;
+
+        /* metrics (RTA_METRICS) */
+        RouteMetric metric;
 
         bool scope_set:1;
         bool table_set:1;
@@ -80,6 +76,7 @@ struct Route {
 extern const struct hash_ops route_hash_ops;
 
 int route_new(Route **ret);
+int route_new_static(Network *network, const char *filename, unsigned section_line, Route **ret);
 Route *route_free(Route *route);
 DEFINE_SECTION_CLEANUP_FUNCTIONS(Route, route_free);
 int route_dup(const Route *src, Route **ret);
@@ -119,16 +116,7 @@ CONFIG_PARSER_PROTOTYPE(config_parse_destination);
 CONFIG_PARSER_PROTOTYPE(config_parse_route_priority);
 CONFIG_PARSER_PROTOTYPE(config_parse_route_scope);
 CONFIG_PARSER_PROTOTYPE(config_parse_route_table);
-CONFIG_PARSER_PROTOTYPE(config_parse_route_boolean);
+CONFIG_PARSER_PROTOTYPE(config_parse_route_gateway_onlink);
 CONFIG_PARSER_PROTOTYPE(config_parse_ipv6_route_preference);
 CONFIG_PARSER_PROTOTYPE(config_parse_route_protocol);
 CONFIG_PARSER_PROTOTYPE(config_parse_route_type);
-CONFIG_PARSER_PROTOTYPE(config_parse_route_tcp_window);
-CONFIG_PARSER_PROTOTYPE(config_parse_route_hop_limit);
-CONFIG_PARSER_PROTOTYPE(config_parse_tcp_window);
-CONFIG_PARSER_PROTOTYPE(config_parse_route_tcp_rto);
-CONFIG_PARSER_PROTOTYPE(config_parse_route_mtu);
-CONFIG_PARSER_PROTOTYPE(config_parse_multipath_route);
-CONFIG_PARSER_PROTOTYPE(config_parse_tcp_congestion);
-CONFIG_PARSER_PROTOTYPE(config_parse_tcp_advmss);
-CONFIG_PARSER_PROTOTYPE(config_parse_route_nexthop);
index 6324b044983649de1e9679022f73fa6dd4b78944..914e288aeccca924ffac3fc9a2dc957b94cb0d89 100644 (file)
@@ -1408,7 +1408,7 @@ int config_parse_routing_policy_rule_port_range(
         if (r < 0)
                 return log_oom();
 
-        r = parse_ip_port_range(rvalue, &low, &high);
+        r = parse_ip_port_range(rvalue, &low, &high, /* allow_zero = */ false);
         if (r < 0) {
                 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse routing policy rule port range '%s'", rvalue);
                 return 0;
index 810ddbb45af2b4d27408d1e5655ad37f549eae78..c7e1a9253c557755e007bda9d060f566271af789 100644 (file)
@@ -388,9 +388,9 @@ int bind_user_setup(
         if (!c || c->n_data == 0)
                 return 0;
 
-        r = userns_mkdir(root, "/run/host", 0755, 0, 0);
+        r = make_run_host(root);
         if (r < 0)
-                return log_error_errno(r, "Failed to create /run/host: %m");
+                return r;
 
         r = userns_mkdir(root, "/run/host/home", 0755, 0, 0);
         if (r < 0)
index 4d3783e00a943e44b3fa214be2d656270d0efa5e..4df0a0092d21b04396a3f4d423875813379d864a 100644 (file)
@@ -1,7 +1,5 @@
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 
-#if HAVE_BLKID
-#endif
 #include <errno.h>
 #include <getopt.h>
 #include <linux/fs.h>
 #include "umask-util.h"
 #include "unit-name.h"
 #include "user-util.h"
+#include "vpick.h"
 
 /* The notify socket inside the container it can use to talk to nspawn using the sd_notify(3) protocol */
 #define NSPAWN_NOTIFY_SOCKET_PATH "/run/host/notify"
@@ -1395,7 +1394,7 @@ static int parse_argv(int argc, char *argv[]) {
                         _cleanup_free_ void *k = NULL;
                         size_t l;
 
-                        r = unhexmem(optarg, strlen(optarg), &k, &l);
+                        r = unhexmem(optarg, &k, &l);
                         if (r < 0)
                                 return log_error_errno(r, "Failed to parse root hash: %s", optarg);
                         if (l < sizeof(sd_id128_t))
@@ -1412,7 +1411,7 @@ static int parse_argv(int argc, char *argv[]) {
                         void *p;
 
                         if ((value = startswith(optarg, "base64:"))) {
-                                r = unbase64mem(value, strlen(value), &p, &l);
+                                r = unbase64mem(value, &p, &l);
                                 if (r < 0)
                                         return log_error_errno(r, "Failed to parse root hash signature '%s': %m", optarg);
 
@@ -2363,6 +2362,18 @@ static int setup_keyring(void) {
         return 0;
 }
 
+int make_run_host(const char *root) {
+        int r;
+
+        assert(root);
+
+        r = userns_mkdir(root, "/run/host", 0755, 0, 0);
+        if (r < 0)
+                return log_error_errno(r, "Failed to create /run/host/: %m");
+
+        return 0;
+}
+
 static int setup_credentials(const char *root) {
         const char *q;
         int r;
@@ -2370,9 +2381,9 @@ static int setup_credentials(const char *root) {
         if (arg_credentials.n_credentials == 0)
                 return 0;
 
-        r = userns_mkdir(root, "/run/host", 0755, 0, 0);
+        r = make_run_host(root);
         if (r < 0)
-                return log_error_errno(r, "Failed to create /run/host: %m");
+                return r;
 
         r = userns_mkdir(root, "/run/host/credentials", 0700, 0, 0);
         if (r < 0)
@@ -2712,9 +2723,9 @@ static int mount_tunnel_dig(const char *root) {
         p = strjoina("/run/systemd/nspawn/propagate/", arg_machine);
         (void) mkdir_p(p, 0600);
 
-        r = userns_mkdir(root, "/run/host", 0755, 0, 0);
+        r = make_run_host(root);
         if (r < 0)
-                return log_error_errno(r, "Failed to create /run/host: %m");
+                return r;
 
         r = userns_mkdir(root, NSPAWN_MOUNT_TUNNEL, 0600, 0, 0);
         if (r < 0)
@@ -2911,14 +2922,72 @@ static int on_request_stop(sd_bus_message *m, void *userdata, sd_bus_error *erro
         return 0;
 }
 
+static int pick_paths(void) {
+        int r;
+
+        if (arg_directory) {
+                _cleanup_(pick_result_done) PickResult result = PICK_RESULT_NULL;
+                PickFilter filter = pick_filter_image_dir;
+
+                filter.architecture = arg_architecture;
+
+                r = path_pick_update_warn(
+                                &arg_directory,
+                                &filter,
+                                PICK_ARCHITECTURE|PICK_TRIES,
+                                &result);
+                if (r < 0) {
+                        /* Accept ENOENT here so that the --template= logic can work */
+                        if (r != -ENOENT)
+                                return r;
+                } else
+                        arg_architecture = result.architecture;
+        }
+
+        if (arg_image) {
+                _cleanup_(pick_result_done) PickResult result = PICK_RESULT_NULL;
+                PickFilter filter = pick_filter_image_raw;
+
+                filter.architecture = arg_architecture;
+
+                r = path_pick_update_warn(
+                                &arg_image,
+                                &filter,
+                                PICK_ARCHITECTURE|PICK_TRIES,
+                                &result);
+                if (r < 0)
+                        return r;
+
+                arg_architecture = result.architecture;
+        }
+
+        if (arg_template) {
+                _cleanup_(pick_result_done) PickResult result = PICK_RESULT_NULL;
+                PickFilter filter = pick_filter_image_dir;
+
+                filter.architecture = arg_architecture;
+
+                r = path_pick_update_warn(
+                                &arg_template,
+                                &filter,
+                                PICK_ARCHITECTURE,
+                                &result);
+                if (r < 0)
+                        return r;
+
+                arg_architecture = result.architecture;
+        }
+
+        return 0;
+}
+
 static int determine_names(void) {
         int r;
 
         if (arg_template && !arg_directory && arg_machine) {
 
-                /* If --template= was specified then we should not
-                 * search for a machine, but instead create a new one
-                 * in /var/lib/machine. */
+                /* If --template= was specified then we should not search for a machine, but instead create a
+                 * new one in /var/lib/machine. */
 
                 arg_directory = path_join("/var/lib/machines", arg_machine);
                 if (!arg_directory)
@@ -3522,9 +3591,11 @@ static int setup_notify_child(void) {
         (void) mkdir_parents(NSPAWN_NOTIFY_SOCKET_PATH, 0755);
         (void) sockaddr_un_unlink(&sa.un);
 
-        r = bind(fd, &sa.sa, SOCKADDR_UN_LEN(sa.un));
-        if (r < 0)
-                return log_error_errno(errno, "bind(" NSPAWN_NOTIFY_SOCKET_PATH ") failed: %m");
+        WITH_UMASK(0577) { /* only set "w" bit, which is all that's necessary for connecting from the container */
+                r = bind(fd, &sa.sa, SOCKADDR_UN_LEN(sa.un));
+                if (r < 0)
+                        return log_error_errno(errno, "bind(" NSPAWN_NOTIFY_SOCKET_PATH ") failed: %m");
+        }
 
         r = userns_lchown(NSPAWN_NOTIFY_SOCKET_PATH, 0, 0);
         if (r < 0)
@@ -3887,11 +3958,11 @@ static int outer_child(
 
         /* The same stuff as the $container env var, but nicely readable for the entire payload */
         p = prefix_roota(directory, "/run/host/container-manager");
-        (void) write_string_file(p, arg_container_service_name, WRITE_STRING_FILE_CREATE);
+        (void) write_string_file(p, arg_container_service_name, WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_MODE_0444);
 
         /* The same stuff as the $container_uuid env var */
         p = prefix_roota(directory, "/run/host/container-uuid");
-        (void) write_string_filef(p, WRITE_STRING_FILE_CREATE, SD_ID128_UUID_FORMAT_STR, SD_ID128_FORMAT_VAL(arg_uuid));
+        (void) write_string_filef(p, WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_MODE_0444, SD_ID128_UUID_FORMAT_STR, SD_ID128_FORMAT_VAL(arg_uuid));
 
         if (!arg_use_cgns) {
                 r = mount_cgroups(
@@ -5406,6 +5477,10 @@ static int run(int argc, char *argv[]) {
         if (r < 0)
                 goto finish;
 
+        r = pick_paths();
+        if (r < 0)
+                goto finish;
+
         r = determine_names();
         if (r < 0)
                 goto finish;
index 27fb0b44eb942469e8a7e99726c78eab2a138926..556f8ee1aff07d081699a52ba114357ef98ac53b 100644 (file)
@@ -5,3 +5,4 @@
 
 int userns_lchown(const char *p, uid_t uid, gid_t gid);
 int userns_mkdir(const char *root, const char *path, mode_t mode, uid_t uid, gid_t gid);
+int make_run_host(const char *root);
index c4e02bc7c13ef15b336bb4c9b33a10b2ec2055eb..822ad4f6221eae37674cf0825abc8acdfacd3ee0 100644 (file)
@@ -20,7 +20,7 @@
 #include "strv.h"
 #include "varlink.h"
 
-static JsonDispatchFlags json_dispatch_flags = 0;
+static JsonDispatchFlags json_dispatch_flags = JSON_ALLOW_EXTENSIONS;
 
 static void setup_logging(void) {
         log_parse_environment_variables();
index 6081254b3dc6c24e6257f8e2d85a076dc6109e5a..839936ce7c774a701163e156f93ab896991bbe3a 100644 (file)
@@ -30,30 +30,17 @@ static void managed_oom_message_destroy(ManagedOOMMessage *message) {
         free(message->property);
 }
 
-static int managed_oom_mode(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) {
-        ManagedOOMMode *mode = userdata, m;
-        const char *s;
-
-        assert(mode);
-        assert_se(s = json_variant_string(v));
-
-        m = managed_oom_mode_from_string(s);
-        if (m < 0)
-                return json_log(v, flags, m, "%s is not a valid ManagedOOMMode", s);
-
-        *mode = m;
-        return 0;
-}
+static JSON_DISPATCH_ENUM_DEFINE(dispatch_managed_oom_mode, ManagedOOMMode, managed_oom_mode_from_string);
 
 static int process_managed_oom_message(Manager *m, uid_t uid, JsonVariant *parameters) {
         JsonVariant *c, *cgroups;
         int r;
 
         static const JsonDispatch dispatch_table[] = {
-                { "mode",     JSON_VARIANT_STRING,        managed_oom_mode,     offsetof(ManagedOOMMessage, mode),     JSON_MANDATORY },
-                { "path",     JSON_VARIANT_STRING,        json_dispatch_string, offsetof(ManagedOOMMessage, path),     JSON_MANDATORY },
-                { "property", JSON_VARIANT_STRING,        json_dispatch_string, offsetof(ManagedOOMMessage, property), JSON_MANDATORY },
-                { "limit",    _JSON_VARIANT_TYPE_INVALID, json_dispatch_uint32, offsetof(ManagedOOMMessage, limit),    0              },
+                { "mode",     JSON_VARIANT_STRING,        dispatch_managed_oom_mode, offsetof(ManagedOOMMessage, mode),     JSON_MANDATORY },
+                { "path",     JSON_VARIANT_STRING,        json_dispatch_string,      offsetof(ManagedOOMMessage, path),     JSON_MANDATORY },
+                { "property", JSON_VARIANT_STRING,        json_dispatch_string,      offsetof(ManagedOOMMessage, property), JSON_MANDATORY },
+                { "limit",    _JSON_VARIANT_TYPE_INVALID, json_dispatch_uint32,      offsetof(ManagedOOMMessage, limit),    0              },
                 {},
         };
 
@@ -642,7 +629,7 @@ Manager* manager_free(Manager *m) {
         sd_event_source_unref(m->mem_pressure_context_event_source);
         sd_event_unref(m->event);
 
-        bus_verify_polkit_async_registry_free(m->polkit_registry);
+        hashmap_free(m->polkit_registry);
         sd_bus_flush_close_unref(m->bus);
 
         hashmap_free(m->monitored_swap_cgroup_contexts);
index 95cae94a8e069af60d0a5bfaf0a0833e8cff4abe..9b6b8a5a14cfde6b57204589c775b2cca33933d3 100644 (file)
@@ -128,7 +128,7 @@ typedef enum FilterPartitionType {
 
 static EmptyMode arg_empty = EMPTY_UNSET;
 static bool arg_dry_run = true;
-static const char *arg_node = NULL;
+static char *arg_node = NULL;
 static char *arg_root = NULL;
 static char *arg_image = NULL;
 static char **arg_definitions = NULL;
@@ -169,6 +169,7 @@ static char **arg_copy_from = NULL;
 static char *arg_copy_source = NULL;
 static char *arg_make_ddi = NULL;
 
+STATIC_DESTRUCTOR_REGISTER(arg_node, freep);
 STATIC_DESTRUCTOR_REGISTER(arg_root, freep);
 STATIC_DESTRUCTOR_REGISTER(arg_image, freep);
 STATIC_DESTRUCTOR_REGISTER(arg_definitions, strv_freep);
@@ -2908,12 +2909,13 @@ static int context_dump_partitions(Context *context) {
         return table_print_with_pager(t, arg_json_format_flags, arg_pager_flags, arg_legend);
 }
 
-static void context_bar_char_process_partition(
+static int context_bar_char_process_partition(
                 Context *context,
                 Partition *bar[],
                 size_t n,
                 Partition *p,
-                size_t *ret_start) {
+                size_t **start_array,
+                size_t *n_start_array) {
 
         uint64_t from, to, total;
         size_t x, y;
@@ -2922,9 +2924,11 @@ static void context_bar_char_process_partition(
         assert(bar);
         assert(n > 0);
         assert(p);
+        assert(start_array);
+        assert(n_start_array);
 
         if (p->dropped)
-                return;
+                return 0;
 
         assert(p->offset != UINT64_MAX);
         assert(p->new_size != UINT64_MAX);
@@ -2947,7 +2951,10 @@ static void context_bar_char_process_partition(
         for (size_t i = x; i < y; i++)
                 bar[i] = p;
 
-        *ret_start = x;
+        if (!GREEDY_REALLOC_APPEND(*start_array, *n_start_array, &x, 1))
+                return log_oom();
+
+        return 1;
 }
 
 static int partition_hint(const Partition *p, const char *node, char **ret) {
@@ -2991,9 +2998,11 @@ done:
 static int context_dump_partition_bar(Context *context) {
         _cleanup_free_ Partition **bar = NULL;
         _cleanup_free_ size_t *start_array = NULL;
+        size_t n_start_array = 0;
         Partition *last = NULL;
         bool z = false;
         size_t c, j = 0;
+        int r;
 
         assert_se((c = columns()) >= 2);
         c -= 2; /* We do not use the leftmost and rightmost character cell */
@@ -3002,12 +3011,11 @@ static int context_dump_partition_bar(Context *context) {
         if (!bar)
                 return log_oom();
 
-        start_array = new(size_t, context->n_partitions);
-        if (!start_array)
-                return log_oom();
-
-        LIST_FOREACH(partitions, p, context->partitions)
-                context_bar_char_process_partition(context, bar, c, p, start_array + j++);
+        LIST_FOREACH(partitions, p, context->partitions) {
+                r = context_bar_char_process_partition(context, bar, c, p, &start_array, &n_start_array);
+                if (r < 0)
+                        return r;
+        }
 
         putc(' ', stdout);
 
@@ -3029,7 +3037,7 @@ static int context_dump_partition_bar(Context *context) {
         fputs(ansi_normal(), stdout);
         putc('\n', stdout);
 
-        for (size_t i = 0; i < context->n_partitions; i++) {
+        for (size_t i = 0; i < n_start_array; i++) {
                 _cleanup_free_ char **line = NULL;
 
                 line = new0(char*, c);
@@ -3039,9 +3047,13 @@ static int context_dump_partition_bar(Context *context) {
                 j = 0;
                 LIST_FOREACH(partitions, p, context->partitions) {
                         _cleanup_free_ char *d = NULL;
+
+                        if (p->dropped)
+                                continue;
+
                         j++;
 
-                        if (i < context->n_partitions - j) {
+                        if (i < n_start_array - j) {
 
                                 if (line[start_array[j-1]]) {
                                         const char *e;
@@ -3061,7 +3073,7 @@ static int context_dump_partition_bar(Context *context) {
                                                 return log_oom();
                                 }
 
-                        } else if (i == context->n_partitions - j) {
+                        } else if (i == n_start_array - j) {
                                 _cleanup_free_ char *hint = NULL;
 
                                 (void) partition_hint(p, context->node, &hint);
@@ -3774,17 +3786,15 @@ static int partition_encrypt(Context *context, Partition *p, PartitionTarget *ta
 
         if (IN_SET(p->encrypt, ENCRYPT_TPM2, ENCRYPT_KEY_FILE_TPM2)) {
 #if HAVE_TPM2
+                _cleanup_(iovec_done) struct iovec pubkey = {}, blob = {}, srk = {};
+                _cleanup_(iovec_done_erase) struct iovec secret = {};
                 _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
-                _cleanup_(erase_and_freep) void *secret = NULL;
-                _cleanup_free_ void *pubkey = NULL;
-                _cleanup_free_ void *blob = NULL, *srk_buf = NULL;
-                size_t secret_size, blob_size, pubkey_size = 0, srk_buf_size = 0;
                 ssize_t base64_encoded_size;
                 int keyslot;
                 TPM2Flags flags = 0;
 
                 if (arg_tpm2_public_key_pcr_mask != 0) {
-                        r = tpm2_load_pcr_public_key(arg_tpm2_public_key, &pubkey, &pubkey_size);
+                        r = tpm2_load_pcr_public_key(arg_tpm2_public_key, &pubkey.iov_base, &pubkey.iov_len);
                         if (r < 0) {
                                 if (arg_tpm2_public_key || r != -ENOENT)
                                         return log_error_errno(r, "Failed to read TPM PCR public key: %m");
@@ -3795,8 +3805,8 @@ static int partition_encrypt(Context *context, Partition *p, PartitionTarget *ta
                 }
 
                 TPM2B_PUBLIC public;
-                if (pubkey) {
-                        r = tpm2_tpm2b_public_from_pem(pubkey, pubkey_size, &public);
+                if (iovec_is_set(&pubkey)) {
+                        r = tpm2_tpm2b_public_from_pem(pubkey.iov_base, pubkey.iov_len, &public);
                         if (r < 0)
                                 return log_error_errno(r, "Could not convert public key to TPM2B_PUBLIC: %m");
                 }
@@ -3853,7 +3863,7 @@ static int partition_encrypt(Context *context, Partition *p, PartitionTarget *ta
                 r = tpm2_calculate_sealing_policy(
                                 arg_tpm2_hash_pcr_values,
                                 arg_tpm2_n_hash_pcr_values,
-                                pubkey ? &public : NULL,
+                                iovec_is_set(&pubkey) ? &public : NULL,
                                 /* use_pin= */ false,
                                 arg_tpm2_pcrlock ? &pcrlock_policy : NULL,
                                 &policy);
@@ -3865,25 +3875,25 @@ static int partition_encrypt(Context *context, Partition *p, PartitionTarget *ta
                                         arg_tpm2_seal_key_handle,
                                         &device_key_public,
                                         /* attributes= */ NULL,
-                                        /* secret= */ NULL, /* secret_size= */ 0,
+                                        /* secret= */ NULL,
                                         &policy,
                                         /* pin= */ NULL,
-                                        &secret, &secret_size,
-                                        &blob, &blob_size,
-                                        &srk_buf, &srk_buf_size);
+                                        &secret,
+                                        &blob,
+                                        &srk);
                 else
                         r = tpm2_seal(tpm2_context,
                                       arg_tpm2_seal_key_handle,
                                       &policy,
                                       /* pin= */ NULL,
-                                      &secret, &secret_size,
-                                      &blob, &blob_size,
+                                      &secret,
+                                      &blob,
                                       /* ret_primary_alg= */ NULL,
-                                      &srk_buf, &srk_buf_size);
+                                      &srk);
                 if (r < 0)
                         return log_error_errno(r, "Failed to seal to TPM2: %m");
 
-                base64_encoded_size = base64mem(secret, secret_size, &base64_encoded);
+                base64_encoded_size = base64mem(secret.iov_base, secret.iov_len, &base64_encoded);
                 if (base64_encoded_size < 0)
                         return log_error_errno(base64_encoded_size, "Failed to base64 encode secret key: %m");
 
@@ -3905,13 +3915,13 @@ static int partition_encrypt(Context *context, Partition *p, PartitionTarget *ta
                                 keyslot,
                                 hash_pcr_mask,
                                 hash_pcr_bank,
-                                pubkey, pubkey_size,
+                                &pubkey,
                                 arg_tpm2_public_key_pcr_mask,
                                 /* primary_alg= */ 0,
-                                blob, blob_size,
-                                policy.buffer, policy.size,
-                                NULL, 0, /* no salt because tpm2_seal has no pin */
-                                srk_buf, srk_buf_size,
+                                &blob,
+                                &IOVEC_MAKE(policy.buffer, policy.size),
+                                /* salt= */ NULL, /* no salt because tpm2_seal has no pin */
+                                &srk,
                                 flags,
                                 &v);
                 if (r < 0)
@@ -7031,7 +7041,11 @@ static int parse_argv(int argc, char *argv[]) {
                         return log_oom();
         }
 
-        arg_node = argc > optind ? argv[optind] : NULL;
+        if (argc > optind) {
+                arg_node = strdup(argv[optind]);
+                if (!arg_node)
+                        return log_oom();
+        }
 
         if (IN_SET(arg_empty, EMPTY_FORCE, EMPTY_REQUIRE, EMPTY_CREATE) && !arg_node && !arg_image)
                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
index d1bab5f59f867045e4346fc30ee08a03a7ad9fa4..9eeef91a4cee422c9f8f2667630c7ecbbdc21f36 100644 (file)
@@ -74,8 +74,8 @@ STATIC_DESTRUCTOR_REGISTER(arg_policy_path, freep);
 #define PCRLOCK_FIRMWARE_CONFIG_LATE_PATH   "/var/lib/pcrlock.d/550-firmware-config-late.pcrlock.d/generated.pcrlock"
 #define PCRLOCK_GPT_PATH                    "/var/lib/pcrlock.d/600-gpt.pcrlock.d/generated.pcrlock"
 #define PCRLOCK_SECUREBOOT_AUTHORITY_PATH   "/var/lib/pcrlock.d/620-secureboot-authority.pcrlock.d/generated.pcrlock"
-#define PCRLOCK_KERNEL_CMDLINE_PATH         "/var/lib/pcrlock.d/710-kernel-cmdline.pcrlock/generated.pcrlock"
-#define PCRLOCK_KERNEL_INITRD_PATH          "/var/lib/pcrlock.d/720-kernel-initrd.pcrlock/generated.pcrlock"
+#define PCRLOCK_KERNEL_CMDLINE_PATH         "/var/lib/pcrlock.d/710-kernel-cmdline.pcrlock.d/generated.pcrlock"
+#define PCRLOCK_KERNEL_INITRD_PATH          "/var/lib/pcrlock.d/720-kernel-initrd.pcrlock.d/generated.pcrlock"
 #define PCRLOCK_MACHINE_ID_PATH             "/var/lib/pcrlock.d/820-machine-id.pcrlock"
 #define PCRLOCK_ROOT_FILE_SYSTEM_PATH       "/var/lib/pcrlock.d/830-root-file-system.pcrlock"
 #define PCRLOCK_FILE_SYSTEM_PATH_PREFIX     "/var/lib/pcrlock.d/840-file-system-"
@@ -4174,7 +4174,7 @@ static int verb_make_policy(int argc, char *argv[], void *userdata) {
          * policies).
          *
          * Whenever we want to lock an encrypted object (for example FDE) against this policy, we'll use a
-         * PolicyAuthorizeNV epxression that pins the NV index in the policy, and permits access to any
+         * PolicyAuthorizeNV expression that pins the NV index in the policy, and permits access to any
          * policies matching the current NV index contents.
          *
          * We grant world-readable read access to the NV index. Write access is controlled by a PIN (which we
@@ -4560,7 +4560,7 @@ static int verb_make_policy(int argc, char *argv[], void *userdata) {
                         return r;
         }
 
-        log_info("Written new policy to '%s' and digest to TPM2 NV index 0x%" PRIu32 ".", path, nv_index);
+        log_info("Written new policy to '%s' and digest to TPM2 NV index 0x%x.", path, nv_index);
 
         log_info("Overall time spent: %s", FORMAT_TIMESPAN(usec_sub_unsigned(now(CLOCK_MONOTONIC), start_usec), 1));
 
index 136c5fa19184a084add11f7f77117d0b6525deec..d4a01fa53b0f3a84ad0363b6243ba29324b1654b 100644 (file)
@@ -65,7 +65,7 @@ static Manager* manager_unref(Manager *m) {
 
         sd_event_source_unref(m->image_cache_defer_event);
 
-        bus_verify_polkit_async_registry_free(m->polkit_registry);
+        hashmap_free(m->polkit_registry);
 
         sd_bus_flush_close_unref(m->bus);
         sd_event_unref(m->event);
@@ -122,17 +122,6 @@ static bool check_idle(void *userdata) {
         return !m->operations;
 }
 
-static int manager_run(Manager *m) {
-        assert(m);
-
-        return bus_event_loop_with_idle(
-                        m->event,
-                        m->bus,
-                        "org.freedesktop.portable1",
-                        DEFAULT_EXIT_USEC,
-                        check_idle, m);
-}
-
 static int run(int argc, char *argv[]) {
         _cleanup_(manager_unrefp) Manager *m = NULL;
         int r;
@@ -162,16 +151,20 @@ static int run(int argc, char *argv[]) {
         if (r < 0)
                 return log_error_errno(r, "Failed to fully start up daemon: %m");
 
-        log_debug("systemd-portabled running as pid " PID_FMT, getpid_cached());
         r = sd_notify(false, NOTIFY_READY);
         if (r < 0)
                 log_warning_errno(r, "Failed to send readiness notification, ignoring: %m");
 
-        r = manager_run(m);
+        r = bus_event_loop_with_idle(
+                        m->event,
+                        m->bus,
+                        "org.freedesktop.portable1",
+                        DEFAULT_EXIT_USEC,
+                        check_idle, m);
+        if (r < 0)
+                return log_error_errno(r, "Failed to run main loop: %m");
 
-        (void) sd_notify(false, NOTIFY_STOPPING);
-        log_debug("systemd-portabled stopped as pid " PID_FMT, getpid_cached());
-        return r;
+        return 0;
 }
 
 DEFINE_MAIN_FUNCTION(run);
index e7867e2f85afe63b9021b47d929cf9fe76f38e9a..d855ded91a3db87b21898ba3c7eed01621ea8585 100644 (file)
@@ -196,6 +196,20 @@ executables += [
                 ],
                 'include_directories' : resolve_includes,
         },
+        test_template + {
+                'sources' : [
+                        files('test-resolved-dummy-server.c'),
+                        basic_dns_sources,
+                        systemd_resolved_sources,
+                ],
+                'dependencies' : [
+                        lib_openssl_or_gcrypt,
+                        libm,
+                        systemd_resolved_dependencies,
+                ],
+                'include_directories' : resolve_includes,
+                'type' : 'manual',
+        },
         resolve_fuzz_template + {
                 'sources' : files('fuzz-dns-packet.c'),
         },
index afa537f160ba684cc044d86c497d5a2b3ea86d8e..b086a67010f3667a3eeb4cb4666d95e5e5692bec 100644 (file)
@@ -2715,24 +2715,26 @@ static int print_answer(JsonVariant *answer) {
 
 static void monitor_query_dump(JsonVariant *v) {
         _cleanup_(json_variant_unrefp) JsonVariant *question = NULL, *answer = NULL, *collected_questions = NULL;
-        int rcode = -1, error = 0, r;
-        const char *state = NULL;
+        int rcode = -1, error = 0, ede_code = -1;
+        const char *state = NULL, *result = NULL, *ede_msg = NULL;
 
         assert(v);
 
         JsonDispatch dispatch_table[] = {
-                { "question",           JSON_VARIANT_ARRAY,         json_dispatch_variant,      PTR_TO_SIZE(&question),            JSON_MANDATORY },
-                { "answer",             JSON_VARIANT_ARRAY,         json_dispatch_variant,      PTR_TO_SIZE(&answer),              0              },
-                { "collectedQuestions", JSON_VARIANT_ARRAY,         json_dispatch_variant,      PTR_TO_SIZE(&collected_questions), 0              },
-                { "state",              JSON_VARIANT_STRING,        json_dispatch_const_string, PTR_TO_SIZE(&state),               JSON_MANDATORY },
-                { "rcode",              _JSON_VARIANT_TYPE_INVALID, json_dispatch_int,          PTR_TO_SIZE(&rcode),               0              },
-                { "errno",              _JSON_VARIANT_TYPE_INVALID, json_dispatch_int,          PTR_TO_SIZE(&error),               0              },
+                { "question",                JSON_VARIANT_ARRAY,         json_dispatch_variant,      PTR_TO_SIZE(&question),            JSON_MANDATORY },
+                { "answer",                  JSON_VARIANT_ARRAY,         json_dispatch_variant,      PTR_TO_SIZE(&answer),              0              },
+                { "collectedQuestions",      JSON_VARIANT_ARRAY,         json_dispatch_variant,      PTR_TO_SIZE(&collected_questions), 0              },
+                { "state",                   JSON_VARIANT_STRING,        json_dispatch_const_string, PTR_TO_SIZE(&state),               JSON_MANDATORY },
+                { "result",                  JSON_VARIANT_STRING,        json_dispatch_const_string, PTR_TO_SIZE(&result),              0              },
+                { "rcode",                   _JSON_VARIANT_TYPE_INVALID, json_dispatch_int,          PTR_TO_SIZE(&rcode),               0              },
+                { "errno",                   _JSON_VARIANT_TYPE_INVALID, json_dispatch_int,          PTR_TO_SIZE(&error),               0              },
+                { "extendedDNSErrorCode",    _JSON_VARIANT_TYPE_INVALID, json_dispatch_int,          PTR_TO_SIZE(&ede_code),            0              },
+                { "extendedDNSErrorMessage", JSON_VARIANT_STRING,        json_dispatch_const_string, PTR_TO_SIZE(&ede_msg),             0              },
                 {}
         };
 
-        r = json_dispatch(v, dispatch_table, 0, NULL);
-        if (r < 0)
-                return (void) log_warning("Received malformed monitor message, ignoring.");
+        if (json_dispatch(v, dispatch_table, JSON_LOG|JSON_ALLOW_EXTENSIONS, NULL) < 0)
+                return;
 
         /* First show the current question */
         print_question('Q', ansi_highlight_cyan(), question);
@@ -2740,7 +2742,7 @@ static void monitor_query_dump(JsonVariant *v) {
         /* And then show the questions that led to this one in case this was a CNAME chain */
         print_question('C', ansi_highlight_grey(), collected_questions);
 
-        printf("%s%s S%s: %s\n",
+        printf("%s%s S%s: %s",
                streq_ptr(state, "success") ? ansi_highlight_green() : ansi_highlight_red(),
                special_glyph(SPECIAL_GLYPH_ARROW_LEFT),
                ansi_normal(),
@@ -2748,6 +2750,17 @@ static void monitor_query_dump(JsonVariant *v) {
                      streq_ptr(state, "rcode-failure") ? dns_rcode_to_string(rcode) :
                      state));
 
+        if (!isempty(result))
+                printf(": %s", result);
+
+        if (ede_code >= 0)
+                printf(" (%s%s%s)",
+                       FORMAT_DNS_EDE_RCODE(ede_code),
+                       !isempty(ede_msg) ? ": " : "",
+                       strempty(ede_msg));
+
+        puts("");
+
         print_answer(answer);
 }
 
@@ -2856,7 +2869,7 @@ static int dump_cache_item(JsonVariant *item) {
         _cleanup_(dns_resource_key_unrefp) DnsResourceKey *k = NULL;
         int r, c = 0;
 
-        r = json_dispatch(item, dispatch_table, JSON_LOG, &item_info);
+        r = json_dispatch(item, dispatch_table, JSON_LOG|JSON_ALLOW_EXTENSIONS, &item_info);
         if (r < 0)
                 return r;
 
@@ -2918,7 +2931,7 @@ static int dump_cache_scope(JsonVariant *scope) {
                 {},
         };
 
-        r = json_dispatch(scope, dispatch_table, JSON_LOG, &scope_info);
+        r = json_dispatch(scope, dispatch_table, JSON_LOG|JSON_ALLOW_EXTENSIONS, &scope_info);
         if (r < 0)
                 return r;
 
@@ -3034,7 +3047,7 @@ static int dump_server_state(JsonVariant *server) {
                 {},
         };
 
-        r = json_dispatch(server, dispatch_table, JSON_LOG|JSON_PERMISSIVE, &server_state);
+        r = json_dispatch(server, dispatch_table, JSON_LOG|JSON_ALLOW_EXTENSIONS, &server_state);
         if (r < 0)
                 return r;
 
index ef3f5237a9ea131aa2a2aed458db83611048458b..d9d967ec400c7fa589362d759835c53c281dbdf5 100644 (file)
@@ -145,8 +145,13 @@ static int reply_query_state(DnsQuery *q) {
                 return reply_method_errorf(q, BUS_ERROR_ABORTED, "Query aborted");
 
         case DNS_TRANSACTION_DNSSEC_FAILED:
-                return reply_method_errorf(q, BUS_ERROR_DNSSEC_FAILED, "DNSSEC validation failed: %s",
-                                           dnssec_result_to_string(q->answer_dnssec_result));
+                return reply_method_errorf(q, BUS_ERROR_DNSSEC_FAILED, "DNSSEC validation failed: %s%s%s%s%s%s",
+                                           dnssec_result_to_string(q->answer_dnssec_result),
+                                           q->answer_ede_rcode >= 0 ? " (" : "",
+                                           q->answer_ede_rcode >= 0 ? FORMAT_DNS_EDE_RCODE(q->answer_ede_rcode) : "",
+                                           (q->answer_ede_rcode >= 0 && !isempty(q->answer_ede_msg)) ? ": " : "",
+                                           q->answer_ede_rcode >= 0 ? strempty(q->answer_ede_msg) : "",
+                                           q->answer_ede_rcode >= 0 ? ")" : "");
 
         case DNS_TRANSACTION_NO_TRUST_ANCHOR:
                 return reply_method_errorf(q, BUS_ERROR_NO_TRUST_ANCHOR, "No suitable trust anchor known");
@@ -183,7 +188,13 @@ static int reply_query_state(DnsQuery *q) {
 
                         rc = FORMAT_DNS_RCODE(q->answer_rcode);
                         n = strjoina(_BUS_ERROR_DNS, rc);
-                        sd_bus_error_setf(&error, n, "Could not resolve '%s', server or network returned error %s", dns_query_string(q), rc);
+                        sd_bus_error_setf(&error, n, "Could not resolve '%s', server or network returned error: %s%s%s%s%s%s",
+                                          dns_query_string(q), rc,
+                                          q->answer_ede_rcode >= 0 ? " (" : "",
+                                          q->answer_ede_rcode >= 0 ? FORMAT_DNS_EDE_RCODE(q->answer_ede_rcode) : "",
+                                          (q->answer_ede_rcode >= 0 && !isempty(q->answer_ede_msg)) ? ": " : "",
+                                          q->answer_ede_rcode >= 0 ? strempty(q->answer_ede_msg) : "",
+                                          q->answer_ede_rcode >= 0 ? ")" : "");
                 }
 
                 return sd_bus_reply_method_error(req, &error);
index 2f08ed0fe67dfd87200d18a803e8b1579fc2fd87..182ac20c3f83754cf5cf40a0919c22b619a1f3f5 100644 (file)
@@ -362,7 +362,7 @@ int config_parse_dnssd_txt(
 
                 case DNS_TXT_ITEM_DATA:
                         if (value) {
-                                r = unbase64mem(value, strlen(value), &decoded, &length);
+                                r = unbase64mem(value, &decoded, &length);
                                 if (r == -ENOMEM)
                                         return log_oom();
                                 if (r < 0) {
index 3c9b90c89b229f6d62423859f13d8b983665d7ce..8788bd6b0bfd4c7eea5bfd86212641193f8a6752 100644 (file)
@@ -2564,6 +2564,7 @@ static const char* const dnssec_result_table[_DNSSEC_RESULT_MAX] = {
         [DNSSEC_FAILED_AUXILIARY]      = "failed-auxiliary",
         [DNSSEC_NSEC_MISMATCH]         = "nsec-mismatch",
         [DNSSEC_INCOMPATIBLE_SERVER]   = "incompatible-server",
+        [DNSSEC_UPSTREAM_FAILURE]      = "upstream-failure",
 };
 DEFINE_STRING_TABLE_LOOKUP(dnssec_result, DnssecResult);
 
index 954bb3ef9de0b2f7208396bfb87cb74309df2b13..2f93a7f5852fbc279a9244d4e5be5398de107200 100644 (file)
@@ -20,11 +20,12 @@ enum DnssecResult {
         DNSSEC_NO_SIGNATURE,
         DNSSEC_MISSING_KEY,
 
-        /* These two are added by the DnsTransaction logic */
+        /* These five are added by the DnsTransaction logic */
         DNSSEC_UNSIGNED,
         DNSSEC_FAILED_AUXILIARY,
         DNSSEC_NSEC_MISMATCH,
         DNSSEC_INCOMPATIBLE_SERVER,
+        DNSSEC_UPSTREAM_FAILURE,
 
         _DNSSEC_RESULT_MAX,
         _DNSSEC_RESULT_INVALID = -EINVAL,
index c1c88550d5b3a1ae6e6e94b97745f11d3c0e08d4..44e1e4faabf3390bf7f0cbe4c7a84a673421c162 100644 (file)
@@ -6,6 +6,7 @@
 
 #include "alloc-util.h"
 #include "dns-domain.h"
+#include "escape.h"
 #include "memory-util.h"
 #include "resolved-dns-packet.h"
 #include "set.h"
@@ -309,9 +310,23 @@ int dns_packet_validate_query(DnsPacket *p) {
 
         switch (p->protocol) {
 
-        case DNS_PROTOCOL_LLMNR:
         case DNS_PROTOCOL_DNS:
-                if (DNS_PACKET_TC(p)) /* mDNS query may have truncation flag. */
+                if (DNS_PACKET_TC(p))
+                        return -EBADMSG;
+
+                if (DNS_PACKET_QDCOUNT(p) != 1)
+                        return -EBADMSG;
+
+                if (DNS_PACKET_ANCOUNT(p) > 0)
+                        return -EBADMSG;
+
+                /* Note, in most cases, DNS query packet does not have authority section. But some query
+                 * types, e.g. IXFR, have Authority sections. Hence, unlike the check for LLMNR, we do not
+                 * check DNS_PACKET_NSCOUNT(p) here. */
+                break;
+
+        case DNS_PROTOCOL_LLMNR:
+                if (DNS_PACKET_TC(p))
                         return -EBADMSG;
 
                 /* RFC 4795, Section 2.1.1. says to discard all queries with QDCOUNT != 1 */
@@ -329,6 +344,9 @@ int dns_packet_validate_query(DnsPacket *p) {
                 break;
 
         case DNS_PROTOCOL_MDNS:
+                /* Note, mDNS query may have truncation flag. So, unlike the check for DNS and LLMNR,
+                 * we do not check DNS_PACKET_TC(p) here. */
+
                 /* RFC 6762, Section 18 specifies that messages with non-zero RCODE
                  * must be silently ignored, and that we must ignore the values of
                  * AA, RD, RA, AD, and CD bits. */
@@ -775,7 +793,7 @@ int dns_packet_append_opt(
 
                 static const uint8_t rfc6975[] = {
 
-                        0, 5, /* OPTION_CODE: DAU */
+                        0, DNS_EDNS_OPT_DAU, /* OPTION_CODE */
 #if PREFER_OPENSSL || (HAVE_GCRYPT && GCRYPT_VERSION_NUMBER >= 0x010600)
                         0, 7, /* LIST_LENGTH */
 #else
@@ -791,13 +809,13 @@ int dns_packet_append_opt(
                         DNSSEC_ALGORITHM_ED25519,
 #endif
 
-                        0, 6, /* OPTION_CODE: DHU */
+                        0, DNS_EDNS_OPT_DHU, /* OPTION_CODE */
                         0, 3, /* LIST_LENGTH */
                         DNSSEC_DIGEST_SHA1,
                         DNSSEC_DIGEST_SHA256,
                         DNSSEC_DIGEST_SHA384,
 
-                        0, 7, /* OPTION_CODE: N3U */
+                        0, DNS_EDNS_OPT_N3U, /* OPTION_CODE */
                         0, 1, /* LIST_LENGTH */
                         NSEC3_ALGORITHM_SHA1,
                 };
@@ -2180,7 +2198,7 @@ static bool opt_is_good(DnsResourceRecord *rr, bool *rfc6975) {
                         return false;
 
                 /* RFC 6975 DAU, DHU or N3U fields found. */
-                if (IN_SET(option_code, 5, 6, 7))
+                if (IN_SET(option_code, DNS_EDNS_OPT_DAU, DNS_EDNS_OPT_DHU, DNS_EDNS_OPT_N3U))
                         found_dau_dhu_n3u = true;
 
                 p += option_length + 4U;
@@ -2570,6 +2588,85 @@ bool dns_packet_equal(const DnsPacket *a, const DnsPacket *b) {
         return dns_packet_compare_func(a, b) == 0;
 }
 
+int dns_packet_ede_rcode(DnsPacket *p, int *ret_ede_rcode, char **ret_ede_msg) {
+        const uint8_t *d;
+        size_t l;
+        int r;
+
+        assert(p);
+
+        if (!p->opt)
+                return -ENOENT;
+
+        d = p->opt->opt.data;
+        l = p->opt->opt.data_size;
+
+        while (l > 0) {
+                uint16_t code, length;
+
+                if (l < 4U)
+                        return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
+                                               "EDNS0 variable part has invalid size.");
+
+                code = unaligned_read_be16(d);
+                length = unaligned_read_be16(d + 2);
+
+                if (l < 4U + length)
+                        return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
+                                               "Truncated option in EDNS0 variable part.");
+
+                if (code == DNS_EDNS_OPT_EXT_ERROR) {
+                        _cleanup_free_ char *msg = NULL;
+
+                        if (length < 2U)
+                                return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
+                                                       "EDNS0 truncated EDE info code.");
+
+                        r = make_cstring((char *) d + 6, length - 2U, MAKE_CSTRING_ALLOW_TRAILING_NUL, &msg);
+                        if (r < 0)
+                                return log_debug_errno(r, "Invalid EDE text in opt.");
+
+                        if (ret_ede_msg) {
+                                if (!utf8_is_valid(msg)) {
+                                        _cleanup_free_ char *msg_escaped = NULL;
+
+                                        msg_escaped = cescape(msg);
+                                        if (!msg_escaped)
+                                                return log_oom_debug();
+
+                                        *ret_ede_msg = TAKE_PTR(msg_escaped);
+                                } else
+                                        *ret_ede_msg = TAKE_PTR(msg);
+                        }
+
+                        if (ret_ede_rcode)
+                                *ret_ede_rcode = unaligned_read_be16(d + 4);
+
+                        return 0;
+                }
+
+                d += 4U + length;
+                l -= 4U + length;
+        }
+
+        return -ENOENT;
+}
+
+bool dns_ede_rcode_is_dnssec(int ede_rcode) {
+        return IN_SET(ede_rcode,
+                        DNS_EDE_RCODE_UNSUPPORTED_DNSKEY_ALG,
+                        DNS_EDE_RCODE_UNSUPPORTED_DS_DIGEST,
+                        DNS_EDE_RCODE_DNSSEC_INDETERMINATE,
+                        DNS_EDE_RCODE_DNSSEC_BOGUS,
+                        DNS_EDE_RCODE_SIG_EXPIRED,
+                        DNS_EDE_RCODE_SIG_NOT_YET_VALID,
+                        DNS_EDE_RCODE_DNSKEY_MISSING,
+                        DNS_EDE_RCODE_RRSIG_MISSING,
+                        DNS_EDE_RCODE_NO_ZONE_KEY_BIT,
+                        DNS_EDE_RCODE_NSEC_MISSING
+                     );
+}
+
 int dns_packet_has_nsid_request(DnsPacket *p) {
         bool has_nsid = false;
         const uint8_t *d;
@@ -2597,7 +2694,7 @@ int dns_packet_has_nsid_request(DnsPacket *p) {
                         return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
                                                "Truncated option in EDNS0 variable part.");
 
-                if (code == 3) {
+                if (code == DNS_EDNS_OPT_NSID) {
                         if (has_nsid)
                                 return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
                                                        "Duplicate NSID option in EDNS0 variable part.");
@@ -2642,6 +2739,7 @@ static const char* const dns_rcode_table[_DNS_RCODE_MAX_DEFINED] = {
         [DNS_RCODE_NXRRSET]   = "NXRRSET",
         [DNS_RCODE_NOTAUTH]   = "NOTAUTH",
         [DNS_RCODE_NOTZONE]   = "NOTZONE",
+        [DNS_RCODE_DSOTYPENI] = "DSOTYPENI",
         [DNS_RCODE_BADVERS]   = "BADVERS",
         [DNS_RCODE_BADKEY]    = "BADKEY",
         [DNS_RCODE_BADTIME]   = "BADTIME",
@@ -2661,6 +2759,48 @@ const char *format_dns_rcode(int i, char buf[static DECIMAL_STR_MAX(int)]) {
         return snprintf_ok(buf, DECIMAL_STR_MAX(int), "%i", i);
 }
 
+static const char* const dns_ede_rcode_table[_DNS_EDE_RCODE_MAX_DEFINED] = {
+        [DNS_EDE_RCODE_OTHER]                  = "Other",
+        [DNS_EDE_RCODE_UNSUPPORTED_DNSKEY_ALG] = "Unsupported DNSKEY Algorithm",
+        [DNS_EDE_RCODE_UNSUPPORTED_DS_DIGEST]  = "Unsupported DS Digest Type",
+        [DNS_EDE_RCODE_STALE_ANSWER]           = "Stale Answer",
+        [DNS_EDE_RCODE_FORGED_ANSWER]          = "Forged Answer",
+        [DNS_EDE_RCODE_DNSSEC_INDETERMINATE]   = "DNSSEC Indeterminate",
+        [DNS_EDE_RCODE_DNSSEC_BOGUS]           = "DNSSEC Bogus",
+        [DNS_EDE_RCODE_SIG_EXPIRED]            = "Signature Expired",
+        [DNS_EDE_RCODE_SIG_NOT_YET_VALID]      = "Signature Not Yet Valid",
+        [DNS_EDE_RCODE_DNSKEY_MISSING]         = "DNSKEY Missing",
+        [DNS_EDE_RCODE_RRSIG_MISSING]          = "RRSIG Missing",
+        [DNS_EDE_RCODE_NO_ZONE_KEY_BIT]        = "No Zone Key Bit Set",
+        [DNS_EDE_RCODE_NSEC_MISSING]           = "NSEC Missing",
+        [DNS_EDE_RCODE_CACHED_ERROR]           = "Cached Error",
+        [DNS_EDE_RCODE_NOT_READY]              = "Not Ready",
+        [DNS_EDE_RCODE_BLOCKED]                = "Blocked",
+        [DNS_EDE_RCODE_CENSORED]               = "Censored",
+        [DNS_EDE_RCODE_FILTERED]               = "Filtered",
+        [DNS_EDE_RCODE_PROHIBITIED]            = "Prohibited",
+        [DNS_EDE_RCODE_STALE_NXDOMAIN_ANSWER]  = "Stale NXDOMAIN Answer",
+        [DNS_EDE_RCODE_NOT_AUTHORITATIVE]      = "Not Authoritative",
+        [DNS_EDE_RCODE_NOT_SUPPORTED]          = "Not Supported",
+        [DNS_EDE_RCODE_UNREACH_AUTHORITY]      = "No Reachable Authority",
+        [DNS_EDE_RCODE_NET_ERROR]              = "Network Error",
+        [DNS_EDE_RCODE_INVALID_DATA]           = "Invalid Data",
+        [DNS_EDE_RCODE_SIG_NEVER]              = "Signature Never Valid",
+        [DNS_EDE_RCODE_TOO_EARLY]              = "Too Early",
+        [DNS_EDE_RCODE_UNSUPPORTED_NSEC3_ITER] = "Unsupported NSEC3 Iterations",
+        [DNS_EDE_RCODE_TRANSPORT_POLICY]       = "Impossible Transport Policy",
+        [DNS_EDE_RCODE_SYNTHESIZED]            = "Synthesized",
+};
+DEFINE_STRING_TABLE_LOOKUP_TO_STRING(dns_ede_rcode, int);
+
+const char *format_dns_ede_rcode(int i, char buf[static DECIMAL_STR_MAX(int)]) {
+        const char *p = dns_ede_rcode_to_string(i);
+        if (p)
+                return p;
+
+        return snprintf_ok(buf, DECIMAL_STR_MAX(int), "%i", i);
+}
+
 static const char* const dns_protocol_table[_DNS_PROTOCOL_MAX] = {
         [DNS_PROTOCOL_DNS]   = "dns",
         [DNS_PROTOCOL_MDNS]  = "mdns",
index a6af44c6ec0ac55426c6cb36bed9b5aaf5db4c11..705fc511d6bd9bc9b3ccfef95de77db27d0bb0af 100644 (file)
@@ -253,32 +253,100 @@ int dns_packet_extract(DnsPacket *p);
 
 bool dns_packet_equal(const DnsPacket *a, const DnsPacket *b);
 
+int dns_packet_ede_rcode(DnsPacket *p, int *ret_ede_rcode, char **ret_ede_msg);
+bool dns_ede_rcode_is_dnssec(int ede_rcode);
 int dns_packet_has_nsid_request(DnsPacket *p);
 
 /* https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml#dns-parameters-6 */
 enum {
-        DNS_RCODE_SUCCESS = 0,
-        DNS_RCODE_FORMERR = 1,
-        DNS_RCODE_SERVFAIL = 2,
-        DNS_RCODE_NXDOMAIN = 3,
-        DNS_RCODE_NOTIMP = 4,
-        DNS_RCODE_REFUSED = 5,
-        DNS_RCODE_YXDOMAIN = 6,
-        DNS_RCODE_YXRRSET = 7,
-        DNS_RCODE_NXRRSET = 8,
-        DNS_RCODE_NOTAUTH = 9,
-        DNS_RCODE_NOTZONE = 10,
-        DNS_RCODE_BADVERS = 16,
-        DNS_RCODE_BADSIG = 16, /* duplicate value! */
-        DNS_RCODE_BADKEY = 17,
-        DNS_RCODE_BADTIME = 18,
-        DNS_RCODE_BADMODE = 19,
-        DNS_RCODE_BADNAME = 20,
-        DNS_RCODE_BADALG = 21,
-        DNS_RCODE_BADTRUNC = 22,
-        DNS_RCODE_BADCOOKIE = 23,
+        DNS_RCODE_SUCCESS       = 0,
+        DNS_RCODE_FORMERR       = 1,
+        DNS_RCODE_SERVFAIL      = 2,
+        DNS_RCODE_NXDOMAIN      = 3,
+        DNS_RCODE_NOTIMP        = 4,
+        DNS_RCODE_REFUSED       = 5,
+        DNS_RCODE_YXDOMAIN      = 6,
+        DNS_RCODE_YXRRSET       = 7,
+        DNS_RCODE_NXRRSET       = 8,
+        DNS_RCODE_NOTAUTH       = 9,
+        DNS_RCODE_NOTZONE       = 10,
+        DNS_RCODE_DSOTYPENI     = 11,
+        /* 12-15 are unassigned. */
+        DNS_RCODE_BADVERS       = 16,
+        DNS_RCODE_BADSIG        = 16, /* duplicate value! */
+        DNS_RCODE_BADKEY        = 17,
+        DNS_RCODE_BADTIME       = 18,
+        DNS_RCODE_BADMODE       = 19,
+        DNS_RCODE_BADNAME       = 20,
+        DNS_RCODE_BADALG        = 21,
+        DNS_RCODE_BADTRUNC      = 22,
+        DNS_RCODE_BADCOOKIE     = 23,
+        /* 24-3840 are unassigned. */
+        /* 3841-4095 are for private use. */
+        /* 4096-65534 are unassigned. */
         _DNS_RCODE_MAX_DEFINED,
-        _DNS_RCODE_MAX = 4095 /* 4 bit rcode in the header plus 8 bit rcode in OPT, makes 12 bit */
+        _DNS_RCODE_MAX          = 65535, /* reserved */
+        _DNS_RCODE_INVALID      = -EINVAL,
+};
+
+/* https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml#dns-parameters-11 */
+enum {
+        DNS_EDNS_OPT_RESERVED      = 0,  /* RFC 6891 */
+        DNS_EDNS_OPT_LLQ           = 1,  /* RFC 8764 */
+        DNS_EDNS_OPT_UL            = 2,
+        DNS_EDNS_OPT_NSID          = 3,  /* RFC 5001 */
+        /* DNS_EDNS_OPT_RESERVED   = 4 */
+        DNS_EDNS_OPT_DAU           = 5,  /* RFC 6975 */
+        DNS_EDNS_OPT_DHU           = 6,  /* RFC 6975 */
+        DNS_EDNS_OPT_N3U           = 7,  /* RFC 6975 */
+        DNS_EDNS_OPT_CLIENT_SUBNET = 8,  /* RFC 7871 */
+        DNS_EDNS_OPT_EXPIRE        = 9,  /* RFC 7314 */
+        DNS_EDNS_OPT_COOKIE        = 10, /* RFC 7873 */
+        DNS_EDNS_OPT_TCP_KEEPALIVE = 11, /* RFC 7828 */
+        DNS_EDNS_OPT_PADDING       = 12, /* RFC 7830 */
+        DNS_EDNS_OPT_CHAIN         = 13, /* RFC 7901 */
+        DNS_EDNS_OPT_KEY_TAG       = 14, /* RFC 8145 */
+        DNS_EDNS_OPT_EXT_ERROR     = 15, /* RFC 8914 */
+        DNS_EDNS_OPT_CLIENT_TAG    = 16,
+        DNS_EDNS_OPT_SERVER_TAG    = 17,
+        _DNS_EDNS_OPT_MAX_DEFINED,
+        _DNS_EDNS_OPT_INVALID      = -EINVAL,
+};
+
+/* https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml#extended-dns-error-codes */
+enum {
+        DNS_EDE_RCODE_OTHER                  = 0,  /* RFC 8914, Section 4.1 */
+        DNS_EDE_RCODE_UNSUPPORTED_DNSKEY_ALG = 1,  /* RFC 8914, Section 4.2 */
+        DNS_EDE_RCODE_UNSUPPORTED_DS_DIGEST  = 2,  /* RFC 8914, Section 4.3 */
+        DNS_EDE_RCODE_STALE_ANSWER           = 3,  /* RFC 8914, Section 4.4 */
+        DNS_EDE_RCODE_FORGED_ANSWER          = 4,  /* RFC 8914, Section 4.5 */
+        DNS_EDE_RCODE_DNSSEC_INDETERMINATE   = 5,  /* RFC 8914, Section 4.6 */
+        DNS_EDE_RCODE_DNSSEC_BOGUS           = 6,  /* RFC 8914, Section 4.7 */
+        DNS_EDE_RCODE_SIG_EXPIRED            = 7,  /* RFC 8914, Section 4.8 */
+        DNS_EDE_RCODE_SIG_NOT_YET_VALID      = 8,  /* RFC 8914, Section 4.9 */
+        DNS_EDE_RCODE_DNSKEY_MISSING         = 9,  /* RFC 8914, Section 4.10 */
+        DNS_EDE_RCODE_RRSIG_MISSING          = 10, /* RFC 8914, Section 4.11 */
+        DNS_EDE_RCODE_NO_ZONE_KEY_BIT        = 11, /* RFC 8914, Section 4.12 */
+        DNS_EDE_RCODE_NSEC_MISSING           = 12, /* RFC 8914, Section 4.13 */
+        DNS_EDE_RCODE_CACHED_ERROR           = 13, /* RFC 8914, Section 4.14 */
+        DNS_EDE_RCODE_NOT_READY              = 14, /* RFC 8914, Section 4.15 */
+        DNS_EDE_RCODE_BLOCKED                = 15, /* RFC 8914, Section 4.16 */
+        DNS_EDE_RCODE_CENSORED               = 16, /* RFC 8914, Section 4.17 */
+        DNS_EDE_RCODE_FILTERED               = 17, /* RFC 8914, Section 4.18 */
+        DNS_EDE_RCODE_PROHIBITIED            = 18, /* RFC 8914, Section 4.19 */
+        DNS_EDE_RCODE_STALE_NXDOMAIN_ANSWER  = 19, /* RFC 8914, Section 4.20 */
+        DNS_EDE_RCODE_NOT_AUTHORITATIVE      = 20, /* RFC 8914, Section 4.21 */
+        DNS_EDE_RCODE_NOT_SUPPORTED          = 21, /* RFC 8914, Section 4.22 */
+        DNS_EDE_RCODE_UNREACH_AUTHORITY      = 22, /* RFC 8914, Section 4.23 */
+        DNS_EDE_RCODE_NET_ERROR              = 23, /* RFC 8914, Section 4.24 */
+        DNS_EDE_RCODE_INVALID_DATA           = 24, /* RFC 8914, Section 4.25 */
+        DNS_EDE_RCODE_SIG_NEVER              = 25,
+        DNS_EDE_RCODE_TOO_EARLY              = 26, /* RFC 9250 */
+        DNS_EDE_RCODE_UNSUPPORTED_NSEC3_ITER = 27, /* RFC 9276 */
+        DNS_EDE_RCODE_TRANSPORT_POLICY       = 28,
+        DNS_EDE_RCODE_SYNTHESIZED            = 29,
+        _DNS_EDE_RCODE_MAX_DEFINED,
+        _DNS_EDE_RCODE_INVALID               = -EINVAL,
 };
 
 const char* dns_rcode_to_string(int i) _const_;
@@ -286,6 +354,10 @@ int dns_rcode_from_string(const char *s) _pure_;
 const char *format_dns_rcode(int i, char buf[static DECIMAL_STR_MAX(int)]);
 #define FORMAT_DNS_RCODE(i) format_dns_rcode(i, (char [DECIMAL_STR_MAX(int)]) {})
 
+const char* dns_ede_rcode_to_string(int i) _const_;
+const char *format_dns_ede_rcode(int i, char buf[static DECIMAL_STR_MAX(int)]);
+#define FORMAT_DNS_EDE_RCODE(i) format_dns_ede_rcode(i, (char [DECIMAL_STR_MAX(int)]) {})
+
 const char* dns_protocol_to_string(DnsProtocol p) _const_;
 DnsProtocol dns_protocol_from_string(const char *s) _pure_;
 
index 7eb6b9736e2465014751414608c63431a5fec5d0..938dd61a6a772636808302d75460d56a4c592417 100644 (file)
@@ -368,6 +368,8 @@ static void dns_query_reset_answer(DnsQuery *q) {
 
         q->answer = dns_answer_unref(q->answer);
         q->answer_rcode = 0;
+        q->answer_ede_rcode = _DNS_EDE_RCODE_INVALID;
+        q->answer_ede_msg = mfree(q->answer_ede_msg);
         q->answer_dnssec_result = _DNSSEC_RESULT_INVALID;
         q->answer_errno = 0;
         q->answer_query_flags = 0;
@@ -514,6 +516,7 @@ int dns_query_new(
                 .question_bypass = dns_packet_ref(question_bypass),
                 .ifindex = ifindex,
                 .flags = flags,
+                .answer_ede_rcode = _DNS_EDE_RCODE_INVALID,
                 .answer_dnssec_result = _DNSSEC_RESULT_INVALID,
                 .answer_protocol = _DNS_PROTOCOL_INVALID,
                 .answer_family = AF_UNSPEC,
@@ -586,7 +589,7 @@ void dns_query_complete(DnsQuery *q, DnsTransactionState state) {
 
         q->state = state;
 
-        (void) manager_monitor_send(q->manager, q->state, q->answer_rcode, q->answer_errno, q->question_idna, q->question_utf8, q->question_bypass, q->collected_questions, q->answer);
+        (void) manager_monitor_send(q->manager, q);
 
         dns_query_stop(q);
         if (q->complete)
@@ -898,6 +901,10 @@ static void dns_query_accept(DnsQuery *q, DnsQueryCandidate *c) {
 
                         DNS_ANSWER_REPLACE(q->answer, dns_answer_ref(t->answer));
                         q->answer_rcode = t->answer_rcode;
+                        q->answer_ede_rcode = t->answer_ede_rcode;
+                        r = free_and_strdup_warn(&q->answer_ede_msg, t->answer_ede_msg);
+                        if (r < 0)
+                                goto fail;
                         q->answer_dnssec_result = t->answer_dnssec_result;
                         q->answer_query_flags = t->answer_query_flags | dns_transaction_source_to_query_flags(t->answer_source);
                         q->answer_errno = t->answer_errno;
index 2723299bee5818e514c02afe2ccfc246d1eea148..29d7288981fa7e020650ca0d2af8660be7afb058 100644 (file)
@@ -73,6 +73,8 @@ struct DnsQuery {
         /* Discovered data */
         DnsAnswer *answer;
         int answer_rcode;
+        int answer_ede_rcode;
+        char *answer_ede_msg;
         DnssecResult answer_dnssec_result;
         uint64_t answer_query_flags;
         DnsProtocol answer_protocol;
index e4a1a29071c2a44a62b9a000dbf8a4aab6456ef4..307630f3c7a1618a5ee99852f7c645cf497b0025 100644 (file)
@@ -28,6 +28,8 @@ static void dns_transaction_reset_answer(DnsTransaction *t) {
         t->received = dns_packet_unref(t->received);
         t->answer = dns_answer_unref(t->answer);
         t->answer_rcode = 0;
+        t->answer_ede_rcode = _DNS_EDE_RCODE_INVALID;
+        t->answer_ede_msg = mfree(t->answer_ede_msg);
         t->answer_dnssec_result = _DNSSEC_RESULT_INVALID;
         t->answer_source = _DNS_TRANSACTION_SOURCE_INVALID;
         t->answer_query_flags = 0;
@@ -284,6 +286,7 @@ int dns_transaction_new(
                 .dns_udp_fd = -EBADF,
                 .answer_source = _DNS_TRANSACTION_SOURCE_INVALID,
                 .answer_dnssec_result = _DNSSEC_RESULT_INVALID,
+                .answer_ede_rcode = _DNS_EDE_RCODE_INVALID,
                 .answer_nsec_ttl = UINT32_MAX,
                 .key = dns_resource_key_ref(key),
                 .query_flags = query_flags,
@@ -885,8 +888,21 @@ static int dns_transaction_dnssec_ready(DnsTransaction *t) {
                         /* We handle DNSSEC failures different from other errors, as we care about the DNSSEC
                          * validation result */
 
-                        log_debug("Auxiliary DNSSEC RR query failed validation: %s", dnssec_result_to_string(dt->answer_dnssec_result));
-                        t->answer_dnssec_result = dt->answer_dnssec_result; /* Copy error code over */
+                        log_debug("Auxiliary DNSSEC RR query failed validation: %s%s%s%s%s%s",
+                                  dnssec_result_to_string(dt->answer_dnssec_result),
+                                  dt->answer_ede_rcode >= 0 ? " (" : "",
+                                  dt->answer_ede_rcode >= 0 ? FORMAT_DNS_EDE_RCODE(dt->answer_ede_rcode) : "",
+                                  (dt->answer_ede_rcode >= 0 && !isempty(dt->answer_ede_msg)) ? ": " : "",
+                                  dt->answer_ede_rcode >= 0 ? strempty(dt->answer_ede_msg) : "",
+                                  dt->answer_ede_rcode >= 0 ? ")" : "");
+
+                        /* Copy error code over */
+                        t->answer_dnssec_result = dt->answer_dnssec_result;
+                        t->answer_ede_rcode = dt->answer_ede_rcode;
+                        r = free_and_strdup(&t->answer_ede_msg, dt->answer_ede_msg);
+                        if (r < 0)
+                                log_oom_debug();
+
                         dns_transaction_complete(t, DNS_TRANSACTION_DNSSEC_FAILED);
                         return 0;
 
@@ -1125,13 +1141,130 @@ void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p, bool encrypt
                 }
         }
 
+        if (DNS_PACKET_TC(p)) {
+
+                /* Truncated packets for mDNS are not allowed. Give up immediately. */
+                if (t->scope->protocol == DNS_PROTOCOL_MDNS) {
+                        dns_transaction_complete(t, DNS_TRANSACTION_INVALID_REPLY);
+                        return;
+                }
+
+                /* Response was truncated, let's try again with good old TCP */
+                log_debug("Reply truncated, retrying via TCP.");
+                retry_with_tcp = true;
+
+        } else if (t->scope->protocol == DNS_PROTOCOL_DNS &&
+                   DNS_PACKET_IS_FRAGMENTED(p)) {
+
+                /* Report the fragment size, so that we downgrade from LARGE to regular EDNS0 if needed */
+                if (t->server)
+                        dns_server_packet_udp_fragmented(t->server, dns_packet_size_unfragmented(p));
+
+                if (t->current_feature_level > DNS_SERVER_FEATURE_LEVEL_UDP) {
+                        /* Packet was fragmented. Let's retry with TCP to avoid fragmentation attack
+                         * issues. (We don't do that on the lowest feature level however, since crappy DNS
+                         * servers often do not implement TCP, hence falling back to TCP on fragmentation is
+                         * counter-productive there.) */
+
+                        log_debug("Reply fragmented, retrying via TCP. (Largest fragment size: %zu; Datagram size: %zu)",
+                                  p->fragsize, p->size);
+                        retry_with_tcp = true;
+                }
+        }
+
+        if (retry_with_tcp) {
+                r = dns_transaction_emit_tcp(t);
+                if (r == -ESRCH) {
+                        /* No servers found? Damn! */
+                        dns_transaction_complete(t, DNS_TRANSACTION_NO_SERVERS);
+                        return;
+                }
+                if (r == -EOPNOTSUPP) {
+                        /* Tried to ask for DNSSEC RRs, on a server that doesn't do DNSSEC  */
+                        dns_transaction_complete(t, DNS_TRANSACTION_RR_TYPE_UNSUPPORTED);
+                        return;
+                }
+                if (r < 0) {
+                        /* On LLMNR, if we cannot connect to the host,
+                         * we immediately give up */
+                        if (t->scope->protocol != DNS_PROTOCOL_DNS)
+                                goto fail;
+
+                        /* On DNS, couldn't send? Try immediately again, with a new server */
+                        if (dns_transaction_limited_retry(t))
+                                return;
+
+                        /* No new server to try, give up */
+                        dns_transaction_complete(t, DNS_TRANSACTION_ATTEMPTS_MAX_REACHED);
+                }
+
+                return;
+        }
+
+        /* After the superficial checks, actually parse the message. */
+        r = dns_packet_extract(p);
+        if (r < 0) {
+                if (t->server) {
+                        dns_server_packet_invalid(t->server, t->current_feature_level);
+
+                        r = dns_transaction_maybe_restart(t);
+                        if (r < 0)
+                                goto fail;
+                        if (r > 0) /* Transaction got restarted... */
+                                return;
+                }
+
+                dns_transaction_complete(t, DNS_TRANSACTION_INVALID_REPLY);
+                return;
+        }
+
         switch (t->scope->protocol) {
 
-        case DNS_PROTOCOL_DNS:
+        case DNS_PROTOCOL_DNS: {
                 assert(t->server);
 
+                (void) dns_packet_ede_rcode(p, &t->answer_ede_rcode, &t->answer_ede_msg);
+
                 if (!t->bypass &&
                     IN_SET(DNS_PACKET_RCODE(p), DNS_RCODE_FORMERR, DNS_RCODE_SERVFAIL, DNS_RCODE_NOTIMP)) {
+                        /* If the server has replied with detailed error data, using a degraded feature set
+                         * will likely not help anyone. Examine the detailed error to determine the best
+                         * course of action. */
+                        if (t->answer_ede_rcode >= 0 && DNS_PACKET_RCODE(p) == DNS_RCODE_SERVFAIL) {
+                                /* These codes are related to DNSSEC configuration errors. If accurate,
+                                 * this is the domain operator's problem, and retrying won't help. */
+                                if (dns_ede_rcode_is_dnssec(t->answer_ede_rcode)) {
+                                        log_debug("Server returned error: %s (%s%s%s). Lookup failed.",
+                                                  FORMAT_DNS_RCODE(DNS_PACKET_RCODE(p)),
+                                                  FORMAT_DNS_EDE_RCODE(t->answer_ede_rcode),
+                                                  isempty(t->answer_ede_msg) ? "" : ": ",
+                                                  strempty(t->answer_ede_msg));
+
+                                        t->answer_dnssec_result = DNSSEC_UPSTREAM_FAILURE;
+                                        dns_transaction_complete(t, DNS_TRANSACTION_DNSSEC_FAILED);
+                                        return;
+                                }
+
+                                /* These codes probably indicate a transient error. Let's try again. */
+                                if (IN_SET(t->answer_ede_rcode, DNS_EDE_RCODE_NOT_READY, DNS_EDE_RCODE_NET_ERROR)) {
+                                        log_debug("Server returned error: %s (%s%s%s), retrying transaction.",
+                                                  FORMAT_DNS_RCODE(DNS_PACKET_RCODE(p)),
+                                                  FORMAT_DNS_EDE_RCODE(t->answer_ede_rcode),
+                                                  isempty(t->answer_ede_msg) ? "" : ": ",
+                                                  strempty(t->answer_ede_msg));
+                                        dns_transaction_retry(t, false);
+                                        return;
+                                }
+
+                                /* OK, the query failed, but we still shouldn't degrade the feature set for
+                                 * this server. */
+                                log_debug("Server returned error: %s (%s%s%s)",
+                                          FORMAT_DNS_RCODE(DNS_PACKET_RCODE(p)),
+                                          FORMAT_DNS_EDE_RCODE(t->answer_ede_rcode),
+                                          isempty(t->answer_ede_msg) ? "" : ": ",
+                                          strempty(t->answer_ede_msg));
+                                break;
+                        }
 
                         /* Request failed, immediately try again with reduced features */
 
@@ -1188,7 +1321,11 @@ void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p, bool encrypt
 
                 if (DNS_PACKET_RCODE(p) == DNS_RCODE_REFUSED) {
                         /* This server refused our request? If so, try again, use a different server */
-                        log_debug("Server returned REFUSED, switching servers, and retrying.");
+                        if (t->answer_ede_rcode >= 0)
+                                log_debug("Server returned REFUSED (%s), switching servers, and retrying.",
+                                          FORMAT_DNS_EDE_RCODE(t->answer_ede_rcode));
+                        else
+                                log_debug("Server returned REFUSED, switching servers, and retrying.");
 
                         if (dns_transaction_limited_retry(t))
                                 return;
@@ -1200,6 +1337,7 @@ void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p, bool encrypt
                         dns_server_packet_truncated(t->server, t->current_feature_level);
 
                 break;
+        }
 
         case DNS_PROTOCOL_LLMNR:
         case DNS_PROTOCOL_MDNS:
@@ -1210,83 +1348,6 @@ void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p, bool encrypt
                 assert_not_reached();
         }
 
-        if (DNS_PACKET_TC(p)) {
-
-                /* Truncated packets for mDNS are not allowed. Give up immediately. */
-                if (t->scope->protocol == DNS_PROTOCOL_MDNS) {
-                        dns_transaction_complete(t, DNS_TRANSACTION_INVALID_REPLY);
-                        return;
-                }
-
-                /* Response was truncated, let's try again with good old TCP */
-                log_debug("Reply truncated, retrying via TCP.");
-                retry_with_tcp = true;
-
-        } else if (t->scope->protocol == DNS_PROTOCOL_DNS &&
-                   DNS_PACKET_IS_FRAGMENTED(p)) {
-
-                /* Report the fragment size, so that we downgrade from LARGE to regular EDNS0 if needed */
-                if (t->server)
-                        dns_server_packet_udp_fragmented(t->server, dns_packet_size_unfragmented(p));
-
-                if (t->current_feature_level > DNS_SERVER_FEATURE_LEVEL_UDP) {
-                        /* Packet was fragmented. Let's retry with TCP to avoid fragmentation attack
-                         * issues. (We don't do that on the lowest feature level however, since crappy DNS
-                         * servers often do not implement TCP, hence falling back to TCP on fragmentation is
-                         * counter-productive there.) */
-
-                        log_debug("Reply fragmented, retrying via TCP. (Largest fragment size: %zu; Datagram size: %zu)",
-                                  p->fragsize, p->size);
-                        retry_with_tcp = true;
-                }
-        }
-
-        if (retry_with_tcp) {
-                r = dns_transaction_emit_tcp(t);
-                if (r == -ESRCH) {
-                        /* No servers found? Damn! */
-                        dns_transaction_complete(t, DNS_TRANSACTION_NO_SERVERS);
-                        return;
-                }
-                if (r == -EOPNOTSUPP) {
-                        /* Tried to ask for DNSSEC RRs, on a server that doesn't do DNSSEC  */
-                        dns_transaction_complete(t, DNS_TRANSACTION_RR_TYPE_UNSUPPORTED);
-                        return;
-                }
-                if (r < 0) {
-                        /* On LLMNR, if we cannot connect to the host,
-                         * we immediately give up */
-                        if (t->scope->protocol != DNS_PROTOCOL_DNS)
-                                goto fail;
-
-                        /* On DNS, couldn't send? Try immediately again, with a new server */
-                        if (dns_transaction_limited_retry(t))
-                                return;
-
-                        /* No new server to try, give up */
-                        dns_transaction_complete(t, DNS_TRANSACTION_ATTEMPTS_MAX_REACHED);
-                }
-
-                return;
-        }
-
-        /* After the superficial checks, actually parse the message. */
-        r = dns_packet_extract(p);
-        if (r < 0) {
-                if (t->server) {
-                        dns_server_packet_invalid(t->server, t->current_feature_level);
-
-                        r = dns_transaction_maybe_restart(t);
-                        if (r < 0)
-                                goto fail;
-                        if (r > 0) /* Transaction got restarted... */
-                                return;
-                }
-
-                dns_transaction_complete(t, DNS_TRANSACTION_INVALID_REPLY);
-                return;
-        }
-
         if (t->server) {
                 /* Report that we successfully received a valid packet with a good rcode after we initially got a bad
                  * rcode and subsequently downgraded the protocol */
@@ -1760,8 +1821,12 @@ static int dns_transaction_prepare(DnsTransaction *t, usec_t ts) {
                                 t->answer_source = DNS_TRANSACTION_CACHE;
                                 if (t->answer_rcode == DNS_RCODE_SUCCESS)
                                         dns_transaction_complete(t, DNS_TRANSACTION_SUCCESS);
-                                else
+                                else {
+                                        if (t->received)
+                                                (void) dns_packet_ede_rcode(t->received, &t->answer_ede_rcode, &t->answer_ede_msg);
+
                                         dns_transaction_complete(t, DNS_TRANSACTION_RCODE_FAILURE);
+                                }
                                 return 0;
                         }
                 }
index 2fd8720e2407170f5fc220740abf8a5c5d9e575d..6de4cdd749122848d2382287604fa29ca6eed7df 100644 (file)
@@ -61,6 +61,8 @@ struct DnsTransaction {
 
         DnsAnswer *answer;
         int answer_rcode;
+        int answer_ede_rcode;
+        char *answer_ede_msg;
         DnssecResult answer_dnssec_result;
         DnsTransactionSource answer_source;
         uint32_t answer_nsec_ttl;
index 2156f4f685d27955cb3d24ffd7d1463d48a084f5..1e42cdddb1e3cb9e77d0dfca4272f1c7b84efb41 100644 (file)
@@ -279,7 +279,7 @@ static int dns_trust_anchor_load_positive(DnsTrustAnchor *d, const char *path, u
                         return -EINVAL;
                 }
 
-                r = unhexmem(p, strlen(p), &dd, &l);
+                r = unhexmem(p, &dd, &l);
                 if (r < 0) {
                         log_warning("Failed to parse DS digest %s on line %s:%u", p, path, line);
                         return -EINVAL;
@@ -338,7 +338,7 @@ static int dns_trust_anchor_load_positive(DnsTrustAnchor *d, const char *path, u
                         return -EINVAL;
                 }
 
-                r = unbase64mem(p, strlen(p), &k, &l);
+                r = unbase64mem(p, &k, &l);
                 if (r < 0)
                         return log_warning_errno(r, "Failed to parse DNSKEY key data %s on line %s:%u", p, path, line);
 
index 0295662b5b5eae4f886adc940e4c7ce6acaa2f5a..5a14e64fe5c4a1f5a74b08fab71bf767e8741242 100644 (file)
@@ -731,7 +731,7 @@ Manager *manager_free(Manager *m) {
 
         ordered_set_free(m->dns_extra_stub_listeners);
 
-        bus_verify_polkit_async_registry_free(m->polkit_registry);
+        hashmap_free(m->polkit_registry);
 
         sd_bus_flush_close_unref(m->bus);
 
@@ -894,7 +894,7 @@ int manager_recv(Manager *m, int fd, DnsProtocol protocol, DnsPacket **ret) {
         return 1;
 }
 
-static int sendmsg_loop(int fd, struct msghdr *mh, int flags) {
+int sendmsg_loop(int fd, struct msghdr *mh, int flags) {
         usec_t end;
         int r;
 
@@ -1098,17 +1098,7 @@ static int dns_question_to_json(DnsQuestion *q, JsonVariant **ret) {
         return 0;
 }
 
-int manager_monitor_send(
-                Manager *m,
-                int state,
-                int rcode,
-                int error,
-                DnsQuestion *question_idna,
-                DnsQuestion *question_utf8,
-                DnsPacket *question_bypass,
-                DnsQuestion *collected_questions,
-                DnsAnswer *answer) {
-
+int manager_monitor_send(Manager *m, DnsQuery *q) {
         _cleanup_(json_variant_unrefp) JsonVariant *jquestion = NULL, *jcollected_questions = NULL, *janswer = NULL;
         _cleanup_(dns_question_unrefp) DnsQuestion *merged = NULL;
         Varlink *connection;
@@ -1121,14 +1111,14 @@ int manager_monitor_send(
                 return 0;
 
         /* Merge all questions into one */
-        r = dns_question_merge(question_idna, question_utf8, &merged);
+        r = dns_question_merge(q->question_idna, q->question_utf8, &merged);
         if (r < 0)
                 return log_error_errno(r, "Failed to merge UTF8/IDNA questions: %m");
 
-        if (question_bypass) {
+        if (q->question_bypass) {
                 _cleanup_(dns_question_unrefp) DnsQuestion *merged2 = NULL;
 
-                r = dns_question_merge(merged, question_bypass->question, &merged2);
+                r = dns_question_merge(merged, q->question_bypass->question, &merged2);
                 if (r < 0)
                         return log_error_errno(r, "Failed to merge UTF8/IDNA questions and DNS packet question: %m");
 
@@ -1142,11 +1132,11 @@ int manager_monitor_send(
                 return log_error_errno(r, "Failed to convert question to JSON: %m");
 
         /* Generate a JSON array of the questions preceding the current one in the CNAME chain */
-        r = dns_question_to_json(collected_questions, &jcollected_questions);
+        r = dns_question_to_json(q->collected_questions, &jcollected_questions);
         if (r < 0)
                 return log_error_errno(r, "Failed to convert question to JSON: %m");
 
-        DNS_ANSWER_FOREACH_ITEM(rri, answer) {
+        DNS_ANSWER_FOREACH_ITEM(rri, q->answer) {
                 _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
 
                 r = dns_resource_record_to_json(rri->rr, &v);
@@ -1169,12 +1159,28 @@ int manager_monitor_send(
 
         SET_FOREACH(connection, m->varlink_subscription) {
                 r = varlink_notifyb(connection,
-                                    JSON_BUILD_OBJECT(JSON_BUILD_PAIR("state", JSON_BUILD_STRING(dns_transaction_state_to_string(state))),
-                                                      JSON_BUILD_PAIR_CONDITION(state == DNS_TRANSACTION_RCODE_FAILURE, "rcode", JSON_BUILD_INTEGER(rcode)),
-                                                      JSON_BUILD_PAIR_CONDITION(state == DNS_TRANSACTION_ERRNO, "errno", JSON_BUILD_INTEGER(error)),
+                                    JSON_BUILD_OBJECT(JSON_BUILD_PAIR("state", JSON_BUILD_STRING(dns_transaction_state_to_string(q->state))),
+                                                      JSON_BUILD_PAIR_CONDITION(q->state == DNS_TRANSACTION_DNSSEC_FAILED,
+                                                                                "result", JSON_BUILD_STRING(dnssec_result_to_string(q->answer_dnssec_result))),
+                                                      JSON_BUILD_PAIR_CONDITION(q->state == DNS_TRANSACTION_RCODE_FAILURE,
+                                                                                "rcode", JSON_BUILD_INTEGER(q->answer_rcode)),
+                                                      JSON_BUILD_PAIR_CONDITION(q->state == DNS_TRANSACTION_ERRNO,
+                                                                                "errno", JSON_BUILD_INTEGER(q->answer_errno)),
+                                                      JSON_BUILD_PAIR_CONDITION(IN_SET(q->state,
+                                                                                       DNS_TRANSACTION_DNSSEC_FAILED,
+                                                                                       DNS_TRANSACTION_RCODE_FAILURE) &&
+                                                                                q->answer_ede_rcode >= 0,
+                                                                                "extendedDNSErrorCode", JSON_BUILD_INTEGER(q->answer_ede_rcode)),
+                                                      JSON_BUILD_PAIR_CONDITION(IN_SET(q->state,
+                                                                                       DNS_TRANSACTION_DNSSEC_FAILED,
+                                                                                       DNS_TRANSACTION_RCODE_FAILURE) &&
+                                                                                q->answer_ede_rcode >= 0 && !isempty(q->answer_ede_msg),
+                                                                                "extendedDNSErrorMessage", JSON_BUILD_STRING(q->answer_ede_msg)),
                                                       JSON_BUILD_PAIR("question", JSON_BUILD_VARIANT(jquestion)),
-                                                      JSON_BUILD_PAIR_CONDITION(jcollected_questions, "collectedQuestions", JSON_BUILD_VARIANT(jcollected_questions)),
-                                                      JSON_BUILD_PAIR_CONDITION(janswer, "answer", JSON_BUILD_VARIANT(janswer))));
+                                                      JSON_BUILD_PAIR_CONDITION(jcollected_questions,
+                                                                                "collectedQuestions", JSON_BUILD_VARIANT(jcollected_questions)),
+                                                      JSON_BUILD_PAIR_CONDITION(janswer,
+                                                                                "answer", JSON_BUILD_VARIANT(janswer))));
                 if (r < 0)
                         log_debug_errno(r, "Failed to send monitor event, ignoring: %m");
         }
index 5cd5e834d39f081829d016ffe858956c523f817a..bd0e0532e7d0154324bf43a4b11a1e9e19834a0e 100644 (file)
@@ -176,8 +176,9 @@ int manager_start(Manager *m);
 
 uint32_t manager_find_mtu(Manager *m);
 
-int manager_monitor_send(Manager *m, int state, int rcode, int error, DnsQuestion *question_idna, DnsQuestion *question_utf8, DnsPacket *question_bypass, DnsQuestion *collected_questions, DnsAnswer *answer);
+int manager_monitor_send(Manager *m, DnsQuery *q);
 
+int sendmsg_loop(int fd, struct msghdr *mh, int flags);
 int manager_write(Manager *m, int fd, DnsPacket *p);
 int manager_send(Manager *m, int fd, int ifindex, int family, const union in_addr_union *destination, uint16_t port, const union in_addr_union *source, DnsPacket *p);
 int manager_recv(Manager *m, int fd, DnsProtocol protocol, DnsPacket **ret);
index 3e178a69f35ac5cc4357849501749184277741f5..4e7e91bdfb75b0a0e540f317f7cc309906188ce9 100644 (file)
@@ -49,7 +49,11 @@ static int reply_query_state(DnsQuery *q) {
 
         case DNS_TRANSACTION_DNSSEC_FAILED:
                 return varlink_errorb(q->varlink_request, "io.systemd.Resolve.DNSSECValidationFailed",
-                                      JSON_BUILD_OBJECT(JSON_BUILD_PAIR("result", JSON_BUILD_STRING(dnssec_result_to_string(q->answer_dnssec_result)))));
+                                      JSON_BUILD_OBJECT(JSON_BUILD_PAIR("result", JSON_BUILD_STRING(dnssec_result_to_string(q->answer_dnssec_result))),
+                                                        JSON_BUILD_PAIR_CONDITION(q->answer_ede_rcode >= 0,
+                                                                                  "extendedDNSErrorCode", JSON_BUILD_INTEGER(q->answer_ede_rcode)),
+                                                        JSON_BUILD_PAIR_CONDITION(q->answer_ede_rcode >= 0 && !isempty(q->answer_ede_msg),
+                                                                                  "extendedDNSErrorMessage", JSON_BUILD_STRING(q->answer_ede_msg))));
 
         case DNS_TRANSACTION_NO_TRUST_ANCHOR:
                 return varlink_error(q->varlink_request, "io.systemd.Resolve.NoTrustAnchor", NULL);
@@ -74,7 +78,11 @@ static int reply_query_state(DnsQuery *q) {
 
         case DNS_TRANSACTION_RCODE_FAILURE:
                 return varlink_errorb(q->varlink_request, "io.systemd.Resolve.DNSError",
-                                      JSON_BUILD_OBJECT(JSON_BUILD_PAIR("rcode", JSON_BUILD_INTEGER(q->answer_rcode))));
+                                      JSON_BUILD_OBJECT(JSON_BUILD_PAIR("rcode", JSON_BUILD_INTEGER(q->answer_rcode)),
+                                                        JSON_BUILD_PAIR_CONDITION(q->answer_ede_rcode >= 0,
+                                                                                  "extendedDNSErrorCode", JSON_BUILD_INTEGER(q->answer_ede_rcode)),
+                                                        JSON_BUILD_PAIR_CONDITION(q->answer_ede_rcode >= 0 && !isempty(q->answer_ede_msg),
+                                                                                  "extendedDNSErrorMessage", JSON_BUILD_STRING(q->answer_ede_msg))));
 
         case DNS_TRANSACTION_NULL:
         case DNS_TRANSACTION_PENDING:
diff --git a/src/resolve/test-resolved-dummy-server.c b/src/resolve/test-resolved-dummy-server.c
new file mode 100644 (file)
index 0000000..58257d7
--- /dev/null
@@ -0,0 +1,450 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "sd-daemon.h"
+
+#include "fd-util.h"
+#include "iovec-util.h"
+#include "log.h"
+#include "main-func.h"
+#include "resolved-dns-packet.h"
+#include "resolved-manager.h"
+#include "socket-netlink.h"
+#include "socket-util.h"
+
+/* Taken from resolved-dns-stub.c */
+#define ADVERTISE_DATAGRAM_SIZE_MAX (65536U-14U-20U-8U)
+
+/* This is more or less verbatim manager_recv() from resolved-manager.c, sans the manager stuff */
+static int server_recv(int fd, DnsPacket **ret) {
+        _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
+        CMSG_BUFFER_TYPE(CMSG_SPACE(MAXSIZE(struct in_pktinfo, struct in6_pktinfo))
+                         + CMSG_SPACE(int) /* ttl/hoplimit */
+                         + EXTRA_CMSG_SPACE /* kernel appears to require extra buffer space */) control;
+        union sockaddr_union sa;
+        struct iovec iov;
+        struct msghdr mh = {
+                .msg_name = &sa.sa,
+                .msg_namelen = sizeof(sa),
+                .msg_iov = &iov,
+                .msg_iovlen = 1,
+                .msg_control = &control,
+                .msg_controllen = sizeof(control),
+        };
+        struct cmsghdr *cmsg;
+        ssize_t ms, l;
+        int r;
+
+        assert(fd >= 0);
+        assert(ret);
+
+        ms = next_datagram_size_fd(fd);
+        if (ms < 0)
+                return ms;
+
+        r = dns_packet_new(&p, DNS_PROTOCOL_DNS, ms, DNS_PACKET_SIZE_MAX);
+        if (r < 0)
+                return r;
+
+        iov = IOVEC_MAKE(DNS_PACKET_DATA(p), p->allocated);
+
+        l = recvmsg_safe(fd, &mh, 0);
+        if (ERRNO_IS_NEG_TRANSIENT(l))
+                return 0;
+        if (l <= 0)
+                return l;
+
+        assert(!(mh.msg_flags & MSG_TRUNC));
+
+        p->size = (size_t) l;
+
+        p->family = sa.sa.sa_family;
+        p->ipproto = IPPROTO_UDP;
+        if (p->family == AF_INET) {
+                p->sender.in = sa.in.sin_addr;
+                p->sender_port = be16toh(sa.in.sin_port);
+        } else if (p->family == AF_INET6) {
+                p->sender.in6 = sa.in6.sin6_addr;
+                p->sender_port = be16toh(sa.in6.sin6_port);
+                p->ifindex = sa.in6.sin6_scope_id;
+        } else
+                return -EAFNOSUPPORT;
+
+        p->timestamp = now(CLOCK_BOOTTIME);
+
+        CMSG_FOREACH(cmsg, &mh) {
+
+                if (cmsg->cmsg_level == IPPROTO_IPV6) {
+                        assert(p->family == AF_INET6);
+
+                        switch (cmsg->cmsg_type) {
+
+                        case IPV6_PKTINFO: {
+                                struct in6_pktinfo *i = CMSG_TYPED_DATA(cmsg, struct in6_pktinfo);
+
+                                if (p->ifindex <= 0)
+                                        p->ifindex = i->ipi6_ifindex;
+
+                                p->destination.in6 = i->ipi6_addr;
+                                break;
+                        }
+
+                        case IPV6_HOPLIMIT:
+                                p->ttl = *CMSG_TYPED_DATA(cmsg, int);
+                                break;
+
+                        case IPV6_RECVFRAGSIZE:
+                                p->fragsize = *CMSG_TYPED_DATA(cmsg, int);
+                                break;
+                        }
+                } else if (cmsg->cmsg_level == IPPROTO_IP) {
+                        assert(p->family == AF_INET);
+
+                        switch (cmsg->cmsg_type) {
+
+                        case IP_PKTINFO: {
+                                struct in_pktinfo *i = CMSG_TYPED_DATA(cmsg, struct in_pktinfo);
+
+                                if (p->ifindex <= 0)
+                                        p->ifindex = i->ipi_ifindex;
+
+                                p->destination.in = i->ipi_addr;
+                                break;
+                        }
+
+                        case IP_TTL:
+                                p->ttl = *CMSG_TYPED_DATA(cmsg, int);
+                                break;
+
+                        case IP_RECVFRAGSIZE:
+                                p->fragsize = *CMSG_TYPED_DATA(cmsg, int);
+                                break;
+                        }
+                }
+        }
+
+        /* The Linux kernel sets the interface index to the loopback
+         * device if the packet came from the local host since it
+         * avoids the routing table in such a case. Let's unset the
+         * interface index in such a case. */
+        if (p->ifindex == LOOPBACK_IFINDEX)
+                p->ifindex = 0;
+
+        log_debug("Received DNS UDP packet of size %zu, ifindex=%i, ttl=%u, fragsize=%zu, sender=%s, destination=%s",
+                  p->size, p->ifindex, p->ttl, p->fragsize,
+                  IN_ADDR_TO_STRING(p->family, &p->sender),
+                  IN_ADDR_TO_STRING(p->family, &p->destination));
+
+        *ret = TAKE_PTR(p);
+        return 1;
+}
+
+/* Same as above, see manager_ipv4_send() in resolved-manager.c */
+static int server_ipv4_send(
+                int fd,
+                const struct in_addr *destination,
+                uint16_t port,
+                const struct in_addr *source,
+                DnsPacket *packet) {
+
+        union sockaddr_union sa;
+        struct iovec iov;
+        struct msghdr mh = {
+                .msg_iov = &iov,
+                .msg_iovlen = 1,
+                .msg_name = &sa.sa,
+                .msg_namelen = sizeof(sa.in),
+        };
+
+        assert(fd >= 0);
+        assert(destination);
+        assert(port > 0);
+        assert(packet);
+
+        iov = IOVEC_MAKE(DNS_PACKET_DATA(packet), packet->size);
+
+        sa = (union sockaddr_union) {
+                .in.sin_family = AF_INET,
+                .in.sin_addr = *destination,
+                .in.sin_port = htobe16(port),
+        };
+
+        return sendmsg_loop(fd, &mh, 0);
+}
+
+static int make_reply_packet(DnsPacket *packet, DnsPacket **ret) {
+        _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
+        int r;
+
+        assert(packet);
+        assert(ret);
+
+        r = dns_packet_new(&p, DNS_PROTOCOL_DNS, 0, DNS_PACKET_PAYLOAD_SIZE_MAX(packet));
+        if (r < 0)
+                return r;
+
+        r = dns_packet_append_question(p, packet->question);
+        if (r < 0)
+                return r;
+
+        DNS_PACKET_HEADER(p)->id = DNS_PACKET_ID(packet);
+        DNS_PACKET_HEADER(p)->qdcount = htobe16(dns_question_size(packet->question));
+
+        *ret = TAKE_PTR(p);
+        return 0;
+}
+
+static int reply_append_edns(DnsPacket *packet, DnsPacket *reply, const char *extra_text, size_t rcode, uint16_t ede_code) {
+        size_t saved_size;
+        int r;
+
+        assert(packet);
+        assert(reply);
+
+        /* Append EDNS0 stuff (inspired by dns_packet_append_opt() from resolved-dns-packet.c).
+         *
+         * Relevant headers from RFC 6891:
+         *
+         * +------------+--------------+------------------------------+
+         * | Field Name | Field Type   | Description                  |
+         * +------------+--------------+------------------------------+
+         * | NAME       | domain name  | MUST be 0 (root domain)      |
+         * | TYPE       | u_int16_t    | OPT (41)                     |
+         * | CLASS      | u_int16_t    | requestor's UDP payload size |
+         * | TTL        | u_int32_t    | extended RCODE and flags     |
+         * | RDLEN      | u_int16_t    | length of all RDATA          |
+         * | RDATA      | octet stream | {attribute,value} pairs      |
+         * +------------+--------------+------------------------------+
+         *
+         *               +0 (MSB)                            +1 (LSB)
+         *    +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
+         * 0: |                          OPTION-CODE                          |
+         *    +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
+         * 2: |                         OPTION-LENGTH                         |
+         *    +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
+         * 4: |                                                               |
+         *    /                          OPTION-DATA                          /
+         *    /                                                               /
+         *    +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
+         *
+         * And from RFC 8914:
+         *
+         *                                              1   1   1   1   1   1
+         *      0   1   2   3   4   5   6   7   8   9   0   1   2   3   4   5
+         *    +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
+         * 0: |                            OPTION-CODE                        |
+         *    +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
+         * 2: |                           OPTION-LENGTH                       |
+         *    +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
+         * 4: | INFO-CODE                                                     |
+         *    +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
+         * 6: / EXTRA-TEXT ...                                                /
+         *    +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
+         */
+
+        saved_size = reply->size;
+
+        /* empty name */
+        r = dns_packet_append_uint8(reply, 0, NULL);
+        if (r < 0)
+                return r;
+
+        /* type */
+        r = dns_packet_append_uint16(reply, DNS_TYPE_OPT, NULL);
+        if (r < 0)
+                return r;
+
+        /* class: maximum udp packet that can be received */
+        r = dns_packet_append_uint16(reply, ADVERTISE_DATAGRAM_SIZE_MAX, NULL);
+        if (r < 0)
+                return r;
+
+        /* extended RCODE and VERSION */
+        r = dns_packet_append_uint16(reply, ((uint16_t) rcode & 0x0FF0) << 4, NULL);
+        if (r < 0)
+                return r;
+
+        /* flags: DNSSEC OK (DO), see RFC3225 */
+        r = dns_packet_append_uint16(reply, 0, NULL);
+        if (r < 0)
+                return r;
+
+        /* RDATA */
+
+        size_t extra_text_len = isempty(extra_text) ? 0 : strlen(extra_text);
+        /* RDLENGTH (OPTION CODE + OPTION LENGTH + INFO-CODE + EXTRA-TEXT) */
+        r = dns_packet_append_uint16(reply, 2 + 2 + 2 + extra_text_len, NULL);
+        if (r < 0)
+                return 0;
+
+        /* OPTION-CODE: 15 for EDE */
+        r = dns_packet_append_uint16(reply, 15, NULL);
+        if (r < 0)
+                return r;
+
+        /* OPTION-LENGTH: INFO-CODE + EXTRA-TEXT */
+        r = dns_packet_append_uint16(reply, 2 + extra_text_len, NULL);
+        if (r < 0)
+                return r;
+
+        /* INFO-CODE: EDE code */
+        r = dns_packet_append_uint16(reply, ede_code, NULL);
+        if (r < 0)
+                return r;
+
+        /* EXTRA-TEXT */
+        if (extra_text_len > 0) {
+                /* From RFC 8914:
+                 *  EDE text may be null terminated but MUST NOT be assumed to be; the length MUST be derived
+                 *  from the OPTION-LENGTH field
+                 *
+                 *  Let's exercise our code on the receiving side and not NUL-terminate the EXTRA-TEXT field
+                 */
+                r = dns_packet_append_blob(reply, extra_text, extra_text_len, NULL);
+                if (r < 0)
+                        return r;
+        }
+
+        DNS_PACKET_HEADER(reply)->arcount = htobe16(DNS_PACKET_ARCOUNT(reply) + 1);
+        reply->opt_start = saved_size;
+        reply->opt_size = reply->size - saved_size;
+
+        /* Order: qr, opcode, aa, tc, rd, ra, ad, cd, rcode */
+        DNS_PACKET_HEADER(reply)->flags = htobe16(DNS_PACKET_MAKE_FLAGS(
+                                                1, 0, 0, 0, DNS_PACKET_RD(packet), 1, 0, 1, rcode));
+        return 0;
+}
+
+static void server_fail(DnsPacket *packet, DnsPacket *reply, int rcode) {
+        assert(reply);
+
+        /* Order: qr, opcode, aa, tc, rd, ra, ad, cd, rcode */
+        DNS_PACKET_HEADER(reply)->flags = htobe16(DNS_PACKET_MAKE_FLAGS(
+                                                1, 0, 0, 0, DNS_PACKET_RD(packet), 1, 0, 1, rcode));
+}
+
+static int server_handle_edns_bogus_dnssec(DnsPacket *packet, DnsPacket *reply) {
+        assert(packet);
+        assert(reply);
+
+        return reply_append_edns(packet, reply, NULL, DNS_RCODE_SERVFAIL, DNS_EDE_RCODE_DNSSEC_BOGUS);
+}
+
+static int server_handle_edns_extra_text(DnsPacket *packet, DnsPacket *reply) {
+        assert(packet);
+        assert(reply);
+
+        return reply_append_edns(packet, reply, "Nothing to see here!", DNS_RCODE_SERVFAIL, DNS_EDE_RCODE_CENSORED);
+}
+
+static int server_handle_edns_invalid_code(DnsPacket *packet, DnsPacket *reply, const char *extra_text) {
+        assert(packet);
+        assert(reply);
+        assert_cc(_DNS_EDE_RCODE_MAX_DEFINED < UINT16_MAX);
+
+        return reply_append_edns(packet, reply, extra_text, DNS_RCODE_SERVFAIL, _DNS_EDE_RCODE_MAX_DEFINED + 1);
+}
+
+static int server_handle_edns_code_zero(DnsPacket *packet, DnsPacket *reply) {
+        assert(packet);
+        assert(reply);
+        assert_cc(DNS_EDE_RCODE_OTHER == 0);
+
+        return reply_append_edns(packet, reply, "\xF0\x9F\x90\xB1", DNS_RCODE_SERVFAIL, DNS_EDE_RCODE_OTHER);
+}
+
+static int on_dns_packet(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
+        _cleanup_(dns_packet_unrefp) DnsPacket *packet = NULL;
+        _cleanup_(dns_packet_unrefp) DnsPacket *reply = NULL;
+        const char *name;
+        int r;
+
+        assert(fd >= 0);
+
+        r = server_recv(fd, &packet);
+        if (r < 0) {
+                log_debug_errno(r, "Failed to receive packet, ignoring: %m");
+                return 0;
+        }
+
+        r = dns_packet_validate_query(packet);
+        if (r < 0) {
+                log_debug_errno(r, "Invalid DNS UDP packet, ignoring.");
+                return 0;
+        }
+
+        r = dns_packet_extract(packet);
+        if (r < 0) {
+                log_debug_errno(r, "Failed to extract DNS packet, ignoring: %m");
+                return 0;
+        }
+
+        name = dns_question_first_name(packet->question);
+        log_info("Processing question for name '%s'", name);
+
+        (void) dns_question_dump(packet->question, stdout);
+
+        r = make_reply_packet(packet, &reply);
+        if (r < 0) {
+                log_debug_errno(r, "Failed to make reply packet, ignoring: %m");
+                return 0;
+        }
+
+        if (streq_ptr(name, "edns-bogus-dnssec.forwarded.test"))
+                r = server_handle_edns_bogus_dnssec(packet, reply);
+        else if (streq_ptr(name, "edns-extra-text.forwarded.test"))
+                r = server_handle_edns_extra_text(packet, reply);
+        else if (streq_ptr(name, "edns-invalid-code.forwarded.test"))
+                r = server_handle_edns_invalid_code(packet, reply, NULL);
+        else if (streq_ptr(name, "edns-invalid-code-with-extra-text.forwarded.test"))
+                r = server_handle_edns_invalid_code(packet, reply, "Hello [#]$%~ World");
+        else if (streq_ptr(name, "edns-code-zero.forwarded.test"))
+                r = server_handle_edns_code_zero(packet, reply);
+        else
+                r = log_debug_errno(SYNTHETIC_ERRNO(EFAULT), "Unhandled name '%s', ignoring.", name);
+        if (r < 0)
+                server_fail(packet, reply, DNS_RCODE_NXDOMAIN);
+
+        r = server_ipv4_send(fd, &packet->sender.in, packet->sender_port, &packet->destination.in, reply);
+        if (r < 0)
+                log_debug_errno(r, "Failed to send reply, ignoring: %m");
+
+        return 0;
+}
+
+static int run(int argc, char *argv[]) {
+        _cleanup_(sd_event_unrefp) sd_event *event = NULL;
+        _cleanup_close_ int fd = -EBADF;
+        int r;
+
+        log_setup();
+
+        if (argc != 2)
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+                                       "This program takes one argument in format ip_address:port");
+
+        fd = make_socket_fd(LOG_DEBUG, argv[1], SOCK_DGRAM, SOCK_CLOEXEC);
+        if (fd < 0)
+                return log_error_errno(fd, "Failed to listen on address '%s': %m", argv[1]);
+
+        r = sd_event_default(&event);
+        if (r < 0)
+                return log_error_errno(r, "Failed to allocate event: %m");
+
+        r = sd_event_add_io(event, NULL, fd, EPOLLIN, on_dns_packet, NULL);
+        if (r < 0)
+                return log_error_errno(r, "Failed to add IO event source: %m");
+
+        r = sd_event_set_signal_exit(event, true);
+        if (r < 0)
+                return log_error_errno(r, "Failed to install SIGINT/SIGTERM handlers: %m");
+
+        (void) sd_notify(/* unset_environment=false */ false, "READY=1");
+
+        r = sd_event_loop(event);
+        if (r < 0)
+                return log_error_errno(r, "Failed to run event loop: %m");
+
+        return 0;
+}
+
+DEFINE_MAIN_FUNCTION(run);
index 45da75f8897bb55bf540156ce7132a74ec621f6e..c78d3c6a1df7f921779c8a00fd4f3f6bee23a6d9 100644 (file)
@@ -32,6 +32,7 @@
 #include "ptyfwd.h"
 #include "signal-util.h"
 #include "spawn-polkit-agent.h"
+#include "special.h"
 #include "strv.h"
 #include "terminal-util.h"
 #include "unit-def.h"
@@ -903,7 +904,7 @@ static int parse_argv_sudo_mode(int argc, char *argv[]) {
         strv_free_and_replace(arg_cmdline, l);
 
         if (!arg_slice) {
-                arg_slice = strdup("user.slice");
+                arg_slice = strdup(SPECIAL_USER_SLICE);
                 if (!arg_slice)
                         return log_oom();
         }
index 9f923372a4b3e3ecdddf523a1c8c57cb5ae89821..ff905d147f82c6f4a859bba01d5407fe963dd07b 100644 (file)
@@ -4,10 +4,11 @@
 #include "bus-message.h"
 #include "bus-polkit.h"
 #include "bus-util.h"
+#include "process-util.h"
 #include "strv.h"
 #include "user-util.h"
 
-static int check_good_user(sd_bus_message *m, uid_t good_user) {
+static int bus_message_check_good_user(sd_bus_message *m, uid_t good_user) {
         _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
         uid_t sender_uid;
         int r;
@@ -15,7 +16,7 @@ static int check_good_user(sd_bus_message *m, uid_t good_user) {
         assert(m);
 
         if (good_user == UID_INVALID)
-                return 0;
+                return false;
 
         r = sd_bus_query_sender_creds(m, SD_BUS_CREDS_EUID, &creds);
         if (r < 0)
@@ -54,7 +55,7 @@ static int bus_message_append_strv_key_value(sd_bus_message *m, const char **l)
         return r;
 }
 
-static int bus_message_new_polkit_auth_call(
+static int bus_message_new_polkit_auth_call_for_bus(
                 sd_bus_message *m,
                 const char *action,
                 const char **details,
@@ -115,7 +116,7 @@ int bus_test_polkit(
 
         /* Tests non-interactively! */
 
-        r = check_good_user(call, good_user);
+        r = bus_message_check_good_user(call, good_user);
         if (r != 0)
                 return r;
 
@@ -129,7 +130,7 @@ int bus_test_polkit(
         _cleanup_(sd_bus_message_unrefp) sd_bus_message *request = NULL, *reply = NULL;
         int authorized = false, challenge = false;
 
-        r = bus_message_new_polkit_auth_call(call, action, details, /* interactive = */ false, &request);
+        r = bus_message_new_polkit_auth_call_for_bus(call, action, details, /* interactive = */ false, &request);
         if (r < 0)
                 return r;
 
@@ -190,8 +191,10 @@ typedef struct AsyncPolkitQuery {
 
         AsyncPolkitQueryAction *action;
 
-        sd_bus_message *request;
+        sd_bus *bus;
+        sd_bus_message *request;  /* the original bus method call that triggered the polkit auth, NULL in case of varlink */
         sd_bus_slot *slot;
+        Varlink *link;            /* the original varlink method call that triggered the polkit auth, NULL in case of bus */
 
         Hashmap *registry;
         sd_event_source *defer_event_source;
@@ -208,11 +211,18 @@ static AsyncPolkitQuery *async_polkit_query_free(AsyncPolkitQuery *q) {
 
         sd_bus_slot_unref(q->slot);
 
-        if (q->registry && q->request)
-                hashmap_remove(q->registry, q->request);
+        if (q->registry) {
+                if (q->request)
+                        hashmap_remove(q->registry, q->request);
+                if (q->link)
+                        hashmap_remove(q->registry, q->link);
+        }
 
         sd_bus_message_unref(q->request);
 
+        sd_bus_unref(q->bus);
+        varlink_unref(q->link);
+
         async_polkit_query_action_free(q->action);
 
         sd_event_source_disable_unref(q->defer_event_source);
@@ -230,6 +240,14 @@ static AsyncPolkitQuery *async_polkit_query_free(AsyncPolkitQuery *q) {
 DEFINE_PRIVATE_TRIVIAL_REF_UNREF_FUNC(AsyncPolkitQuery, async_polkit_query, async_polkit_query_free);
 DEFINE_TRIVIAL_CLEANUP_FUNC(AsyncPolkitQuery*, async_polkit_query_unref);
 
+DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR(
+                async_polkit_query_hash_ops,
+                void,
+                trivial_hash_func,
+                trivial_compare_func,
+                AsyncPolkitQuery,
+                async_polkit_query_unref);
+
 static int async_polkit_defer(sd_event_source *s, void *userdata) {
         AsyncPolkitQuery *q = ASSERT_PTR(userdata);
 
@@ -316,7 +334,7 @@ static int async_polkit_process_reply(sd_bus_message *reply, AsyncPolkitQuery *q
 
         if (!q->defer_event_source) {
                 r = sd_event_add_defer(
-                                sd_bus_get_event(sd_bus_message_get_bus(reply)),
+                                sd_bus_get_event(q->bus),
                                 &q->defer_event_source,
                                 async_polkit_defer,
                                 q);
@@ -332,13 +350,21 @@ static int async_polkit_process_reply(sd_bus_message *reply, AsyncPolkitQuery *q
         if (r < 0)
                 return r;
 
-        r = sd_bus_message_rewind(q->request, true);
-        if (r < 0)
-                return r;
+        if (q->request) {
+                r = sd_bus_message_rewind(q->request, true);
+                if (r < 0)
+                        return r;
 
-        r = sd_bus_enqueue_for_read(sd_bus_message_get_bus(q->request), q->request);
-        if (r < 0)
-                return r;
+                r = sd_bus_enqueue_for_read(q->bus, q->request);
+                if (r < 0)
+                        return r;
+        }
+
+        if (q->link) {
+                r = varlink_dispatch_again(q->link);
+                if (r < 0)
+                        return r;
+        }
 
         return 1;
 }
@@ -352,7 +378,10 @@ static int async_polkit_callback(sd_bus_message *reply, void *userdata, sd_bus_e
         r = async_polkit_process_reply(reply, q);
         if (r < 0) {
                 log_debug_errno(r, "Processing asynchronous PolicyKit reply failed, ignoring: %m");
-                (void) sd_bus_reply_method_errno(q->request, r, NULL);
+                if (q->request)
+                        (void) sd_bus_reply_method_errno(q->request, r, NULL);
+                if (q->link)
+                        varlink_error_errno(q->link, r);
                 async_polkit_query_unref(q);
         }
         return r;
@@ -366,11 +395,10 @@ static int async_polkit_query_check_action(
 
         assert(q);
         assert(action);
-        assert(ret_error);
 
         LIST_FOREACH(authorized, a, q->authorized_actions)
                 if (streq(a->action, action) && strv_equal(a->details, (char**) details))
-                        return 1;
+                        return 1; /* Allow! */
 
         if (q->error_action && streq(q->error_action->action, action))
                 return sd_bus_error_copy(ret_error, &q->error);
@@ -480,7 +508,7 @@ int bus_verify_polkit_async_full(
         assert(registry);
         assert(ret_error);
 
-        r = check_good_user(call, good_user);
+        r = bus_message_check_good_user(call, good_user);
         if (r != 0)
                 return r;
 
@@ -512,11 +540,7 @@ int bus_verify_polkit_async_full(
         if (c > 0)
                 interactive = true;
 
-        r = hashmap_ensure_allocated(registry, NULL);
-        if (r < 0)
-                return r;
-
-        r = bus_message_new_polkit_auth_call(call, action, details, interactive, &pk);
+        r = bus_message_new_polkit_auth_call_for_bus(call, action, details, interactive, &pk);
         if (r < 0)
                 return r;
 
@@ -528,6 +552,7 @@ int bus_verify_polkit_async_full(
                 *q = (AsyncPolkitQuery) {
                         .n_ref = 1,
                         .request = sd_bus_message_ref(call),
+                        .bus = sd_bus_ref(sd_bus_message_get_bus(call)),
                 };
         }
 
@@ -544,7 +569,7 @@ int bus_verify_polkit_async_full(
                 return -ENOMEM;
 
         if (!q->registry) {
-                r = hashmap_put(*registry, call, q);
+                r = hashmap_ensure_put(registry, &async_polkit_query_hash_ops, call, q);
                 if (r < 0)
                         return r;
 
@@ -563,11 +588,231 @@ int bus_verify_polkit_async_full(
         return -EACCES;
 }
 
-Hashmap *bus_verify_polkit_async_registry_free(Hashmap *registry) {
+static int varlink_check_good_user(Varlink *link, uid_t good_user) {
+        int r;
+
+        assert(link);
+
+        if (good_user == UID_INVALID)
+                return false;
+
+        uid_t peer_uid;
+        r = varlink_get_peer_uid(link, &peer_uid);
+        if (r < 0)
+                return r;
+
+        return good_user == peer_uid;
+}
+
+static int varlink_check_peer_privilege(Varlink *link) {
+        int r;
+
+        assert(link);
+
+        uid_t peer_uid;
+        r = varlink_get_peer_uid(link, &peer_uid);
+        if (r < 0)
+                return r;
+
+        uid_t our_uid = getuid();
+        return peer_uid == our_uid ||
+                (our_uid != 0 && peer_uid == 0);
+}
+
 #if ENABLE_POLKIT
-        return hashmap_free_with_destructor(registry, async_polkit_query_unref);
-#else
-        assert(hashmap_isempty(registry));
-        return hashmap_free(registry);
+static int bus_message_new_polkit_auth_call_for_varlink(
+                sd_bus *bus,
+                Varlink *link,
+                const char *action,
+                const char **details,
+                bool interactive,
+                sd_bus_message **ret) {
+
+        _cleanup_(sd_bus_message_unrefp) sd_bus_message *c = NULL;
+        _cleanup_(pidref_done) PidRef pidref = PIDREF_NULL;
+        int r;
+
+        assert(bus);
+        assert(link);
+        assert(action);
+        assert(ret);
+
+        r = varlink_get_peer_pidref(link, &pidref);
+        if (r < 0)
+                return r;
+        if (r == 0) /* if we couldn't get a pidfd this returns == 0 */
+                return log_debug_errno(SYNTHETIC_ERRNO(EPERM), "Failed to get peer pidfd, cannot securely authenticate.");
+
+        uid_t uid;
+        r = varlink_get_peer_uid(link, &uid);
+        if (r < 0)
+                return r;
+
+        r = sd_bus_message_new_method_call(
+                        bus,
+                        &c,
+                        "org.freedesktop.PolicyKit1",
+                        "/org/freedesktop/PolicyKit1/Authority",
+                        "org.freedesktop.PolicyKit1.Authority",
+                        "CheckAuthorization");
+        if (r < 0)
+                return r;
+
+        r = sd_bus_message_append(
+                        c,
+                        "(sa{sv})s",
+                        "unix-process", 2,
+                        "pidfd", "h", (uint32_t) pidref.fd,
+                        "uid", "i", (int32_t) uid,
+                        action);
+        if (r < 0)
+                return r;
+
+        r = bus_message_append_strv_key_value(c, details);
+        if (r < 0)
+                return r;
+
+        r = sd_bus_message_append(c, "us", interactive, NULL);
+        if (r < 0)
+                return r;
+
+        *ret = TAKE_PTR(c);
+        return 0;
+}
+
+static bool varlink_allow_interactive_authentication(Varlink *link) {
+        _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
+        int r;
+
+        assert(link);
+
+        /* We look for the allowInteractiveAuthentication field in the message currently being dispatched,
+         * always under the same name. */
+
+        r = varlink_get_current_parameters(link, &v);
+        if (r < 0)
+                return r;
+
+        JsonVariant *b;
+        b = json_variant_by_key(v, "allowInteractiveAuthentication");
+        if (b) {
+                if (json_variant_is_boolean(b))
+                        return json_variant_boolean(b);
+
+                log_debug("Incoming 'allowInteractiveAuthentication' field is not a boolean, ignoring.");
+        }
+
+        return false;
+}
 #endif
+
+int varlink_verify_polkit_async(
+                Varlink *link,
+                sd_bus *bus,
+                const char *action,
+                const char **details,
+                uid_t good_user,
+                Hashmap **registry) {
+
+        int r;
+
+        assert(link);
+        assert(registry);
+
+        /* This is the same as bus_verify_polkit_async_full(), but authenticates the peer of a varlink
+         * connection rather than the sender of a bus message. */
+
+        r = varlink_check_good_user(link, good_user);
+        if (r != 0)
+                return r;
+
+        r = varlink_check_peer_privilege(link);
+        if (r != 0)
+                return r;
+
+#if ENABLE_POLKIT
+        _cleanup_(async_polkit_query_unrefp) AsyncPolkitQuery *q = NULL;
+
+        q = async_polkit_query_ref(hashmap_get(*registry, link));
+        /* This is a repeated invocation of this function, hence let's check if we've already got
+         * a response from polkit for this action */
+        if (q) {
+                _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+                r = async_polkit_query_check_action(q, action, details, &error);
+                if (r < 0) {
+                        /* Reply with a nice error */
+                        if (sd_bus_error_has_name(&error, SD_BUS_ERROR_INTERACTIVE_AUTHORIZATION_REQUIRED))
+                                return varlink_error(link, VARLINK_ERROR_INTERACTIVE_AUTHENTICATION_REQUIRED, NULL);
+
+                        if (ERRNO_IS_NEG_PRIVILEGE(r))
+                                return varlink_error(link, VARLINK_ERROR_PERMISSION_DENIED, NULL);
+
+                        return r;
+                }
+                if (r > 0)
+                        return r;
+        }
+
+        _cleanup_(sd_bus_unrefp) sd_bus *mybus = NULL;
+        if (!bus) {
+                r = sd_bus_open_system(&mybus);
+                if (r < 0)
+                        return r;
+
+                r = sd_bus_attach_event(mybus, varlink_get_event(link), 0);
+                if (r < 0)
+                        return r;
+
+                bus = mybus;
+        }
+
+        bool interactive = varlink_allow_interactive_authentication(link);
+
+        _cleanup_(sd_bus_message_unrefp) sd_bus_message *pk = NULL;
+        r = bus_message_new_polkit_auth_call_for_varlink(bus, link, action, details, interactive, &pk);
+        if (r < 0)
+                return r;
+
+        if (!q) {
+                q = new(AsyncPolkitQuery, 1);
+                if (!q)
+                        return -ENOMEM;
+
+                *q = (AsyncPolkitQuery) {
+                        .n_ref = 1,
+                        .link = varlink_ref(link),
+                        .bus = sd_bus_ref(bus),
+                };
+        }
+
+        assert(!q->action);
+        q->action = new(AsyncPolkitQueryAction, 1);
+        if (!q->action)
+                return -ENOMEM;
+
+        *q->action = (AsyncPolkitQueryAction) {
+                .action = strdup(action),
+                .details = strv_copy((char**) details),
+        };
+        if (!q->action->action || !q->action->details)
+                return -ENOMEM;
+
+        if (!q->registry) {
+                r = hashmap_ensure_put(registry, &async_polkit_query_hash_ops, link, q);
+                if (r < 0)
+                        return r;
+
+                q->registry = *registry;
+        }
+
+        r = sd_bus_call_async(bus, &q->slot, pk, async_polkit_callback, q, 0);
+        if (r < 0)
+                return r;
+
+        TAKE_PTR(q);
+
+        return 0;
+#endif
+
+        return -EACCES;
 }
index d82ac4679c617ee70cb024aadb9b0c1d77b74871..0fe3a4ca0ed91d0ab0bdc0c72c0a89d64ad6104e 100644 (file)
@@ -5,6 +5,7 @@
 
 #include "hashmap.h"
 #include "user-util.h"
+#include "varlink.h"
 
 int bus_test_polkit(sd_bus_message *call, const char *action, const char **details, uid_t good_user, bool *_challenge, sd_bus_error *e);
 
@@ -13,4 +14,12 @@ static inline int bus_verify_polkit_async(sd_bus_message *call, const char *acti
         return bus_verify_polkit_async_full(call, action, details, false, UID_INVALID, registry, ret_error);
 }
 
-Hashmap *bus_verify_polkit_async_registry_free(Hashmap *registry);
+int varlink_verify_polkit_async(Varlink *link, sd_bus *bus, const char *action, const char **details, uid_t good_user, Hashmap **registry);
+
+/* A JsonDispatch initializer that makes sure the allowInteractiveAuthentication boolean field we want for
+ * polkit support in Varlink calls is ignored while regular dispatching (and does not result in errors
+ * regarding unexpected fields) */
+#define VARLINK_DISPATCH_POLKIT_FIELD {                          \
+                .name = "allowInteractiveAuthentication",        \
+                .type = JSON_VARIANT_BOOLEAN,                    \
+        }
index 6704e1ef3dfa8ae5b070832fd3d8798c86beebb5..99b1cc7c709922503d4e59bc8753ae0183cad294 100644 (file)
@@ -164,9 +164,11 @@ static int bus_print_property(const char *name, const char *expected_value, sd_b
 
                         bus_print_property_value(name, expected_value, flags, "[not set]");
 
-                else if ((ENDSWITH_SET(name, "MemoryLow", "MemoryMin", "MemoryHigh", "MemoryMax", "MemorySwapMax", "MemoryZSwapMax", "MemoryLimit") &&
+                else if ((ENDSWITH_SET(name, "MemoryLow", "MemoryMin",
+                                             "MemoryHigh", "MemoryMax",
+                                             "MemorySwapMax", "MemoryZSwapMax", "MemoryLimit") &&
                           u == CGROUP_LIMIT_MAX) ||
-                         (STR_IN_SET(name, "TasksMax", "DefaultTasksMax") && u == UINT64_MAX) ||
+                         (endswith(name, "TasksMax") && u == UINT64_MAX) ||
                          (startswith(name, "Limit") && u == UINT64_MAX) ||
                          (startswith(name, "DefaultLimit") && u == UINT64_MAX))
 
index bba040112d2cba34c8e95a2ea2c444028b6248e7..dae7dd5e36f9420ed1a5c073bc09cb55dda9dc6e 100644 (file)
@@ -1213,7 +1213,7 @@ static int bus_append_execute_property(sd_bus_message *m, const char *field, con
                                 _cleanup_free_ void *decoded = NULL;
                                 size_t decoded_size;
 
-                                r = unbase64mem(p, SIZE_MAX, &decoded, &decoded_size);
+                                r = unbase64mem(p, &decoded, &decoded_size);
                                 if (r < 0)
                                         return log_error_errno(r, "Failed to base64 decode encrypted credential: %m");
 
@@ -1400,7 +1400,7 @@ static int bus_append_execute_property(sd_bus_message *m, const char *field, con
                 _cleanup_free_ void *decoded = NULL;
                 size_t sz;
 
-                r = unbase64mem(eq, SIZE_MAX, &decoded, &sz);
+                r = unbase64mem(eq, &decoded, &sz);
                 if (r < 0)
                         return log_error_errno(r, "Failed to decode base64 data '%s': %m", eq);
 
@@ -1787,7 +1787,7 @@ static int bus_append_execute_property(sd_bus_message *m, const char *field, con
                         return bus_append_string(m, "RootHashPath", eq);
 
                 /* We have a roothash to decode, eg: RootHash=012345789abcdef */
-                r = unhexmem(eq, strlen(eq), &roothash_decoded, &roothash_decoded_size);
+                r = unhexmem(eq, &roothash_decoded, &roothash_decoded_size);
                 if (r < 0)
                         return log_error_errno(r, "Failed to decode RootHash= '%s': %m", eq);
                 if (roothash_decoded_size < sizeof(sd_id128_t))
@@ -1809,7 +1809,7 @@ static int bus_append_execute_property(sd_bus_message *m, const char *field, con
                         return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to decode RootHashSignature= '%s', not a path but doesn't start with 'base64:': %m", eq);
 
                 /* We have a roothash signature to decode, eg: RootHashSignature=base64:012345789abcdef */
-                r = unbase64mem(value, strlen(value), &roothash_sig_decoded, &roothash_sig_decoded_size);
+                r = unbase64mem(value, &roothash_sig_decoded, &roothash_sig_decoded_size);
                 if (r < 0)
                         return log_error_errno(r, "Failed to decode RootHashSignature= '%s': %m", eq);
 
index 4123152d93a91944af83d6cb51f383cdbfe002ea..09a3734aebc71000d764d1dd325627703895b77a 100644 (file)
@@ -18,6 +18,7 @@
 #include "bus-internal.h"
 #include "bus-label.h"
 #include "bus-util.h"
+#include "daemon-util.h"
 #include "data-fd-util.h"
 #include "fd-util.h"
 #include "memstream-util.h"
@@ -128,8 +129,8 @@ int bus_event_loop_with_idle(
 
                 if (r == 0 && !exiting && idle) {
                         /* Inform the service manager that we are going down, so that it will queue all
-                         * further start requests, instead of assuming we are already running. */
-                        sd_notify(false, "STOPPING=1");
+                         * further start requests, instead of assuming we are still running. */
+                        (void) sd_notify(false, NOTIFY_STOPPING);
 
                         r = bus_async_unregister_and_exit(e, bus, name);
                         if (r < 0)
index d3446e8a9da00dbd21bfc2ec06abf4df5936ed3e..7563f29c94fbc737e7a254056c39bfbb570e86e8 100644 (file)
@@ -59,7 +59,7 @@
 #include "string-util.h"
 #include "tomoyo-util.h"
 #include "tpm2-util.h"
-#include "uid-alloc-range.h"
+#include "uid-classification.h"
 #include "user-util.h"
 #include "virt.h"
 
index 5d4500cb05bf320a572d0b9940a2c482afaef976..d8a2c47209d83eaac4333e26024e2d93b1362bff 100644 (file)
@@ -1088,6 +1088,7 @@ int config_parse_string(
                 void *userdata) {
 
         char **s = ASSERT_PTR(data);
+        int r;
 
         assert(filename);
         assert(lvalue);
@@ -1095,7 +1096,7 @@ int config_parse_string(
 
         if (isempty(rvalue)) {
                 *s = mfree(*s);
-                return 0;
+                return 1;
         }
 
         if (FLAGS_SET(ltype, CONFIG_PARSE_STRING_SAFE) && !string_is_safe(rvalue)) {
@@ -1116,7 +1117,11 @@ int config_parse_string(
                 return 0;
         }
 
-        return free_and_strdup_warn(s, empty_to_null(rvalue));
+        r = free_and_strdup_warn(s, empty_to_null(rvalue));
+        if (r < 0)
+                return r;
+
+        return 1;
 }
 
 int config_parse_dns_name(
@@ -1592,7 +1597,7 @@ int config_parse_mtu(
                 return 0;
         }
 
-        return 0;
+        return 1;
 }
 
 int config_parse_rlimit(
index 0026da5b48352fcabc7ebd73caeb0245989fad5a..434907c9986b9eb18ec8a48d966a73644f808e5b 100644 (file)
@@ -138,14 +138,13 @@ int read_credential(const char *name, void **ret, size_t *ret_size) {
 }
 
 int read_credential_with_decryption(const char *name, void **ret, size_t *ret_size) {
+        _cleanup_(iovec_done_erase) struct iovec ret_iovec = {};
         _cleanup_(erase_and_freep) void *data = NULL;
         _cleanup_free_ char *fn = NULL;
         size_t sz = 0;
         const char *d;
         int r;
 
-        assert(ret);
-
         /* Just like read_credential() but will also look for encrypted credentials. Note that services only
          * receive decrypted credentials, hence use read_credential() for those. This helper here is for
          * generators, i.e. code that runs outside of service context, and thus has no decrypted credentials
@@ -193,18 +192,22 @@ int read_credential_with_decryption(const char *name, void **ret, size_t *ret_si
                         now(CLOCK_REALTIME),
                         /* tpm2_device = */ NULL,
                         /* tpm2_signature_path = */ NULL,
-                        data,
-                        sz,
-                        ret,
-                        ret_size);
+                        &IOVEC_MAKE(data, sz),
+                        /* flags= */ 0,
+                        &ret_iovec);
         if (r < 0)
                 return r;
 
+        if (ret)
+                *ret = TAKE_PTR(ret_iovec.iov_base);
+        if (ret_size)
+                *ret_size = ret_iovec.iov_len;
+
         return 1; /* found */
 
 not_found:
-        *ret = NULL;
-
+        if (ret)
+                *ret = NULL;
         if (ret_size)
                 *ret_size = 0;
 
@@ -216,6 +219,7 @@ int read_credential_strings_many_internal(
                 ...) {
 
         _cleanup_free_ void *b = NULL;
+        bool all = true;
         int r, ret = 0;
 
         /* Reads a bunch of credentials into the specified buffers. If the specified buffers are already
@@ -231,10 +235,11 @@ int read_credential_strings_many_internal(
         r = read_credential(first_name, &b, NULL);
         if (r == -ENXIO) /* No creds passed at all? Bail immediately. */
                 return 0;
-        if (r < 0) {
-                if (r != -ENOENT)
-                        ret = r;
-        } else
+        if (r == -ENOENT)
+                all = false;
+        else if (r < 0)
+                RET_GATHER(ret, r);
+        else
                 free_and_replace(*first_value, b);
 
         va_list ap;
@@ -249,20 +254,19 @@ int read_credential_strings_many_internal(
                 if (!name)
                         break;
 
-                value = va_arg(ap, char **);
-                if (*value)
-                        continue;
+                value = ASSERT_PTR(va_arg(ap, char **));
 
                 r = read_credential(name, &bb, NULL);
-                if (r < 0) {
-                        if (ret >= 0 && r != -ENOENT)
-                                ret = r;
-                } else
+                if (r == -ENOENT)
+                        all = false;
+                else if (r < 0)
+                        RET_GATHER(ret, r);
+                else
                         free_and_replace(*value, bb);
         }
 
         va_end(ap);
-        return ret;
+        return ret < 0 ? ret : all;
 }
 
 int read_credential_bool(const char *name) {
@@ -352,8 +356,7 @@ static int make_credential_host_secret(
                 CredentialSecretFlags flags,
                 const char *dirname,
                 const char *fn,
-                void **ret_data,
-                size_t *ret_size) {
+                struct iovec *ret) {
 
         _cleanup_free_ char *t = NULL;
         _cleanup_close_ int fd = -EBADF;
@@ -420,7 +423,7 @@ static int make_credential_host_secret(
                 goto fail;
         }
 
-        if (ret_data) {
+        if (ret) {
                 void *copy;
 
                 copy = memdup(buf.data, sizeof(buf.data));
@@ -429,12 +432,9 @@ static int make_credential_host_secret(
                         goto fail;
                 }
 
-                *ret_data = copy;
+                *ret = IOVEC_MAKE(copy, sizeof(buf.data));
         }
 
-        if (ret_size)
-                *ret_size = sizeof(buf.data);
-
         return 0;
 
 fail:
@@ -444,7 +444,7 @@ fail:
         return r;
 }
 
-int get_credential_host_secret(CredentialSecretFlags flags, void **ret, size_t *ret_size) {
+int get_credential_host_secret(CredentialSecretFlags flags, struct iovec *ret) {
         _cleanup_free_ char *_dirname = NULL, *_filename = NULL;
         _cleanup_close_ int dfd = -EBADF;
         sd_id128_t machine_id;
@@ -512,7 +512,7 @@ int get_credential_host_secret(CredentialSecretFlags flags, void **ret, size_t *
                                                        "Failed to open %s/%s: %m", dirname, filename);
 
 
-                        r = make_credential_host_secret(dfd, machine_id, flags, dirname, filename, ret, ret_size);
+                        r = make_credential_host_secret(dfd, machine_id, flags, dirname, filename, ret);
                         if (r == -EEXIST) {
                                 log_debug_errno(r, "Credential secret %s/%s appeared while we were creating it, rereading.",
                                                 dirname, filename);
@@ -579,12 +579,9 @@ int get_credential_host_secret(CredentialSecretFlags flags, void **ret, size_t *
                                 if (!copy)
                                         return log_oom_debug();
 
-                                *ret = copy;
+                                *ret = IOVEC_MAKE(copy, sz);
                         }
 
-                        if (ret_size)
-                                *ret_size = sz;
-
                         return 0;
                 }
 
@@ -683,17 +680,15 @@ struct _packed_ metadata_credential_header {
 #define CREDENTIAL_FIELD_SIZE_MAX (16U*1024U)
 
 static int sha256_hash_host_and_tpm2_key(
-                const void *host_key,
-                size_t host_key_size,
-                const void *tpm2_key,
-                size_t tpm2_key_size,
+                const struct iovec *host_key,
+                const struct iovec *tpm2_key,
                 uint8_t ret[static SHA256_DIGEST_LENGTH]) {
 
         _cleanup_(EVP_MD_CTX_freep) EVP_MD_CTX *md = NULL;
         unsigned l;
 
-        assert(host_key_size == 0 || host_key);
-        assert(tpm2_key_size == 0 || tpm2_key);
+        assert(iovec_is_valid(host_key));
+        assert(iovec_is_valid(tpm2_key));
         assert(ret);
 
         /* Combines the host key and the TPM2 HMAC hash into a SHA256 hash value we'll use as symmetric encryption key. */
@@ -705,10 +700,10 @@ static int sha256_hash_host_and_tpm2_key(
         if (EVP_DigestInit_ex(md, EVP_sha256(), NULL) != 1)
                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to initial SHA256 context.");
 
-        if (host_key && EVP_DigestUpdate(md, host_key, host_key_size) != 1)
+        if (iovec_is_set(host_key) && EVP_DigestUpdate(md, host_key->iov_base, host_key->iov_len) != 1)
                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to hash host key.");
 
-        if (tpm2_key && EVP_DigestUpdate(md, tpm2_key, tpm2_key_size) != 1)
+        if (iovec_is_set(tpm2_key) && EVP_DigestUpdate(md, tpm2_key->iov_base, tpm2_key->iov_len) != 1)
                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to hash TPM2 key.");
 
         assert(EVP_MD_CTX_size(md) == SHA256_DIGEST_LENGTH);
@@ -729,28 +724,24 @@ int encrypt_credential_and_warn(
                 uint32_t tpm2_hash_pcr_mask,
                 const char *tpm2_pubkey_path,
                 uint32_t tpm2_pubkey_pcr_mask,
-                const void *input,
-                size_t input_size,
-                void **ret,
-                size_t *ret_size) {
+                const struct iovec *input,
+                CredentialFlags flags,
+                struct iovec *ret) {
 
+        _cleanup_(iovec_done) struct iovec tpm2_blob = {}, tpm2_policy_hash = {}, iv = {}, pubkey = {};
+        _cleanup_(iovec_done_erase) struct iovec tpm2_key = {}, output = {}, host_key = {};
         _cleanup_(EVP_CIPHER_CTX_freep) EVP_CIPHER_CTX *context = NULL;
-        _cleanup_(erase_and_freep) void *host_key = NULL, *tpm2_key = NULL;
-        size_t host_key_size = 0, tpm2_key_size = 0, tpm2_blob_size = 0, tpm2_policy_hash_size = 0, output_size, p, ml;
-        _cleanup_free_ void *tpm2_blob = NULL, *tpm2_policy_hash = NULL, *iv = NULL, *output = NULL;
         _cleanup_free_ struct metadata_credential_header *m = NULL;
         uint16_t tpm2_pcr_bank = 0, tpm2_primary_alg = 0;
         struct encrypted_credential_header *h;
         int ksz, bsz, ivsz, tsz, added, r;
-        _cleanup_free_ void *pubkey = NULL;
-        size_t pubkey_size = 0;
         uint8_t md[SHA256_DIGEST_LENGTH];
         const EVP_CIPHER *cc;
         sd_id128_t id;
+        size_t p, ml;
 
-        assert(input || input_size == 0);
+        assert(iovec_is_valid(input));
         assert(ret);
-        assert(ret_size);
 
         if (!sd_id128_in_set(with_key,
                              _CRED_AUTO,
@@ -760,7 +751,7 @@ int encrypt_credential_and_warn(
                              CRED_AES256_GCM_BY_TPM2_HMAC_WITH_PK,
                              CRED_AES256_GCM_BY_HOST_AND_TPM2_HMAC,
                              CRED_AES256_GCM_BY_HOST_AND_TPM2_HMAC_WITH_PK,
-                             CRED_AES256_GCM_BY_TPM2_ABSENT))
+                             CRED_AES256_GCM_BY_NULL))
                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid key type: " SD_ID128_FORMAT_STR, SD_ID128_FORMAT_VAL(with_key));
 
         if (name && !credential_name_valid(name))
@@ -790,8 +781,7 @@ int encrypt_credential_and_warn(
                                 CREDENTIAL_SECRET_GENERATE|
                                 CREDENTIAL_SECRET_WARN_NOT_ENCRYPTED|
                                 (sd_id128_equal(with_key, _CRED_AUTO) ? CREDENTIAL_SECRET_FAIL_ON_TEMPORARY_FS : 0),
-                                &host_key,
-                                &host_key_size);
+                                &host_key);
                 if (r == -ENOMEDIUM && sd_id128_equal(with_key, _CRED_AUTO))
                         log_debug_errno(r, "Credential host secret location on temporary file system, not using.");
                 else if (r < 0)
@@ -824,7 +814,7 @@ int encrypt_credential_and_warn(
 
                         /* Load public key for PCR policies, if one is specified, or explicitly requested */
 
-                        r = tpm2_load_pcr_public_key(tpm2_pubkey_path, &pubkey, &pubkey_size);
+                        r = tpm2_load_pcr_public_key(tpm2_pubkey_path, &pubkey.iov_base, &pubkey.iov_len);
                         if (r < 0) {
                                 if (tpm2_pubkey_path || r != -ENOENT || !sd_id128_in_set(with_key, _CRED_AUTO, _CRED_AUTO_INITRD))
                                         return log_error_errno(r, "Failed read TPM PCR public key: %m");
@@ -833,7 +823,7 @@ int encrypt_credential_and_warn(
                         }
                 }
 
-                if (!pubkey)
+                if (!iovec_is_set(&pubkey))
                         tpm2_pubkey_pcr_mask = 0;
 
                 _cleanup_(tpm2_context_unrefp) Tpm2Context *tpm2_context = NULL;
@@ -855,8 +845,8 @@ int encrypt_credential_and_warn(
                         return log_error_errno(r, "Could not read PCR values: %m");
 
                 TPM2B_PUBLIC public;
-                if (pubkey) {
-                        r = tpm2_tpm2b_public_from_pem(pubkey, pubkey_size, &public);
+                if (iovec_is_set(&pubkey)) {
+                        r = tpm2_tpm2b_public_from_pem(pubkey.iov_base, pubkey.iov_len, &public);
                         if (r < 0)
                                 return log_error_errno(r, "Could not convert public key to TPM2B_PUBLIC: %m");
                 }
@@ -865,7 +855,7 @@ int encrypt_credential_and_warn(
                 r = tpm2_calculate_sealing_policy(
                                 tpm2_hash_pcr_values,
                                 tpm2_n_hash_pcr_values,
-                                pubkey ? &public : NULL,
+                                iovec_is_set(&pubkey) ? &public : NULL,
                                 /* use_pin= */ false,
                                 /* pcrlock_policy= */ NULL,
                                 &tpm2_policy);
@@ -876,11 +866,10 @@ int encrypt_credential_and_warn(
                               /* seal_key_handle= */ 0,
                               &tpm2_policy,
                               /* pin= */ NULL,
-                              &tpm2_key, &tpm2_key_size,
-                              &tpm2_blob, &tpm2_blob_size,
+                              &tpm2_key,
+                              &tpm2_blob,
                               &tpm2_primary_alg,
-                              /* ret_srk_buf= */ NULL,
-                              /* ret_srk_buf_size= */ NULL);
+                              /* ret_srk= */ NULL);
                 if (r < 0) {
                         if (sd_id128_equal(with_key, _CRED_AUTO_INITRD))
                                 log_warning("TPM2 present and used, but we didn't manage to talk to it. Credential will be refused if SecureBoot is enabled.");
@@ -890,39 +879,36 @@ int encrypt_credential_and_warn(
                         log_notice_errno(r, "TPM2 sealing didn't work, continuing without TPM2: %m");
                 }
 
-                tpm2_policy_hash_size = tpm2_policy.size;
-                tpm2_policy_hash = malloc(tpm2_policy_hash_size);
-                if (!tpm2_policy_hash)
+                if (!iovec_memdup(&IOVEC_MAKE(tpm2_policy.buffer, tpm2_policy.size), &tpm2_policy_hash))
                         return log_oom();
-                memcpy(tpm2_policy_hash, tpm2_policy.buffer, tpm2_policy_hash_size);
 
-                assert(tpm2_blob_size <= CREDENTIAL_FIELD_SIZE_MAX);
-                assert(tpm2_policy_hash_size <= CREDENTIAL_FIELD_SIZE_MAX);
+                assert(tpm2_blob.iov_len <= CREDENTIAL_FIELD_SIZE_MAX);
+                assert(tpm2_policy_hash.iov_len <= CREDENTIAL_FIELD_SIZE_MAX);
         }
 #endif
 
         if (sd_id128_in_set(with_key, _CRED_AUTO, _CRED_AUTO_INITRD)) {
                 /* Let's settle the key type in auto mode now. */
 
-                if (host_key && tpm2_key)
-                        id = pubkey ? CRED_AES256_GCM_BY_HOST_AND_TPM2_HMAC_WITH_PK : CRED_AES256_GCM_BY_HOST_AND_TPM2_HMAC;
-                else if (tpm2_key)
-                        id = pubkey ? CRED_AES256_GCM_BY_TPM2_HMAC_WITH_PK : CRED_AES256_GCM_BY_TPM2_HMAC;
-                else if (host_key)
+                if (iovec_is_set(&host_key) && iovec_is_set(&tpm2_key))
+                        id = iovec_is_set(&pubkey) ? CRED_AES256_GCM_BY_HOST_AND_TPM2_HMAC_WITH_PK : CRED_AES256_GCM_BY_HOST_AND_TPM2_HMAC;
+                else if (iovec_is_set(&tpm2_key))
+                        id = iovec_is_set(&pubkey) ? CRED_AES256_GCM_BY_TPM2_HMAC_WITH_PK : CRED_AES256_GCM_BY_TPM2_HMAC;
+                else if (iovec_is_set(&host_key))
                         id = CRED_AES256_GCM_BY_HOST;
                 else if (sd_id128_equal(with_key, _CRED_AUTO_INITRD))
-                        id = CRED_AES256_GCM_BY_TPM2_ABSENT;
+                        id = CRED_AES256_GCM_BY_NULL;
                 else
                         return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
                                                "TPM2 not available and host key located on temporary file system, no encryption key available.");
         } else
                 id = with_key;
 
-        if (sd_id128_equal(id, CRED_AES256_GCM_BY_TPM2_ABSENT))
+        if (sd_id128_equal(id, CRED_AES256_GCM_BY_NULL) && !FLAGS_SET(flags, CREDENTIAL_ALLOW_NULL))
                 log_warning("Using a null key for encryption and signing. Confidentiality or authenticity will not be provided.");
 
         /* Let's now take the host key and the TPM2 key and hash it together, to use as encryption key for the data */
-        r = sha256_hash_host_and_tpm2_key(host_key, host_key_size, tpm2_key, tpm2_key_size, md);
+        r = sha256_hash_host_and_tpm2_key(&host_key, &tpm2_key, md);
         if (r < 0)
                 return r;
 
@@ -939,11 +925,13 @@ int encrypt_credential_and_warn(
         if (ivsz > 0) {
                 assert((size_t) ivsz <= CREDENTIAL_FIELD_SIZE_MAX);
 
-                iv = malloc(ivsz);
-                if (!iv)
+                iv.iov_base = malloc(ivsz);
+                if (!iv.iov_base)
                         return log_oom();
 
-                r = crypto_random_bytes(iv, ivsz);
+                iv.iov_len = ivsz;
+
+                r = crypto_random_bytes(iv.iov_base, iv.iov_len);
                 if (r < 0)
                         return log_error_errno(r, "Failed to acquired randomized IV: %m");
         }
@@ -955,61 +943,61 @@ int encrypt_credential_and_warn(
                 return log_error_errno(SYNTHETIC_ERRNO(ENOMEM), "Failed to allocate encryption object: %s",
                                        ERR_error_string(ERR_get_error(), NULL));
 
-        if (EVP_EncryptInit_ex(context, cc, NULL, md, iv) != 1)
+        if (EVP_EncryptInit_ex(context, cc, NULL, md, iv.iov_base) != 1)
                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to initialize encryption context: %s",
                                        ERR_error_string(ERR_get_error(), NULL));
 
         /* Just an upper estimate */
-        output_size =
+        output.iov_len =
                 ALIGN8(offsetof(struct encrypted_credential_header, iv) + ivsz) +
-                ALIGN8(tpm2_key ? offsetof(struct tpm2_credential_header, policy_hash_and_blob) + tpm2_blob_size + tpm2_policy_hash_size : 0) +
-                ALIGN8(pubkey ? offsetof(struct tpm2_public_key_credential_header, data) + pubkey_size : 0) +
+                ALIGN8(iovec_is_set(&tpm2_key) ? offsetof(struct tpm2_credential_header, policy_hash_and_blob) + tpm2_blob.iov_len + tpm2_policy_hash.iov_len : 0) +
+                ALIGN8(iovec_is_set(&pubkey) ? offsetof(struct tpm2_public_key_credential_header, data) + pubkey.iov_len : 0) +
                 ALIGN8(offsetof(struct metadata_credential_header, name) + strlen_ptr(name)) +
-                input_size + 2U * (size_t) bsz +
+                input->iov_len + 2U * (size_t) bsz +
                 tsz;
 
-        output = malloc0(output_size);
-        if (!output)
+        output.iov_base = malloc0(output.iov_len);
+        if (!output.iov_base)
                 return log_oom();
 
-        h = (struct encrypted_credential_header*) output;
+        h = (struct encrypted_credential_header*) output.iov_base;
         h->id = id;
         h->block_size = htole32(bsz);
         h->key_size = htole32(ksz);
         h->tag_size = htole32(tsz);
         h->iv_size = htole32(ivsz);
-        memcpy(h->iv, iv, ivsz);
+        memcpy(h->iv, iv.iov_base, ivsz);
 
         p = ALIGN8(offsetof(struct encrypted_credential_header, iv) + ivsz);
 
-        if (tpm2_key) {
+        if (iovec_is_set(&tpm2_key)) {
                 struct tpm2_credential_header *t;
 
-                t = (struct tpm2_credential_header*) ((uint8_t*) output + p);
+                t = (struct tpm2_credential_header*) ((uint8_t*) output.iov_base + p);
                 t->pcr_mask = htole64(tpm2_hash_pcr_mask);
                 t->pcr_bank = htole16(tpm2_pcr_bank);
                 t->primary_alg = htole16(tpm2_primary_alg);
-                t->blob_size = htole32(tpm2_blob_size);
-                t->policy_hash_size = htole32(tpm2_policy_hash_size);
-                memcpy(t->policy_hash_and_blob, tpm2_blob, tpm2_blob_size);
-                memcpy(t->policy_hash_and_blob + tpm2_blob_size, tpm2_policy_hash, tpm2_policy_hash_size);
+                t->blob_size = htole32(tpm2_blob.iov_len);
+                t->policy_hash_size = htole32(tpm2_policy_hash.iov_len);
+                memcpy(t->policy_hash_and_blob, tpm2_blob.iov_base, tpm2_blob.iov_len);
+                memcpy(t->policy_hash_and_blob + tpm2_blob.iov_len, tpm2_policy_hash.iov_base, tpm2_policy_hash.iov_len);
 
-                p += ALIGN8(offsetof(struct tpm2_credential_header, policy_hash_and_blob) + tpm2_blob_size + tpm2_policy_hash_size);
+                p += ALIGN8(offsetof(struct tpm2_credential_header, policy_hash_and_blob) + tpm2_blob.iov_len + tpm2_policy_hash.iov_len);
         }
 
-        if (pubkey) {
+        if (iovec_is_set(&pubkey)) {
                 struct tpm2_public_key_credential_header *z;
 
-                z = (struct tpm2_public_key_credential_header*) ((uint8_t*) output + p);
+                z = (struct tpm2_public_key_credential_header*) ((uint8_t*) output.iov_base + p);
                 z->pcr_mask = htole64(tpm2_pubkey_pcr_mask);
-                z->size = htole32(pubkey_size);
-                memcpy(z->data, pubkey, pubkey_size);
+                z->size = htole32(pubkey.iov_len);
+                memcpy(z->data, pubkey.iov_base, pubkey.iov_len);
 
-                p += ALIGN8(offsetof(struct tpm2_public_key_credential_header, data) + pubkey_size);
+                p += ALIGN8(offsetof(struct tpm2_public_key_credential_header, data) + pubkey.iov_len);
         }
 
         /* Pass the encrypted + TPM2 header as AAD */
-        if (EVP_EncryptUpdate(context, NULL, &added, output, p) != 1)
+        if (EVP_EncryptUpdate(context, NULL, &added, output.iov_base, p) != 1)
                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to write AAD data: %s",
                                        ERR_error_string(ERR_get_error(), NULL));
 
@@ -1025,53 +1013,52 @@ int encrypt_credential_and_warn(
         memcpy_safe(m->name, name, ml);
 
         /* And encrypt the metadata header */
-        if (EVP_EncryptUpdate(context, (uint8_t*) output + p, &added, (const unsigned char*) m, ALIGN8(offsetof(struct metadata_credential_header, name) + ml)) != 1)
+        if (EVP_EncryptUpdate(context, (uint8_t*) output.iov_base + p, &added, (const unsigned char*) m, ALIGN8(offsetof(struct metadata_credential_header, name) + ml)) != 1)
                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to encrypt metadata header: %s",
                                        ERR_error_string(ERR_get_error(), NULL));
 
         assert(added >= 0);
-        assert((size_t) added <= output_size - p);
+        assert((size_t) added <= output.iov_len - p);
         p += added;
 
         /* Then encrypt the plaintext */
-        if (EVP_EncryptUpdate(context, (uint8_t*) output + p, &added, input, input_size) != 1)
+        if (EVP_EncryptUpdate(context, (uint8_t*) output.iov_base + p, &added, input->iov_base, input->iov_len) != 1)
                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to encrypt data: %s",
                                        ERR_error_string(ERR_get_error(), NULL));
 
         assert(added >= 0);
-        assert((size_t) added <= output_size - p);
+        assert((size_t) added <= output.iov_len - p);
         p += added;
 
         /* Finalize */
-        if (EVP_EncryptFinal_ex(context, (uint8_t*) output + p, &added) != 1)
+        if (EVP_EncryptFinal_ex(context, (uint8_t*) output.iov_base + p, &added) != 1)
                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to finalize data encryption: %s",
                                        ERR_error_string(ERR_get_error(), NULL));
 
         assert(added >= 0);
-        assert((size_t) added <= output_size - p);
+        assert((size_t) added <= output.iov_len - p);
         p += added;
 
-        assert(p <= output_size - tsz);
+        assert(p <= output.iov_len - tsz);
 
         /* Append tag */
-        if (EVP_CIPHER_CTX_ctrl(context, EVP_CTRL_GCM_GET_TAG, tsz, (uint8_t*) output + p) != 1)
+        if (EVP_CIPHER_CTX_ctrl(context, EVP_CTRL_GCM_GET_TAG, tsz, (uint8_t*) output.iov_base + p) != 1)
                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to get tag: %s",
                                        ERR_error_string(ERR_get_error(), NULL));
 
         p += tsz;
-        assert(p <= output_size);
+        assert(p <= output.iov_len);
+        output.iov_len = p;
 
-        if (DEBUG_LOGGING && input_size > 0) {
+        if (DEBUG_LOGGING && input->iov_len > 0) {
                 size_t base64_size;
 
-                base64_size = DIV_ROUND_UP(p * 4, 3); /* Include base64 size increase in debug output */
-                assert(base64_size >= input_size);
-                log_debug("Input of %zu bytes grew to output of %zu bytes (+%2zu%%).", input_size, base64_size, base64_size * 100 / input_size - 100);
+                base64_size = DIV_ROUND_UP(output.iov_len * 4, 3); /* Include base64 size increase in debug output */
+                assert(base64_size >= input->iov_len);
+                log_debug("Input of %zu bytes grew to output of %zu bytes (+%2zu%%).", input->iov_len, base64_size, base64_size * 100 / input->iov_len - 100);
         }
 
-        *ret = TAKE_PTR(output);
-        *ret_size = p;
-
+        *ret = TAKE_STRUCT(output);
         return 0;
 }
 
@@ -1080,38 +1067,36 @@ int decrypt_credential_and_warn(
                 usec_t validate_timestamp,
                 const char *tpm2_device,
                 const char *tpm2_signature_path,
-                const void *input,
-                size_t input_size,
-                void **ret,
-                size_t *ret_size) {
+                const struct iovec *input,
+                CredentialFlags flags,
+                struct iovec *ret) {
 
-        _cleanup_(erase_and_freep) void *host_key = NULL, *tpm2_key = NULL, *plaintext = NULL;
+        _cleanup_(iovec_done_erase) struct iovec host_key = {}, plaintext = {}, tpm2_key = {};
         _cleanup_(json_variant_unrefp) JsonVariant *signature_json = NULL;
         _cleanup_(EVP_CIPHER_CTX_freep) EVP_CIPHER_CTX *context = NULL;
-        size_t host_key_size = 0, tpm2_key_size = 0, plaintext_size, p, hs;
         struct encrypted_credential_header *h;
         struct metadata_credential_header *m;
         uint8_t md[SHA256_DIGEST_LENGTH];
-        bool with_tpm2, with_host_key, is_tpm2_absent, with_tpm2_pk;
+        bool with_tpm2, with_tpm2_pk, with_host_key, with_null;
         const EVP_CIPHER *cc;
+        size_t p, hs;
         int r, added;
 
-        assert(input || input_size == 0);
+        assert(iovec_is_valid(input));
         assert(ret);
-        assert(ret_size);
 
-        h = (struct encrypted_credential_header*) input;
+        h = (struct encrypted_credential_header*) input->iov_base;
 
         /* The ID must fit in, for the current and all future formats */
-        if (input_size < sizeof(h->id))
+        if (input->iov_len < sizeof(h->id))
                 return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Encrypted file too short.");
 
         with_host_key = sd_id128_in_set(h->id, CRED_AES256_GCM_BY_HOST, CRED_AES256_GCM_BY_HOST_AND_TPM2_HMAC, CRED_AES256_GCM_BY_HOST_AND_TPM2_HMAC_WITH_PK);
         with_tpm2_pk = sd_id128_in_set(h->id, CRED_AES256_GCM_BY_TPM2_HMAC_WITH_PK, CRED_AES256_GCM_BY_HOST_AND_TPM2_HMAC_WITH_PK);
         with_tpm2 = sd_id128_in_set(h->id, CRED_AES256_GCM_BY_TPM2_HMAC, CRED_AES256_GCM_BY_HOST_AND_TPM2_HMAC) || with_tpm2_pk;
-        is_tpm2_absent = sd_id128_equal(h->id, CRED_AES256_GCM_BY_TPM2_ABSENT);
+        with_null = sd_id128_equal(h->id, CRED_AES256_GCM_BY_NULL);
 
-        if (!with_host_key && !with_tpm2 && !is_tpm2_absent)
+        if (!with_host_key && !with_tpm2 && !with_null)
                 return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Unknown encryption format, or corrupted data: %m");
 
         if (with_tpm2_pk) {
@@ -1120,7 +1105,7 @@ int decrypt_credential_and_warn(
                         return log_error_errno(r, "Failed to load pcr signature: %m");
         }
 
-        if (is_tpm2_absent) {
+        if (with_null && !FLAGS_SET(flags, CREDENTIAL_ALLOW_NULL)) {
                 /* So this is a credential encrypted with a zero length key. We support this to cover for the
                  * case where neither a host key not a TPM2 are available (specifically: initrd environments
                  * where the host key is not yet accessible and no TPM2 chip exists at all), to minimize
@@ -1141,7 +1126,7 @@ int decrypt_credential_and_warn(
         }
 
         /* Now we know the minimum header size */
-        if (input_size < offsetof(struct encrypted_credential_header, iv))
+        if (input->iov_len < offsetof(struct encrypted_credential_header, iv))
                 return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Encrypted file too short.");
 
         /* Verify some basic header values */
@@ -1156,7 +1141,7 @@ int decrypt_credential_and_warn(
 
         /* Ensure we have space for the full header now (we don't know the size of the name hence this is a
          * lower limit only) */
-        if (input_size <
+        if (input->iov_len <
             ALIGN8(offsetof(struct encrypted_credential_header, iv) + le32toh(h->iv_size)) +
             ALIGN8(with_tpm2 ? offsetof(struct tpm2_credential_header, policy_hash_and_blob) : 0) +
             ALIGN8(with_tpm2_pk ? offsetof(struct tpm2_public_key_credential_header, data) : 0) +
@@ -1168,7 +1153,7 @@ int decrypt_credential_and_warn(
 
         if (with_tpm2) {
 #if HAVE_TPM2
-                struct tpm2_credential_header* t = (struct tpm2_credential_header*) ((uint8_t*) input + p);
+                struct tpm2_credential_header* t = (struct tpm2_credential_header*) ((uint8_t*) input->iov_base + p);
                 struct tpm2_public_key_credential_header *z = NULL;
 
                 if (!TPM2_PCR_MASK_VALID(t->pcr_mask))
@@ -1184,7 +1169,7 @@ int decrypt_credential_and_warn(
 
                 /* Ensure we have space for the full TPM2 header now (still don't know the name, and its size
                  * though, hence still just a lower limit test only) */
-                if (input_size <
+                if (input->iov_len <
                     ALIGN8(offsetof(struct encrypted_credential_header, iv) + le32toh(h->iv_size)) +
                     ALIGN8(offsetof(struct tpm2_credential_header, policy_hash_and_blob) + le32toh(t->blob_size) + le32toh(t->policy_hash_size)) +
                     ALIGN8(with_tpm2_pk ? offsetof(struct tpm2_public_key_credential_header, data) : 0) +
@@ -1197,14 +1182,14 @@ int decrypt_credential_and_warn(
                             le32toh(t->policy_hash_size));
 
                 if (with_tpm2_pk) {
-                        z = (struct tpm2_public_key_credential_header*) ((uint8_t*) input + p);
+                        z = (struct tpm2_public_key_credential_header*) ((uint8_t*) input->iov_base + p);
 
                         if (!TPM2_PCR_MASK_VALID(le64toh(z->pcr_mask)) || le64toh(z->pcr_mask) == 0)
                                 return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "TPM2 PCR mask out of range.");
                         if (le32toh(z->size) > PUBLIC_KEY_MAX)
                                 return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Unexpected public key size.");
 
-                        if (input_size <
+                        if (input->iov_len <
                             ALIGN8(offsetof(struct encrypted_credential_header, iv) + le32toh(h->iv_size)) +
                             ALIGN8(offsetof(struct tpm2_credential_header, policy_hash_and_blob) + le32toh(t->blob_size) + le32toh(t->policy_hash_size)) +
                             ALIGN8(offsetof(struct tpm2_public_key_credential_header, data) + le32toh(z->size)) +
@@ -1226,21 +1211,16 @@ int decrypt_credential_and_warn(
                 r = tpm2_unseal(tpm2_context,
                                 le64toh(t->pcr_mask),
                                 le16toh(t->pcr_bank),
-                                z ? z->data : NULL,
-                                z ? le32toh(z->size) : 0,
+                                z ? &IOVEC_MAKE(z->data, le32toh(z->size)) : NULL,
                                 z ? le64toh(z->pcr_mask) : 0,
                                 signature_json,
                                 /* pin= */ NULL,
                                 /* pcrlock_policy= */ NULL,
                                 le16toh(t->primary_alg),
-                                t->policy_hash_and_blob,
-                                le32toh(t->blob_size),
-                                t->policy_hash_and_blob + le32toh(t->blob_size),
-                                le32toh(t->policy_hash_size),
-                                /* srk_buf= */ NULL,
-                                /* srk_buf_size= */ 0,
-                                &tpm2_key,
-                                &tpm2_key_size);
+                                &IOVEC_MAKE(t->policy_hash_and_blob, le32toh(t->blob_size)),
+                                &IOVEC_MAKE(t->policy_hash_and_blob + le32toh(t->blob_size), le32toh(t->policy_hash_size)),
+                                /* srk= */ NULL,
+                                &tpm2_key);
                 if (r < 0)
                         return log_error_errno(r, "Failed to unseal secret using TPM2: %m");
 #else
@@ -1249,18 +1229,15 @@ int decrypt_credential_and_warn(
         }
 
         if (with_host_key) {
-                r = get_credential_host_secret(
-                                0,
-                                &host_key,
-                                &host_key_size);
+                r = get_credential_host_secret(/* flags= */ 0, &host_key);
                 if (r < 0)
                         return log_error_errno(r, "Failed to determine local credential key: %m");
         }
 
-        if (is_tpm2_absent)
+        if (with_null && !FLAGS_SET(flags, CREDENTIAL_ALLOW_NULL))
                 log_warning("Warning: using a null key for decryption and authentication. Confidentiality or authenticity are not provided.");
 
-        sha256_hash_host_and_tpm2_key(host_key, host_key_size, tpm2_key, tpm2_key_size, md);
+        sha256_hash_host_and_tpm2_key(&host_key, &tpm2_key, md);
 
         assert_se(cc = EVP_aes_256_gcm());
 
@@ -1287,41 +1264,41 @@ int decrypt_credential_and_warn(
                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to set IV and key: %s",
                                        ERR_error_string(ERR_get_error(), NULL));
 
-        if (EVP_DecryptUpdate(context, NULL, &added, input, p) != 1)
+        if (EVP_DecryptUpdate(context, NULL, &added, input->iov_base, p) != 1)
                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to write AAD data: %s",
                                        ERR_error_string(ERR_get_error(), NULL));
 
-        plaintext = malloc(input_size - p - le32toh(h->tag_size));
-        if (!plaintext)
+        plaintext.iov_base = malloc(input->iov_len - p - le32toh(h->tag_size));
+        if (!plaintext.iov_base)
                 return -ENOMEM;
 
         if (EVP_DecryptUpdate(
                             context,
-                            plaintext,
+                            plaintext.iov_base,
                             &added,
-                            (uint8_t*) input + p,
-                            input_size - p - le32toh(h->tag_size)) != 1)
+                            (uint8_t*) input->iov_base + p,
+                            input->iov_len - p - le32toh(h->tag_size)) != 1)
                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to decrypt data: %s",
                                        ERR_error_string(ERR_get_error(), NULL));
 
         assert(added >= 0);
-        assert((size_t) added <= input_size - p - le32toh(h->tag_size));
-        plaintext_size = added;
+        assert((size_t) added <= input->iov_len - p - le32toh(h->tag_size));
+        plaintext.iov_len = added;
 
-        if (EVP_CIPHER_CTX_ctrl(context, EVP_CTRL_GCM_SET_TAG, le32toh(h->tag_size), (uint8_t*) input + input_size - le32toh(h->tag_size)) != 1)
+        if (EVP_CIPHER_CTX_ctrl(context, EVP_CTRL_GCM_SET_TAG, le32toh(h->tag_size), (uint8_t*) input->iov_base + input->iov_len - le32toh(h->tag_size)) != 1)
                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to set tag: %s",
                                        ERR_error_string(ERR_get_error(), NULL));
 
-        if (EVP_DecryptFinal_ex(context, (uint8_t*) plaintext + plaintext_size, &added) != 1)
+        if (EVP_DecryptFinal_ex(context, (uint8_t*) plaintext.iov_base + plaintext.iov_len, &added) != 1)
                 return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Decryption failed (incorrect key?): %s",
                                        ERR_error_string(ERR_get_error(), NULL));
 
-        plaintext_size += added;
+        plaintext.iov_len += added;
 
-        if (plaintext_size < ALIGN8(offsetof(struct metadata_credential_header, name)))
+        if (plaintext.iov_len < ALIGN8(offsetof(struct metadata_credential_header, name)))
                 return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Metadata header incomplete.");
 
-        m = plaintext;
+        m = plaintext.iov_base;
 
         if (le64toh(m->timestamp) != USEC_INFINITY &&
             le64toh(m->not_after) != USEC_INFINITY &&
@@ -1332,7 +1309,7 @@ int decrypt_credential_and_warn(
                 return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Embedded credential name too long, refusing.");
 
         hs = ALIGN8(offsetof(struct metadata_credential_header, name) + le32toh(m->name_size));
-        if (plaintext_size < hs)
+        if (plaintext.iov_len < hs)
                 return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Metadata header incomplete.");
 
         if (le32toh(m->name_size) > 0) {
@@ -1374,32 +1351,30 @@ int decrypt_credential_and_warn(
         }
 
         if (ret) {
-                char *without_metadata;
+                _cleanup_(iovec_done_erase) struct iovec without_metadata = {};
 
-                without_metadata = memdup((uint8_t*) plaintext + hs, plaintext_size - hs);
-                if (!without_metadata)
+                without_metadata.iov_len = plaintext.iov_len - hs;
+                without_metadata.iov_base = memdup_suffix0((uint8_t*) plaintext.iov_base + hs, without_metadata.iov_len);
+                if (!without_metadata.iov_base)
                         return log_oom();
 
-                *ret = without_metadata;
+                *ret = TAKE_STRUCT(without_metadata);
         }
 
-        if (ret_size)
-                *ret_size = plaintext_size - hs;
-
         return 0;
 }
 
 #else
 
-int get_credential_host_secret(CredentialSecretFlags flags, void **ret, size_t *ret_size) {
+int get_credential_host_secret(CredentialSecretFlags flags, struct iovec *ret) {
         return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Support for encrypted credentials not available.");
 }
 
-int encrypt_credential_and_warn(sd_id128_t with_key, const char *name, usec_t timestamp, usec_t not_after, const char *tpm2_device, uint32_t tpm2_hash_pcr_mask, const char *tpm2_pubkey_path, uint32_t tpm2_pubkey_pcr_mask, const void *input, size_t input_size, void **ret, size_t *ret_size) {
+int encrypt_credential_and_warn(sd_id128_t with_key, const char *name, usec_t timestamp, usec_t not_after, const char *tpm2_device, uint32_t tpm2_hash_pcr_mask, const char *tpm2_pubkey_path, uint32_t tpm2_pubkey_pcr_mask, const struct iovec *input, CredentialFlags flags, struct iovec *ret) {
         return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Support for encrypted credentials not available.");
 }
 
-int decrypt_credential_and_warn(const char *validate_name, usec_t validate_timestamp, const char *tpm2_device, const char *tpm2_signature_path, const void *input, size_t input_size, void **ret, size_t *ret_size) {
+int decrypt_credential_and_warn(const char *validate_name, usec_t validate_timestamp, const char *tpm2_device, const char *tpm2_signature_path, const struct iovec *input, CredentialFlags flags, struct iovec *ret) {
         return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Support for encrypted credentials not available.");
 }
 
index 36ca0fb61009a8b18e559267d12a900c5b228c72..9362d4e52c4c72109fe563490eedadf553803f0b 100644 (file)
@@ -53,10 +53,14 @@ typedef enum CredentialSecretFlags {
         CREDENTIAL_SECRET_FAIL_ON_TEMPORARY_FS = 1 << 2,
 } CredentialSecretFlags;
 
-int get_credential_host_secret(CredentialSecretFlags flags, void **ret, size_t *ret_size);
+int get_credential_host_secret(CredentialSecretFlags flags, struct iovec *ret);
 
 int get_credential_user_password(const char *username, char **ret_password, bool *ret_is_hashed);
 
+typedef enum CredentialFlags {
+        CREDENTIAL_ALLOW_NULL = 1 << 0, /* allow decryption of NULL key, even if TPM is around */
+} CredentialFlags;
+
 /* The four modes we support: keyed only by on-disk key, only by TPM2 HMAC key, and by the combination of
  * both, as well as one with a fixed zero length key if TPM2 is missing (the latter of course provides no
  * authenticity or confidentiality, but is still useful for integrity protection, and makes things simpler
@@ -67,7 +71,7 @@ int get_credential_user_password(const char *username, char **ret_password, bool
 #define CRED_AES256_GCM_BY_HOST_AND_TPM2_HMAC SD_ID128_MAKE(93,a8,94,09,48,74,44,90,90,ca,f2,fc,93,ca,b5,53)
 #define CRED_AES256_GCM_BY_HOST_AND_TPM2_HMAC_WITH_PK           \
                                               SD_ID128_MAKE(af,49,50,a8,49,13,4e,b1,a7,38,46,30,4f,f3,0c,05)
-#define CRED_AES256_GCM_BY_TPM2_ABSENT        SD_ID128_MAKE(05,84,69,da,f6,f5,43,24,80,05,49,da,0f,8e,a2,fb)
+#define CRED_AES256_GCM_BY_NULL               SD_ID128_MAKE(05,84,69,da,f6,f5,43,24,80,05,49,da,0f,8e,a2,fb)
 
 /* Two special IDs to pick a general automatic mode (i.e. tpm2+host if TPM2 exists, only host otherwise) or
  * an initrd-specific automatic mode (i.e. tpm2 if firmware can do it, otherwise fixed zero-length key, and
@@ -77,5 +81,5 @@ int get_credential_user_password(const char *username, char **ret_password, bool
 #define _CRED_AUTO                            SD_ID128_MAKE(a2,19,cb,07,85,b2,4c,04,b1,6d,18,ca,b9,d2,ee,01)
 #define _CRED_AUTO_INITRD                     SD_ID128_MAKE(02,dc,8e,de,3a,02,43,ab,a9,ec,54,9c,05,e6,a0,71)
 
-int encrypt_credential_and_warn(sd_id128_t with_key, const char *name, usec_t timestamp, usec_t not_after, const char *tpm2_device, uint32_t tpm2_hash_pcr_mask, const char *tpm2_pubkey_path, uint32_t tpm2_pubkey_pcr_mask, const void *input, size_t input_size, void **ret, size_t *ret_size);
-int decrypt_credential_and_warn(const char *validate_name, usec_t validate_timestamp, const char *tpm2_device, const char *tpm2_signature_path, const void *input, size_t input_size, void **ret, size_t *ret_size);
+int encrypt_credential_and_warn(sd_id128_t with_key, const char *name, usec_t timestamp, usec_t not_after, const char *tpm2_device, uint32_t tpm2_hash_pcr_mask, const char *tpm2_pubkey_path, uint32_t tpm2_pubkey_pcr_mask, const struct iovec *input, CredentialFlags flags, struct iovec *ret);
+int decrypt_credential_and_warn(const char *validate_name, usec_t validate_timestamp, const char *tpm2_device, const char *tpm2_signature_path, const struct iovec *input, CredentialFlags flags, struct iovec *ret);
index 6e559e17f4ad13189b8415234cd534371a5b4ee8..2d59a630a65b8b013d535ea29dc7d3135a3f7e9e 100644 (file)
@@ -177,7 +177,7 @@ int acquire_fido2_key_auto(
                         return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
                                                "FIDO2 token data lacks 'fido2-credential' field.");
 
-                r = unbase64mem(json_variant_string(w), SIZE_MAX, &cid, &cid_size);
+                r = unbase64mem(json_variant_string(w), &cid, &cid_size);
                 if (r < 0)
                         return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
                                                "Invalid base64 data in 'fido2-credential' field.");
@@ -189,7 +189,7 @@ int acquire_fido2_key_auto(
 
                 assert(!salt);
                 assert(salt_size == 0);
-                r = unbase64mem(json_variant_string(w), SIZE_MAX, &salt, &salt_size);
+                r = unbase64mem(json_variant_string(w), &salt, &salt_size);
                 if (r < 0)
                         return log_error_errno(r, "Failed to decode base64 encoded salt.");
 
index f7ed16193f183881ebfc72f064d597f70bf555d4..08fdd60570470a66c0b767a30ba3203b2c686e71 100644 (file)
@@ -7,6 +7,7 @@
 #include "alloc-util.h"
 #include "dev-setup.h"
 #include "fd-util.h"
+#include "fs-util.h"
 #include "label-util.h"
 #include "lock-util.h"
 #include "log.h"
@@ -79,15 +80,11 @@ int make_inaccessible_nodes(
                 uid_t uid,
                 gid_t gid) {
 
-        static const struct {
-                const char *name;
-                mode_t mode;
-        } table[] = {
-                { "inaccessible",      S_IFDIR  | 0755 },
-                { "inaccessible/reg",  S_IFREG  | 0000 },
-                { "inaccessible/dir",  S_IFDIR  | 0000 },
-                { "inaccessible/fifo", S_IFIFO  | 0000 },
-                { "inaccessible/sock", S_IFSOCK | 0000 },
+        static const mode_t table[] = {
+                S_IFREG,
+                S_IFDIR,
+                S_IFIFO,
+                S_IFSOCK,
 
                 /* The following two are likely to fail if we lack the privs for it (for example in an userns
                  * environment, if CAP_SYS_MKNOD is missing, or if a device node policy prohibits creation of
@@ -95,10 +92,13 @@ int make_inaccessible_nodes(
                  * should implement falling back to use a different node then, for example
                  * <root>/inaccessible/sock, which is close enough in behaviour and semantics for most uses.
                  */
-                { "inaccessible/chr",  S_IFCHR  | 0000 },
-                { "inaccessible/blk",  S_IFBLK  | 0000 },
+                S_IFCHR,
+                S_IFBLK,
+
+                /* NB: S_IFLNK is not listed here, as there is no such thing as an inaccessible symlink */
         };
 
+        _cleanup_close_ int parent_fd = -EBADF, inaccessible_fd = -EBADF;
         int r;
 
         if (!parent_dir)
@@ -106,32 +106,48 @@ int make_inaccessible_nodes(
 
         BLOCK_WITH_UMASK(0000);
 
+        parent_fd = open(parent_dir, O_DIRECTORY|O_CLOEXEC|O_PATH, 0);
+        if (parent_fd < 0)
+                return -errno;
+
+        inaccessible_fd = open_mkdir_at(parent_fd, "inaccessible", O_CLOEXEC, 0755);
+        if (inaccessible_fd < 0)
+                return inaccessible_fd;
+
         /* Set up inaccessible (and empty) file nodes of all types. This are used to as mount sources for over-mounting
          * ("masking") file nodes that shall become inaccessible and empty for specific containers or services. We try
          * to lock down these nodes as much as we can, but otherwise try to match them as closely as possible with the
          * underlying file, i.e. in the best case we offer the same node type as the underlying node. */
 
-        for (size_t i = 0; i < ELEMENTSOF(table); i++) {
+        FOREACH_ARRAY(m, table, ELEMENTSOF(table)) {
                 _cleanup_free_ char *path = NULL;
+                mode_t inode_type = *m;
+                const char *fn;
 
-                path = path_join(parent_dir, table[i].name);
+                fn = inode_type_to_string(inode_type);
+                path = path_join(parent_dir, fn);
                 if (!path)
                         return log_oom();
 
-                if (S_ISDIR(table[i].mode))
-                        r = mkdir_label(path, table[i].mode & 07777);
+                if (S_ISDIR(inode_type))
+                        r = mkdirat_label(inaccessible_fd, fn, 0000);
                 else
-                        r = mknod_label(path, table[i].mode, makedev(0, 0));
-                if (r < 0) {
+                        r = RET_NERRNO(mknodat(inaccessible_fd, fn, inode_type | 0000, makedev(0, 0)));
+                if (r == -EEXIST) {
+                        if (fchmodat(inaccessible_fd, fn, 0000, AT_SYMLINK_NOFOLLOW) < 0)
+                                log_debug_errno(errno, "Failed to adjust access mode of existing inode '%s', ignoring: %m", path);
+                } else if (r < 0) {
                         log_debug_errno(r, "Failed to create '%s', ignoring: %m", path);
                         continue;
                 }
 
-                if (uid != UID_INVALID || gid != GID_INVALID) {
-                        if (lchown(path, uid, gid) < 0)
-                                log_debug_errno(errno, "Failed to chown '%s': %m", path);
-                }
+                if (uid_is_valid(uid) || gid_is_valid(gid))
+                        if (fchownat(inaccessible_fd, fn, uid, gid, AT_SYMLINK_NOFOLLOW) < 0)
+                                log_debug_errno(errno, "Failed to chown '%s', ignoring: %m", path);
         }
 
+        if (fchmod(inaccessible_fd, 0555) < 0)
+                log_debug_errno(errno, "Failed to mark inaccessible directory read-only, ignoring: %m");
+
         return 0;
 }
index 3baa84c8bd52b0523e6b8b5023b796c391c9e9af..6d4f7612caac15c1ff3e4c00c01816817d7c8026 100644 (file)
@@ -45,6 +45,7 @@
 #include "strv.h"
 #include "time-util.h"
 #include "utf8.h"
+#include "vpick.h"
 #include "xattr-util.h"
 
 static const char* const image_search_path[_IMAGE_CLASS_MAX] = {
@@ -215,40 +216,60 @@ static int image_new(
         return 0;
 }
 
-static int extract_pretty(
+static int extract_image_basename(
                 const char *path,
-                const char *class_suffix,
-                const char *format_suffix,
-                char **ret) {
+                const char *class_suffix,  /* e.g. ".sysext" (this is an optional suffix) */
+                char **format_suffixes,    /* e.g. ".raw"    (one of these will be required) */
+                char **ret_basename,
+                char **ret_suffix) {
 
-        _cleanup_free_ char *name = NULL;
+        _cleanup_free_ char *name = NULL, *suffix = NULL;
         int r;
 
         assert(path);
-        assert(ret);
 
         r = path_extract_filename(path, &name);
         if (r < 0)
                 return r;
 
-        if (format_suffix) {
-                char *e = endswith(name, format_suffix);
+        if (format_suffixes) {
+                char *e = endswith_strv(name, format_suffixes);
                 if (!e) /* Format suffix is required */
                         return -EINVAL;
 
+                if (ret_suffix) {
+                        suffix = strdup(e);
+                        if (!suffix)
+                                return -ENOMEM;
+                }
+
                 *e = 0;
         }
 
         if (class_suffix) {
                 char *e = endswith(name, class_suffix);
-                if (e) /* Class suffix is optional */
+                if (e) { /* Class suffix is optional */
+                        if (ret_suffix) {
+                                _cleanup_free_ char *j = strjoin(e, suffix);
+                                if (!j)
+                                        return -ENOMEM;
+
+                                free_and_replace(suffix, j);
+                        }
+
                         *e = 0;
+                }
         }
 
         if (!image_name_is_valid(name))
                 return -EINVAL;
 
-        *ret = TAKE_PTR(name);
+        if (ret_suffix)
+                *ret_suffix = TAKE_PTR(suffix);
+
+        if (ret_basename)
+                *ret_basename = TAKE_PTR(name);
+
         return 0;
 }
 
@@ -303,7 +324,12 @@ static int image_make(
                         return 0;
 
                 if (!pretty) {
-                        r = extract_pretty(filename, image_class_suffix_to_string(c), NULL, &pretty_buffer);
+                        r = extract_image_basename(
+                                        filename,
+                                        image_class_suffix_to_string(c),
+                                        /* format_suffix= */ NULL,
+                                        &pretty_buffer,
+                                        /* ret_suffix= */ NULL);
                         if (r < 0)
                                 return r;
 
@@ -390,7 +416,12 @@ static int image_make(
                 (void) fd_getcrtime_at(dfd, filename, AT_SYMLINK_FOLLOW, &crtime);
 
                 if (!pretty) {
-                        r = extract_pretty(filename, image_class_suffix_to_string(c), ".raw", &pretty_buffer);
+                        r = extract_image_basename(
+                                        filename,
+                                        image_class_suffix_to_string(c),
+                                        STRV_MAKE(".raw"),
+                                        &pretty_buffer,
+                                        /* ret_suffix= */ NULL);
                         if (r < 0)
                                 return r;
 
@@ -424,7 +455,12 @@ static int image_make(
                         return 0;
 
                 if (!pretty) {
-                        r = extract_pretty(filename, NULL, NULL, &pretty_buffer);
+                        r = extract_image_basename(
+                                        filename,
+                                        /* class_suffix= */ NULL,
+                                        /* format_suffix= */ NULL,
+                                        &pretty_buffer,
+                                        /* ret_suffix= */ NULL);
                         if (r < 0)
                                 return r;
 
@@ -488,6 +524,37 @@ static const char *pick_image_search_path(ImageClass class) {
         return in_initrd() && image_search_path_initrd[class] ? image_search_path_initrd[class] : image_search_path[class];
 }
 
+static char **make_possible_filenames(ImageClass class, const char *image_name) {
+        _cleanup_strv_free_ char **l = NULL;
+
+        assert(image_name);
+
+        FOREACH_STRING(v_suffix, "", ".v")
+                FOREACH_STRING(format_suffix, "", ".raw") {
+                        _cleanup_free_ char *j = NULL;
+                        const char *class_suffix;
+
+                        class_suffix = image_class_suffix_to_string(class);
+                        if (class_suffix) {
+                                j = strjoin(image_name, class_suffix, format_suffix, v_suffix);
+                                if (!j)
+                                        return NULL;
+
+                                if (strv_consume(&l, TAKE_PTR(j)) < 0)
+                                        return NULL;
+                        }
+
+                        j = strjoin(image_name, format_suffix, v_suffix);
+                        if (!j)
+                                return NULL;
+
+                        if (strv_consume(&l, TAKE_PTR(j)) < 0)
+                                return NULL;
+                }
+
+        return TAKE_PTR(l);
+}
+
 int image_find(ImageClass class,
                const char *name,
                const char *root,
@@ -503,6 +570,10 @@ int image_find(ImageClass class,
         if (!image_name_is_valid(name))
                 return -ENOENT;
 
+        _cleanup_strv_free_ char **names = make_possible_filenames(class, name);
+        if (!names)
+                return -ENOMEM;
+
         NULSTR_FOREACH(path, pick_image_search_path(class)) {
                 _cleanup_free_ char *resolved = NULL;
                 _cleanup_closedir_ DIR *d = NULL;
@@ -519,43 +590,97 @@ int image_find(ImageClass class,
                  * to symlink block devices into the search path. (For now, we disable that when operating
                  * relative to some root directory.) */
                 flags = root ? AT_SYMLINK_NOFOLLOW : 0;
-                if (fstatat(dirfd(d), name, &st, flags) < 0) {
-                        _cleanup_free_ char *raw = NULL;
 
-                        if (errno != ENOENT)
-                                return -errno;
+                STRV_FOREACH(n, names) {
+                        _cleanup_free_ char *fname_buf = NULL;
+                        const char *fname = *n;
 
-                        raw = strjoin(name, ".raw");
-                        if (!raw)
-                                return -ENOMEM;
+                        if (fstatat(dirfd(d), fname, &st, flags) < 0) {
+                                if (errno != ENOENT)
+                                        return -errno;
 
-                        if (fstatat(dirfd(d), raw, &st, flags) < 0) {
-                                if (errno == ENOENT)
+                                continue; /* Vanished while we were looking at it */
+                        }
+
+                        if (endswith(fname, ".raw")) {
+                                if (!S_ISREG(st.st_mode)) {
+                                        log_debug("Ignoring non-regular file '%s' with .raw suffix.", fname);
                                         continue;
+                                }
 
-                                return -errno;
-                        }
+                        } else if (endswith(fname, ".v")) {
 
-                        if (!S_ISREG(st.st_mode))
-                                continue;
+                                if (!S_ISDIR(st.st_mode)) {
+                                        log_debug("Ignoring non-directory file '%s' with .v suffix.", fname);
+                                        continue;
+                                }
+
+                                _cleanup_free_ char *suffix = NULL;
+                                suffix = strdup(ASSERT_PTR(startswith(fname, name)));
+                                if (!suffix)
+                                        return -ENOMEM;
+
+                                *ASSERT_PTR(endswith(suffix, ".v")) = 0;
+
+                                _cleanup_free_ char *vp = path_join(resolved, fname);
+                                if (!vp)
+                                        return -ENOMEM;
+
+                                PickFilter filter = {
+                                        .type_mask = endswith(suffix, ".raw") ? (UINT32_C(1) << DT_REG) | (UINT32_C(1) << DT_BLK) : (UINT32_C(1) << DT_DIR),
+                                        .basename = name,
+                                        .architecture = _ARCHITECTURE_INVALID,
+                                        .suffix = suffix,
+                                };
+
+                                _cleanup_(pick_result_done) PickResult result = PICK_RESULT_NULL;
+                                r = path_pick(root,
+                                              /* toplevel_fd= */ AT_FDCWD,
+                                              vp,
+                                              &filter,
+                                              PICK_ARCHITECTURE|PICK_TRIES,
+                                              &result);
+                                if (r < 0) {
+                                        log_debug_errno(r, "Failed to pick versioned image on '%s', skipping: %m", vp);
+                                        continue;
+                                }
+                                if (!result.path) {
+                                        log_debug("Found versioned directory '%s', without matching entry, skipping: %m", vp);
+                                        continue;
+                                }
 
-                        r = image_make(class, name, dirfd(d), resolved, raw, &st, ret);
+                                /* Refresh the stat data for the discovered target */
+                                st = result.st;
 
-                } else {
-                        if (!S_ISDIR(st.st_mode) && !S_ISBLK(st.st_mode))
+                                _cleanup_free_ char *bn = NULL;
+                                r = path_extract_filename(result.path, &bn);
+                                if (r < 0) {
+                                        log_debug_errno(r, "Failed to extract basename of image path '%s', skipping: %m", result.path);
+                                        continue;
+                                }
+
+                                fname_buf = path_join(fname, bn);
+                                if (!fname_buf)
+                                        return log_oom();
+
+                                fname = fname_buf;
+
+                        } else if (!S_ISDIR(st.st_mode) && !S_ISBLK(st.st_mode)) {
+                                log_debug("Ignoring non-directory and non-block device file '%s' without suffix.", fname);
                                 continue;
+                        }
 
-                        r = image_make(class, name, dirfd(d), resolved, name, &st, ret);
-                }
-                if (IN_SET(r, -ENOENT, -EMEDIUMTYPE))
-                        continue;
-                if (r < 0)
-                        return r;
+                        r = image_make(class, name, dirfd(d), resolved, fname, &st, ret);
+                        if (IN_SET(r, -ENOENT, -EMEDIUMTYPE))
+                                continue;
+                        if (r < 0)
+                                return r;
 
-                if (ret)
-                        (*ret)->discoverable = true;
+                        if (ret)
+                                (*ret)->discoverable = true;
 
-                return 1;
+                        return 1;
+                }
         }
 
         if (class == IMAGE_MACHINE && streq(name, ".host")) {
@@ -566,7 +691,7 @@ int image_find(ImageClass class,
                 if (ret)
                         (*ret)->discoverable = true;
 
-                return r;
+                return 1;
         }
 
         return -ENOENT;
@@ -613,43 +738,133 @@ int image_discover(
                         return r;
 
                 FOREACH_DIRENT_ALL(de, d, return -errno) {
+                        _cleanup_free_ char *pretty = NULL, *fname_buf = NULL;
                         _cleanup_(image_unrefp) Image *image = NULL;
-                        _cleanup_free_ char *pretty = NULL;
+                        const char *fname = de->d_name;
                         struct stat st;
                         int flags;
 
-                        if (dot_or_dot_dot(de->d_name))
+                        if (dot_or_dot_dot(fname))
                                 continue;
 
                         /* As mentioned above, we follow symlinks on this fstatat(), because we want to
                          * permit people to symlink block devices into the search path. */
                         flags = root ? AT_SYMLINK_NOFOLLOW : 0;
-                        if (fstatat(dirfd(d), de->d_name, &st, flags) < 0) {
+                        if (fstatat(dirfd(d), fname, &st, flags) < 0) {
                                 if (errno == ENOENT)
                                         continue;
 
                                 return -errno;
                         }
 
-                        if (S_ISREG(st.st_mode))
-                                r = extract_pretty(de->d_name, image_class_suffix_to_string(class), ".raw", &pretty);
-                        else if (S_ISDIR(st.st_mode))
-                                r = extract_pretty(de->d_name, image_class_suffix_to_string(class), NULL, &pretty);
-                        else if (S_ISBLK(st.st_mode))
-                                r = extract_pretty(de->d_name, NULL, NULL, &pretty);
-                        else {
-                                log_debug("Skipping directory entry '%s', which is neither regular file, directory nor block device.", de->d_name);
-                                continue;
-                        }
-                        if (r < 0) {
-                                log_debug_errno(r, "Skipping directory entry '%s', which doesn't look like an image.", de->d_name);
+                        if (S_ISREG(st.st_mode)) {
+                                r = extract_image_basename(
+                                                fname,
+                                                image_class_suffix_to_string(class),
+                                                STRV_MAKE(".raw"),
+                                                &pretty,
+                                                /* suffix= */ NULL);
+                                if (r < 0) {
+                                        log_debug_errno(r, "Skipping directory entry '%s', which doesn't look like an image.", fname);
+                                        continue;
+                                }
+                        } else if (S_ISDIR(st.st_mode)) {
+                                const char *v;
+
+                                v = endswith(fname, ".v");
+                                if (v) {
+                                        _cleanup_free_ char *suffix = NULL, *nov = NULL;
+
+                                        nov = strndup(fname, v - fname); /* Chop off the .v */
+                                        if (!nov)
+                                                return -ENOMEM;
+
+                                        r = extract_image_basename(
+                                                        nov,
+                                                        image_class_suffix_to_string(class),
+                                                        STRV_MAKE(".raw", ""),
+                                                        &pretty,
+                                                        &suffix);
+                                        if (r < 0) {
+                                                log_debug_errno(r, "Skipping directory entry '%s', which doesn't look like a versioned image.", fname);
+                                                continue;
+                                        }
+
+                                        _cleanup_free_ char *vp = path_join(resolved, fname);
+                                        if (!vp)
+                                                return -ENOMEM;
+
+                                        PickFilter filter = {
+                                                .type_mask = endswith(suffix, ".raw") ? (UINT32_C(1) << DT_REG) | (UINT32_C(1) << DT_BLK) : (UINT32_C(1) << DT_DIR),
+                                                .basename = pretty,
+                                                .architecture = _ARCHITECTURE_INVALID,
+                                                .suffix = suffix,
+                                        };
+
+                                        _cleanup_(pick_result_done) PickResult result = PICK_RESULT_NULL;
+                                        r = path_pick(root,
+                                                      /* toplevel_fd= */ AT_FDCWD,
+                                                      vp,
+                                                      &filter,
+                                                      PICK_ARCHITECTURE|PICK_TRIES,
+                                                      &result);
+                                        if (r < 0) {
+                                                log_debug_errno(r, "Failed to pick versioned image on '%s', skipping: %m", vp);
+                                                continue;
+                                        }
+                                        if (!result.path) {
+                                                log_debug("Found versioned directory '%s', without matching entry, skipping: %m", vp);
+                                                continue;
+                                        }
+
+                                        /* Refresh the stat data for the discovered target */
+                                        st = result.st;
+
+                                        _cleanup_free_ char *bn = NULL;
+                                        r = path_extract_filename(result.path, &bn);
+                                        if (r < 0) {
+                                                log_debug_errno(r, "Failed to extract basename of image path '%s', skipping: %m", result.path);
+                                                continue;
+                                        }
+
+                                        fname_buf = path_join(fname, bn);
+                                        if (!fname_buf)
+                                                return log_oom();
+
+                                        fname = fname_buf;
+                                } else {
+                                        r = extract_image_basename(
+                                                        fname,
+                                                        image_class_suffix_to_string(class),
+                                                        /* format_suffix= */ NULL,
+                                                        &pretty,
+                                                        /* ret_suffix= */ NULL);
+                                        if (r < 0) {
+                                                log_debug_errno(r, "Skipping directory entry '%s', which doesn't look like an image.", fname);
+                                                continue;
+                                        }
+                                }
+
+                        } else if (S_ISBLK(st.st_mode)) {
+                                r = extract_image_basename(
+                                                fname,
+                                                /* class_suffix= */ NULL,
+                                                /* format_suffix= */ NULL,
+                                                &pretty,
+                                                /* ret_v_suffix= */ NULL);
+                                if (r < 0) {
+                                        log_debug_errno(r, "Skipping directory entry '%s', which doesn't look like an image.", fname);
+                                        continue;
+                                }
+                        } else {
+                                log_debug("Skipping directory entry '%s', which is neither regular file, directory nor block device.", fname);
                                 continue;
                         }
 
                         if (hashmap_contains(h, pretty))
                                 continue;
 
-                        r = image_make(class, pretty, dirfd(d), resolved, de->d_name, &st, &image);
+                        r = image_make(class, pretty, dirfd(d), resolved, fname, &st, &image);
                         if (IN_SET(r, -ENOENT, -EMEDIUMTYPE))
                                 continue;
                         if (r < 0)
@@ -1066,6 +1281,11 @@ int image_read_only(Image *i, bool b) {
         return 0;
 }
 
+static void make_lock_dir(void) {
+        (void) mkdir_p("/run/systemd/nspawn", 0755);
+        (void) mkdir("/run/systemd/nspawn/locks", 0700);
+}
+
 int image_path_lock(const char *path, int operation, LockFile *global, LockFile *local) {
         _cleanup_free_ char *p = NULL;
         LockFile t = LOCK_FILE_INIT;
@@ -1141,7 +1361,7 @@ int image_path_lock(const char *path, int operation, LockFile *global, LockFile
         }
 
         if (p) {
-                (void) mkdir_p("/run/systemd/nspawn/locks", 0700);
+                make_lock_dir();
 
                 r = make_lock_file(p, operation, global);
                 if (r < 0) {
@@ -1316,7 +1536,7 @@ int image_name_lock(const char *name, int operation, LockFile *ret) {
                 return 0;
         }
 
-        (void) mkdir_p("/run/systemd/nspawn/locks", 0700);
+        make_lock_dir();
 
         p = strjoina("/run/systemd/nspawn/locks/name-", name);
         return make_lock_file(p, operation, ret);
@@ -1354,7 +1574,6 @@ bool image_in_search_path(
                 /* Accept trailing slashes */
                 if (p[strspn(p, "/")] == 0)
                         return true;
-
         }
 
         return false;
index 6b0e5fe24847b701eba7dbdd001a011d9f065c71..e5e47e4ac6d7480e74f27327eaf7753e580d077a 100644 (file)
@@ -3166,7 +3166,7 @@ int verity_settings_load(
                 }
 
                 if (text) {
-                        r = unhexmem(text, strlen(text), &root_hash, &root_hash_size);
+                        r = unhexmem(text, &root_hash, &root_hash_size);
                         if (r < 0)
                                 return r;
                         if (root_hash_size < sizeof(sd_id128_t))
@@ -3320,7 +3320,7 @@ int dissected_image_load_verity_sig_partition(
         if (!json_variant_is_string(rh))
                 return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "'rootHash' field of signature JSON object is not a string.");
 
-        r = unhexmem(json_variant_string(rh), SIZE_MAX, &root_hash, &root_hash_size);
+        r = unhexmem(json_variant_string(rh), &root_hash, &root_hash_size);
         if (r < 0)
                 return log_debug_errno(r, "Failed to parse root hash field: %m");
 
@@ -3341,7 +3341,7 @@ int dissected_image_load_verity_sig_partition(
         if (!json_variant_is_string(sig))
                 return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "'signature' field of signature JSON object is not a string.");
 
-        r = unbase64mem(json_variant_string(sig), SIZE_MAX, &root_hash_sig, &root_hash_sig_size);
+        r = unbase64mem(json_variant_string(sig), &root_hash_sig, &root_hash_sig_size);
         if (r < 0)
                 return log_debug_errno(r, "Failed to parse signature field: %m");
 
index db87084a4da83861a897e99777c4ea8f4e89d1b4..f830d6dfe3258594860d325271aa519ad864dfcd 100644 (file)
@@ -556,13 +556,16 @@ int find_esp_and_warn(
         if (rfd < 0)
                 return -errno;
 
-        r = find_esp_and_warn_at(rfd, path, unprivileged_mode,
-                                 ret_path ? &p : NULL,
-                                 ret_part ? &part : NULL,
-                                 ret_pstart ? &pstart : NULL,
-                                 ret_psize ? &psize : NULL,
-                                 ret_uuid ? &uuid : NULL,
-                                 ret_devid ? &devid : NULL);
+        r = find_esp_and_warn_at(
+                        rfd,
+                        path,
+                        unprivileged_mode,
+                        ret_path ? &p : NULL,
+                        ret_part ? &part : NULL,
+                        ret_pstart ? &pstart : NULL,
+                        ret_psize ? &psize : NULL,
+                        ret_uuid ? &uuid : NULL,
+                        ret_devid ? &devid : NULL);
         if (r < 0)
                 return r;
 
@@ -871,12 +874,12 @@ int find_xbootldr_and_warn_at(
 }
 
 int find_xbootldr_and_warn(
-        const char *root,
-        const char *path,
-        int unprivileged_mode,
-        char **ret_path,
-        sd_id128_t *ret_uuid,
-        dev_t *ret_devid) {
+                const char *root,
+                const char *path,
+                int unprivileged_mode,
+                char **ret_path,
+                sd_id128_t *ret_uuid,
+                dev_t *ret_devid) {
 
         _cleanup_close_ int rfd = -EBADF;
         _cleanup_free_ char *p = NULL;
@@ -888,10 +891,13 @@ int find_xbootldr_and_warn(
         if (rfd < 0)
                 return -errno;
 
-        r = find_xbootldr_and_warn_at(rfd, path, unprivileged_mode,
-                                      ret_path ? &p : NULL,
-                                      ret_uuid ? &uuid : NULL,
-                                      ret_devid ? &devid : NULL);
+        r = find_xbootldr_and_warn_at(
+                        rfd,
+                        path,
+                        unprivileged_mode,
+                        ret_path ? &p : NULL,
+                        ret_uuid ? &uuid : NULL,
+                        ret_devid ? &devid : NULL);
         if (r < 0)
                 return r;
 
index 1e33bdfed58fb1d19c3bfa50f01f55bd134138e6..7eebd2300e4f5c0e0e84a54e00bbf02fee6763f9 100644 (file)
@@ -2,7 +2,7 @@
 
 #include "group-record.h"
 #include "strv.h"
-#include "uid-alloc-range.h"
+#include "uid-classification.h"
 #include "user-util.h"
 
 GroupRecord* group_record_new(void) {
@@ -230,7 +230,7 @@ int group_record_load(
         if (r < 0)
                 return r;
 
-        r = json_dispatch(h->json, group_dispatch_table, json_flags, h);
+        r = json_dispatch(h->json, group_dispatch_table, json_flags | JSON_ALLOW_EXTENSIONS, h);
         if (r < 0)
                 return r;
 
index af2fc9aa70c10dc2f0d72e2d50488f47cc454ed7..073bf31acde8c86db5c0c7e5be67fbac446a3985 100644 (file)
@@ -1771,6 +1771,25 @@ static int json_format(FILE *f, JsonVariant *v, JsonFormatFlags flags, const cha
         return 0;
 }
 
+static bool json_variant_is_sensitive_recursive(JsonVariant *v) {
+        if (!v)
+                return false;
+        if (json_variant_is_sensitive(v))
+                return true;
+        if (!json_variant_is_regular(v))
+                return false;
+        if (!IN_SET(v->type, JSON_VARIANT_ARRAY, JSON_VARIANT_OBJECT))
+                return false;
+        if (v->is_reference)
+                return json_variant_is_sensitive_recursive(v->reference);
+
+        for (size_t i = 0; i < json_variant_elements(v); i++)
+                if (json_variant_is_sensitive_recursive(json_variant_by_index(v, i)))
+                        return true;
+
+        return false;
+}
+
 int json_variant_format(JsonVariant *v, JsonFormatFlags flags, char **ret) {
         _cleanup_(memstream_done) MemStream m = {};
         size_t sz;
@@ -1786,6 +1805,10 @@ int json_variant_format(JsonVariant *v, JsonFormatFlags flags, char **ret) {
         if (flags & JSON_FORMAT_OFF)
                 return -ENOEXEC;
 
+        if ((flags & JSON_FORMAT_REFUSE_SENSITIVE))
+                if (json_variant_is_sensitive_recursive(v))
+                        return -EPERM;
+
         f = memstream_init(&m);
         if (!f)
                 return -ENOMEM;
@@ -3790,7 +3813,8 @@ int json_buildv(JsonVariant **ret, va_list ap) {
                         break;
                 }
 
-                case _JSON_BUILD_IOVEC_BASE64: {
+                case _JSON_BUILD_IOVEC_BASE64:
+                case _JSON_BUILD_IOVEC_HEX: {
                         const struct iovec *iov;
 
                         if (!IN_SET(current->expect, EXPECT_TOPLEVEL, EXPECT_OBJECT_VALUE, EXPECT_ARRAY_ELEMENT)) {
@@ -3798,10 +3822,14 @@ int json_buildv(JsonVariant **ret, va_list ap) {
                                 goto finish;
                         }
 
-                        iov = ASSERT_PTR(va_arg(ap, const struct iovec*));
+                        iov = va_arg(ap, const struct iovec*);
 
                         if (current->n_suppress == 0) {
-                                r = json_variant_new_base64(&add, iov->iov_base, iov->iov_len);
+                                if (iov)
+                                        r = command == _JSON_BUILD_IOVEC_BASE64 ? json_variant_new_base64(&add, iov->iov_base, iov->iov_len) :
+                                                                                  json_variant_new_hex(&add, iov->iov_base, iov->iov_len);
+                                else
+                                        r = json_variant_new_string(&add, "");
                                 if (r < 0)
                                         goto finish;
                         }
@@ -4592,8 +4620,12 @@ int json_dispatch_full(
                                         done++;
 
                         } else  {
-                                json_log(value, flags, 0, "Unexpected object field '%s'.", json_variant_string(key));
+                                if (flags & JSON_ALLOW_EXTENSIONS) {
+                                        json_log(value, flags, 0, "Unrecognized object field '%s', assuming extension.", json_variant_string(key));
+                                        continue;
+                                }
 
+                                json_log(value, flags, 0, "Unexpected object field '%s'.", json_variant_string(key));
                                 if (flags & JSON_PERMISSIVE)
                                         continue;
 
@@ -5107,14 +5139,14 @@ int json_variant_unbase64(JsonVariant *v, void **ret, size_t *ret_size) {
         if (!json_variant_is_string(v))
                 return -EINVAL;
 
-        return unbase64mem(json_variant_string(v), SIZE_MAX, ret, ret_size);
+        return unbase64mem(json_variant_string(v), ret, ret_size);
 }
 
 int json_variant_unhex(JsonVariant *v, void **ret, size_t *ret_size) {
         if (!json_variant_is_string(v))
                 return -EINVAL;
 
-        return unhexmem(json_variant_string(v), SIZE_MAX, ret, ret_size);
+        return unhexmem(json_variant_string(v), ret, ret_size);
 }
 
 static const char* const json_variant_type_table[_JSON_VARIANT_TYPE_MAX] = {
index c40c23487ab97fd5899e284d42e35835cc04585a..a40e9496c2d06ba3dfeb4016dc6002645065a9a7 100644 (file)
@@ -185,17 +185,18 @@ struct json_variant_foreach_state {
 int json_variant_get_source(JsonVariant *v, const char **ret_source, unsigned *ret_line, unsigned *ret_column);
 
 typedef enum JsonFormatFlags {
-        JSON_FORMAT_NEWLINE     = 1 << 0, /* suffix with newline */
-        JSON_FORMAT_PRETTY      = 1 << 1, /* add internal whitespace to appeal to human readers */
-        JSON_FORMAT_PRETTY_AUTO = 1 << 2, /* same, but only if connected to a tty (and JSON_FORMAT_NEWLINE otherwise) */
-        JSON_FORMAT_COLOR       = 1 << 3, /* insert ANSI color sequences */
-        JSON_FORMAT_COLOR_AUTO  = 1 << 4, /* insert ANSI color sequences if colors_enabled() says so */
-        JSON_FORMAT_SOURCE      = 1 << 5, /* prefix with source filename/line/column */
-        JSON_FORMAT_SSE         = 1 << 6, /* prefix/suffix with W3C server-sent events */
-        JSON_FORMAT_SEQ         = 1 << 7, /* prefix/suffix with RFC 7464 application/json-seq */
-        JSON_FORMAT_FLUSH       = 1 << 8, /* call fflush() after dumping JSON */
-        JSON_FORMAT_EMPTY_ARRAY = 1 << 9, /* output "[]" for empty input */
-        JSON_FORMAT_OFF         = 1 << 10, /* make json_variant_format() fail with -ENOEXEC */
+        JSON_FORMAT_NEWLINE          = 1 << 0, /* suffix with newline */
+        JSON_FORMAT_PRETTY           = 1 << 1, /* add internal whitespace to appeal to human readers */
+        JSON_FORMAT_PRETTY_AUTO      = 1 << 2, /* same, but only if connected to a tty (and JSON_FORMAT_NEWLINE otherwise) */
+        JSON_FORMAT_COLOR            = 1 << 3, /* insert ANSI color sequences */
+        JSON_FORMAT_COLOR_AUTO       = 1 << 4, /* insert ANSI color sequences if colors_enabled() says so */
+        JSON_FORMAT_SOURCE           = 1 << 5, /* prefix with source filename/line/column */
+        JSON_FORMAT_SSE              = 1 << 6, /* prefix/suffix with W3C server-sent events */
+        JSON_FORMAT_SEQ              = 1 << 7, /* prefix/suffix with RFC 7464 application/json-seq */
+        JSON_FORMAT_FLUSH            = 1 << 8, /* call fflush() after dumping JSON */
+        JSON_FORMAT_EMPTY_ARRAY      = 1 << 9, /* output "[]" for empty input */
+        JSON_FORMAT_OFF              = 1 << 10, /* make json_variant_format() fail with -ENOEXEC */
+        JSON_FORMAT_REFUSE_SENSITIVE = 1 << 11, /* return EPERM if any node in the tree is marked as senstitive */
 } JsonFormatFlags;
 
 int json_variant_format(JsonVariant *v, JsonFormatFlags flags, char **ret);
@@ -271,6 +272,7 @@ enum {
         _JSON_BUILD_IOVEC_BASE64,
         _JSON_BUILD_BASE32HEX,
         _JSON_BUILD_HEX,
+        _JSON_BUILD_IOVEC_HEX,
         _JSON_BUILD_OCTESCAPE,
         _JSON_BUILD_ID128,
         _JSON_BUILD_UUID,
@@ -315,6 +317,7 @@ typedef int (*JsonBuildCallback)(JsonVariant **ret, const char *name, void *user
 #define JSON_BUILD_IOVEC_BASE64(iov) _JSON_BUILD_IOVEC_BASE64, (const struct iovec*) { iov }
 #define JSON_BUILD_BASE32HEX(p, n) _JSON_BUILD_BASE32HEX, (const void*) { p }, (size_t) { n }
 #define JSON_BUILD_HEX(p, n) _JSON_BUILD_HEX, (const void*) { p }, (size_t) { n }
+#define JSON_BUILD_IOVEC_HEX(iov) _JSON_BUILD_IOVEC_HEX, (const struct iovec*) { iov }
 #define JSON_BUILD_OCTESCAPE(p, n) _JSON_BUILD_OCTESCAPE, (const void*) { p }, (size_t) { n }
 #define JSON_BUILD_ID128(id) _JSON_BUILD_ID128, (const sd_id128_t*) { &(id) }
 #define JSON_BUILD_UUID(id) _JSON_BUILD_UUID, (const sd_id128_t*) { &(id) }
@@ -345,6 +348,7 @@ typedef int (*JsonBuildCallback)(JsonVariant **ret, const char *name, void *user
 #define JSON_BUILD_PAIR_BASE64(name, p, n) JSON_BUILD_PAIR(name, JSON_BUILD_BASE64(p, n))
 #define JSON_BUILD_PAIR_IOVEC_BASE64(name, iov) JSON_BUILD_PAIR(name, JSON_BUILD_IOVEC_BASE64(iov))
 #define JSON_BUILD_PAIR_HEX(name, p, n) JSON_BUILD_PAIR(name, JSON_BUILD_HEX(p, n))
+#define JSON_BUILD_PAIR_IOVEC_HEX(name, iov) JSON_BUILD_PAIR(name, JSON_BUILD_IOVEC_HEX(iov))
 #define JSON_BUILD_PAIR_ID128(name, id) JSON_BUILD_PAIR(name, JSON_BUILD_ID128(id))
 #define JSON_BUILD_PAIR_UUID(name, id) JSON_BUILD_PAIR(name, JSON_BUILD_UUID(id))
 #define JSON_BUILD_PAIR_BYTE_ARRAY(name, v, n) JSON_BUILD_PAIR(name, JSON_BUILD_BYTE_ARRAY(v, n))
@@ -375,15 +379,16 @@ int json_buildv(JsonVariant **ret, va_list ap);
  * entry, as well the bitmask specified for json_log() calls */
 typedef enum JsonDispatchFlags {
         /* The following three may be set in JsonDispatch's .flags field or the json_dispatch() flags parameter  */
-        JSON_PERMISSIVE = 1 << 0, /* Shall parsing errors be considered fatal for this property? */
-        JSON_MANDATORY  = 1 << 1, /* Should existence of this property be mandatory? */
-        JSON_LOG        = 1 << 2, /* Should the parser log about errors? */
-        JSON_SAFE       = 1 << 3, /* Don't accept "unsafe" strings in json_dispatch_string() + json_dispatch_string() */
-        JSON_RELAX      = 1 << 4, /* Use relaxed user name checking in json_dispatch_user_group_name */
+        JSON_PERMISSIVE       = 1 << 0, /* Shall parsing errors be considered fatal for this field or object? */
+        JSON_MANDATORY        = 1 << 1, /* Should existence of this property be mandatory? */
+        JSON_LOG              = 1 << 2, /* Should the parser log about errors? */
+        JSON_SAFE             = 1 << 3, /* Don't accept "unsafe" strings in json_dispatch_string() + json_dispatch_string() */
+        JSON_RELAX            = 1 << 4, /* Use relaxed user name checking in json_dispatch_user_group_name */
+        JSON_ALLOW_EXTENSIONS = 1 << 5, /* Subset of JSON_PERMISSIVE: allow additional fields, but no other permissive handling */
 
         /* The following two may be passed into log_json() in addition to those above */
-        JSON_DEBUG      = 1 << 5, /* Indicates that this log message is a debug message */
-        JSON_WARNING    = 1 << 6, /* Indicates that this log message is a warning message */
+        JSON_DEBUG            = 1 << 6, /* Indicates that this log message is a debug message */
+        JSON_WARNING          = 1 << 7, /* Indicates that this log message is a warning message */
 } JsonDispatchFlags;
 
 typedef int (*JsonDispatchCallback)(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata);
@@ -427,6 +432,28 @@ assert_cc(sizeof(uint32_t) == sizeof(unsigned));
 assert_cc(sizeof(int32_t) == sizeof(int));
 #define json_dispatch_int json_dispatch_int32
 
+#define JSON_DISPATCH_ENUM_DEFINE(name, type, func)                     \
+        int name(const char *n, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) { \
+                type *c = ASSERT_PTR(userdata);                         \
+                                                                        \
+                assert(variant);                                        \
+                                                                        \
+                if (json_variant_is_null(variant)) {                    \
+                        *c = (type) -EINVAL;                            \
+                        return 0;                                       \
+                }                                                       \
+                                                                        \
+                if (!json_variant_is_string(variant))                   \
+                        return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a string.", strna(n)); \
+                                                                        \
+                type cc = func(json_variant_string(variant));           \
+                if (cc < 0)                                             \
+                        return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "Value of JSON field '%s' not recognized.", strna(n)); \
+                                                                        \
+                *c = cc;                                                \
+                return 0;                                               \
+        }
+
 static inline int json_dispatch_level(JsonDispatchFlags flags) {
 
         /* Did the user request no logging? If so, then never log higher than LOG_DEBUG. Also, if this is marked as
@@ -470,5 +497,13 @@ int json_log_internal(JsonVariant *variant, int level, int error, const char *fi
 int json_variant_unbase64(JsonVariant *v, void **ret, size_t *ret_size);
 int json_variant_unhex(JsonVariant *v, void **ret, size_t *ret_size);
 
+static inline int json_variant_unbase64_iovec(JsonVariant *v, struct iovec *ret) {
+        return json_variant_unbase64(v, ret ? &ret->iov_base : NULL, ret ? &ret->iov_len : NULL);
+}
+
+static inline int json_variant_unhex_iovec(JsonVariant *v, struct iovec *ret) {
+        return json_variant_unhex(v, ret ? &ret->iov_base : NULL, ret ? &ret->iov_len : NULL);
+}
+
 const char *json_variant_type_to_string(JsonVariantType t);
 JsonVariantType json_variant_type_from_string(const char *s);
index a5d04003bdfe80bea62414ab76e2f66438d6b937..858b707f6c8c99da83c90624fb4be0ff1179a345 100644 (file)
@@ -523,6 +523,9 @@ static int output_short(
                 return 0;
         }
 
+        if (identifier && set_contains(j->exclude_syslog_identifiers, identifier))
+                return 0;
+
         if (!(flags & OUTPUT_SHOW_ALL))
                 strip_tab_ansi(&message, &message_len, highlight_shifted);
 
index b2cee6fa2c869ac6d9f949e79fe09b5887e7fdd0..69a60b0f4569fb15c26fa84d7422ef2ddd9fc0fa 100644 (file)
@@ -174,6 +174,7 @@ shared_sources = files(
         'varlink-idl.c',
         'varlink-io.systemd.c',
         'varlink-io.systemd.Credentials.c',
+        'varlink-io.systemd.Hostname.c',
         'varlink-io.systemd.Journal.c',
         'varlink-io.systemd.ManagedOOM.c',
         'varlink-io.systemd.Network.c',
@@ -189,6 +190,7 @@ shared_sources = files(
         'verbs.c',
         'vlan-util.c',
         'volatile-util.c',
+        'vpick.c',
         'wall.c',
         'watchdog.c',
         'web-util.c',
index ba3a9e995d13102c31673bd15033861881432f60..3305b6360e7a427c7468fbb846113172fda4741c 100644 (file)
@@ -821,8 +821,8 @@ int mount_option_mangle(
 
                         if (!(ent->mask & MNT_INVERT))
                                 mount_flags |= ent->id;
-                        else if (mount_flags & ent->id)
-                                mount_flags ^= ent->id;
+                        else
+                                mount_flags &= ~ent->id;
 
                         break;
                 }
index 1057104194eabd2322f5e5dc56c3e38b7321ee5e..13b68234c9bf5b838b66d5a5d3758b2395a7968d 100644 (file)
@@ -202,3 +202,69 @@ void pam_cleanup_free(pam_handle_t *handle, void *data, int error_status) {
         /* A generic destructor for pam_set_data() that just frees the specified data */
         free(data);
 }
+
+int pam_get_item_many_internal(pam_handle_t *handle, ...) {
+        va_list ap;
+        int r;
+
+        va_start(ap, handle);
+        for (;;) {
+                int item_type = va_arg(ap, int);
+
+                if (item_type <= 0) {
+                        r = PAM_SUCCESS;
+                        break;
+                }
+
+                const void **value = ASSERT_PTR(va_arg(ap, const void **));
+
+                r = pam_get_item(handle, item_type, value);
+                if (!IN_SET(r, PAM_BAD_ITEM, PAM_SUCCESS))
+                        break;
+        }
+        va_end(ap);
+
+        return r;
+}
+
+int pam_prompt_graceful(pam_handle_t *handle, int style, char **ret_response, const char *fmt, ...) {
+        va_list args;
+        int r;
+
+        assert(handle);
+        assert(fmt);
+
+        /* This is just like pam_prompt(), but does not noisily (i.e. beyond LOG_DEBUG) log on its own, but leaves that to the caller */
+
+        _cleanup_free_ char *msg = NULL;
+        va_start(args, fmt);
+        r = vasprintf(&msg, fmt, args);
+        va_end(args);
+        if (r < 0)
+                return PAM_BUF_ERR;
+
+        const struct pam_conv *conv = NULL;
+        r = pam_get_item(handle, PAM_CONV, (const void**) &conv);
+        if (!IN_SET(r, PAM_SUCCESS, PAM_BAD_ITEM))
+                return pam_syslog_pam_error(handle, LOG_DEBUG, r, "Failed to get conversation function structure: @PAMERR@");
+        if (!conv || !conv->conv) {
+                pam_syslog(handle, LOG_DEBUG, "No conversation function.");
+                return PAM_SYSTEM_ERR;
+        }
+
+        struct pam_message message = {
+                .msg_style = style,
+                .msg = msg,
+        };
+        const struct pam_message *pmessage = &message;
+        _cleanup_free_ struct pam_response *response = NULL;
+        r = conv->conv(1, &pmessage, &response, conv->appdata_ptr);
+        _cleanup_(erase_and_freep) char *rr = response ? response->resp : NULL; /* make sure string is freed + erased */
+        if (r != PAM_SUCCESS)
+                return pam_syslog_pam_error(handle, LOG_DEBUG, r, "Conversation function failed: @PAMERR@");
+
+        if (ret_response)
+                *ret_response = TAKE_PTR(rr);
+
+        return PAM_SUCCESS;
+}
index 5a05fb71f11c4750ce72db5e5197e81323166e27..3439d4246e1636edb6064ac281e0c03869e8f43e 100644 (file)
@@ -39,3 +39,9 @@ int pam_acquire_bus_connection(pam_handle_t *handle, const char *module_name, sd
 int pam_release_bus_connection(pam_handle_t *handle, const char *module_name);
 
 void pam_cleanup_free(pam_handle_t *handle, void *data, int error_status);
+
+int pam_get_item_many_internal(pam_handle_t *handle, ...);
+
+#define pam_get_item_many(handle, ...) pam_get_item_many_internal(handle, __VA_ARGS__, -1)
+
+int pam_prompt_graceful(pam_handle_t *handle, int style, char **ret_response, const char *fmt, ...) _printf_(4,5);
index 9664b9c773bcbc23156fcffe3718fd5ced4aaa24..bad3af8ebf65df9cda10aa5daf8bafbc59a990fd 100644 (file)
@@ -102,6 +102,8 @@ static int parse_ip_ports_token(
                 uint16_t *nr_ports,
                 uint16_t *port_min) {
 
+        int r;
+
         assert(token);
         assert(nr_ports);
         assert(port_min);
@@ -110,7 +112,7 @@ static int parse_ip_ports_token(
                 *nr_ports = *port_min = 0;
         else {
                 uint16_t mn = 0, mx = 0;
-                int r = parse_ip_port_range(token, &mn, &mx);
+                r = parse_ip_port_range(token, &mn, &mx, /* allow_zero = */ true);
                 if (r < 0)
                         return r;
 
@@ -194,6 +196,7 @@ int parse_socket_bind_item(
         *ip_protocol = proto;
         *nr_ports = nr;
         *port_min = mn;
+
         return 0;
 }
 
index 9792f70d42202624eca85fc1eee1abe0afa339f0..2f583f232e42ad343b45939cebbdc21829d97b43 100644 (file)
@@ -50,6 +50,8 @@ const char *(*sym_p11_kit_strerror)(CK_RV rv);
 int (*sym_p11_kit_uri_format)(P11KitUri *uri, P11KitUriType uri_type, char **string);
 void (*sym_p11_kit_uri_free)(P11KitUri *uri);
 CK_ATTRIBUTE_PTR (*sym_p11_kit_uri_get_attributes)(P11KitUri *uri, CK_ULONG *n_attrs);
+CK_ATTRIBUTE_PTR (*sym_p11_kit_uri_get_attribute)(P11KitUri *uri, CK_ATTRIBUTE_TYPE attr_type);
+int (*sym_p11_kit_uri_set_attribute)(P11KitUri *uri, CK_ATTRIBUTE_PTR attr);
 CK_INFO_PTR (*sym_p11_kit_uri_get_module_info)(P11KitUri *uri);
 CK_SLOT_INFO_PTR (*sym_p11_kit_uri_get_slot_info)(P11KitUri *uri);
 CK_TOKEN_INFO_PTR (*sym_p11_kit_uri_get_token_info)(P11KitUri *uri);
@@ -69,6 +71,8 @@ int dlopen_p11kit(void) {
                         DLSYM_ARG(p11_kit_uri_format),
                         DLSYM_ARG(p11_kit_uri_free),
                         DLSYM_ARG(p11_kit_uri_get_attributes),
+                        DLSYM_ARG(p11_kit_uri_get_attribute),
+                        DLSYM_ARG(p11_kit_uri_set_attribute),
                         DLSYM_ARG(p11_kit_uri_get_module_info),
                         DLSYM_ARG(p11_kit_uri_get_slot_info),
                         DLSYM_ARG(p11_kit_uri_get_token_info),
@@ -948,7 +952,7 @@ static int pkcs11_token_decrypt_data_ecc(
                 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;
+                        size_t compressed_point_size = 0; /* Explicit initialization to appease gcc */
                         r = ecc_convert_to_compressed(m, session, object, encrypted_data, encrypted_data_size, &compressed_point, &compressed_point_size);
                         if (r < 0)
                                 return r;
index 2ff6997823e1b4a486fbac75ab5eebcb3bc885f9..d901bbea91fce2acc271e2f1f67de0b7e135d106 100644 (file)
@@ -26,6 +26,8 @@ extern const char *(*sym_p11_kit_strerror)(CK_RV rv);
 extern int (*sym_p11_kit_uri_format)(P11KitUri *uri, P11KitUriType uri_type, char **string);
 extern void (*sym_p11_kit_uri_free)(P11KitUri *uri);
 extern CK_ATTRIBUTE_PTR (*sym_p11_kit_uri_get_attributes)(P11KitUri *uri, CK_ULONG *n_attrs);
+extern CK_ATTRIBUTE_PTR (*sym_p11_kit_uri_get_attribute)(P11KitUri *uri, CK_ATTRIBUTE_TYPE attr_type);
+extern int (*sym_p11_kit_uri_set_attribute)(P11KitUri *uri, CK_ATTRIBUTE_PTR attr);
 extern CK_INFO_PTR (*sym_p11_kit_uri_get_module_info)(P11KitUri *uri);
 extern CK_SLOT_INFO_PTR (*sym_p11_kit_uri_get_slot_info)(P11KitUri *uri);
 extern CK_TOKEN_INFO_PTR (*sym_p11_kit_uri_get_token_info)(P11KitUri *uri);
index d5dfb78678ac1469f5c36c500518b7427b55db71..9c7d24c0d6de107d0205006c840c16db4aa9fe9b 100644 (file)
@@ -79,7 +79,7 @@ bool can_memlock(void);
 #define DEFINE_HEX_PTR(name, hex)                                       \
         _cleanup_free_ void *name = NULL;                               \
         size_t name##_len = 0;                                          \
-        assert_se(unhexmem(hex, strlen_ptr(hex), &name, &name##_len) >= 0);
+        assert_se(unhexmem_full(hex, strlen_ptr(hex), false, &name, &name##_len) >= 0);
 
 #define TEST_REQ_RUNNING_SYSTEMD(x)                                 \
         if (sd_booted() > 0) {                                      \
index 8c683254a2a7098cfacc99aab6952425cea8f2c2..22b8a872a4811387f8025b5ce6cfe00fc964dc99 100644 (file)
@@ -1875,7 +1875,7 @@ int tpm2_pcr_value_from_string(const char *arg, Tpm2PCRValue *ret_pcr_value) {
 
                         _cleanup_free_ void *buf = NULL;
                         size_t buf_size = 0;
-                        r = unhexmem(p, SIZE_MAX, &buf, &buf_size);
+                        r = unhexmem(p, &buf, &buf_size);
                         if (r < 0)
                                 return log_debug_errno(r, "Invalid pcr hash value '%s': %m", p);
 
@@ -5007,7 +5007,7 @@ static int tpm2_calculate_seal_ecc_seed(
         size_t bits = (size_t) r * 8;
 
         _cleanup_free_ void *seed = NULL;
-        size_t seed_size;
+        size_t seed_size = 0; /* Explicit initialization to appease gcc */
         r = tpm2_kdfe(parent->publicArea.nameAlg,
                       shared_secret,
                       shared_secret_size,
@@ -5044,7 +5044,7 @@ static int tpm2_calculate_seal_seed(
         log_debug("Calculating encrypted seed for sealed object.");
 
         _cleanup_free_ void *seed = NULL, *encrypted_seed = NULL;
-        size_t seed_size, encrypted_seed_size;
+        size_t seed_size = 0, encrypted_seed_size = 0; /* Explicit initialization to appease gcc */
         if (parent->publicArea.type == TPM2_ALG_RSA)
                 r = tpm2_calculate_seal_rsa_seed(parent, &seed, &seed_size, &encrypted_seed, &encrypted_seed_size);
         else if (parent->publicArea.type == TPM2_ALG_ECC)
@@ -5067,28 +5067,22 @@ int tpm2_calculate_seal(
                 TPM2_HANDLE parent_handle,
                 const TPM2B_PUBLIC *parent_public,
                 const TPMA_OBJECT *attributes,
-                const void *secret,
-                size_t secret_size,
+                const struct iovec *secret,
                 const TPM2B_DIGEST *policy,
                 const char *pin,
-                void **ret_secret,
-                size_t *ret_secret_size,
-                void **ret_blob,
-                size_t *ret_blob_size,
-                void **ret_serialized_parent,
-                size_t *ret_serialized_parent_size) {
+                struct iovec *ret_secret,
+                struct iovec *ret_blob,
+                struct iovec *ret_serialized_parent) {
 
 #if HAVE_OPENSSL
         int r;
 
         assert(parent_public);
-        assert(secret || secret_size == 0);
+        assert(iovec_is_valid(secret));
         assert(secret || ret_secret);
         assert(!(secret && ret_secret)); /* Either provide a secret, or we create one, but not both */
         assert(ret_blob);
-        assert(ret_blob_size);
         assert(ret_serialized_parent);
-        assert(ret_serialized_parent_size);
 
         log_debug("Calculating sealed object.");
 
@@ -5109,27 +5103,27 @@ int tpm2_calculate_seal(
                                        parent_handle);
         }
 
-        _cleanup_(erase_and_freep) void *generated_secret = NULL;
+        _cleanup_(iovec_done_erase) struct iovec generated_secret = {};
         if (!secret) {
                 /* No secret provided, generate a random secret. We use SHA256 digest length, though it can
                  * be up to TPM2_MAX_SEALED_DATA. The secret length is not limited to the nameAlg hash
                  * size. */
-                secret_size = TPM2_SHA256_DIGEST_SIZE;
-                generated_secret = malloc(secret_size);
-                if (!generated_secret)
+                generated_secret.iov_len = TPM2_SHA256_DIGEST_SIZE;
+                generated_secret.iov_base = malloc(generated_secret.iov_len);
+                if (!generated_secret.iov_base)
                         return log_oom_debug();
 
-                r = crypto_random_bytes(generated_secret, secret_size);
+                r = crypto_random_bytes(generated_secret.iov_base, generated_secret.iov_len);
                 if (r < 0)
                         return log_debug_errno(r, "Failed to generate secret key: %m");
 
-                secret = generated_secret;
+                secret = &generated_secret;
         }
 
-        if (secret_size > TPM2_MAX_SEALED_DATA)
+        if (secret->iov_len > TPM2_MAX_SEALED_DATA)
                 return log_debug_errno(SYNTHETIC_ERRNO(EOVERFLOW),
                                        "Secret size %zu too large, limit is %d bytes.",
-                                       secret_size, TPM2_MAX_SEALED_DATA);
+                                       secret->iov_len, TPM2_MAX_SEALED_DATA);
 
         TPM2B_DIGEST random_seed;
         TPM2B_ENCRYPTED_SECRET seed;
@@ -5138,7 +5132,7 @@ int tpm2_calculate_seal(
                 return r;
 
         TPM2B_PUBLIC public;
-        r = tpm2_calculate_seal_public(parent_public, attributes, policy, &random_seed, secret, secret_size, &public);
+        r = tpm2_calculate_seal_public(parent_public, attributes, policy, &random_seed, secret->iov_base, secret->iov_len, &public);
         if (r < 0)
                 return r;
 
@@ -5148,13 +5142,12 @@ int tpm2_calculate_seal(
                 return r;
 
         TPM2B_PRIVATE private;
-        r = tpm2_calculate_seal_private(parent_public, &name, pin, &random_seed, secret, secret_size, &private);
+        r = tpm2_calculate_seal_private(parent_public, &name, pin, &random_seed, secret->iov_base, secret->iov_len, &private);
         if (r < 0)
                 return r;
 
-        _cleanup_free_ void *blob = NULL;
-        size_t blob_size;
-        r = tpm2_marshal_blob(&public, &private, &seed, &blob, &blob_size);
+        _cleanup_(iovec_done) struct iovec blob = {};
+        r = tpm2_marshal_blob(&public, &private, &seed, &blob.iov_base, &blob.iov_len);
         if (r < 0)
                 return log_debug_errno(r, "Could not create sealed blob: %m");
 
@@ -5163,25 +5156,20 @@ int tpm2_calculate_seal(
         if (r < 0)
                 return r;
 
-        _cleanup_free_ void *serialized_parent = NULL;
-        size_t serialized_parent_size;
+        _cleanup_(iovec_done) struct iovec serialized_parent = {};
         r = tpm2_calculate_serialize(
                         parent_handle,
                         &parent_name,
                         parent_public,
-                        &serialized_parent,
-                        &serialized_parent_size);
+                        &serialized_parent.iov_base,
+                        &serialized_parent.iov_len);
         if (r < 0)
                 return r;
 
         if (ret_secret)
-                *ret_secret = TAKE_PTR(generated_secret);
-        if (ret_secret_size)
-                *ret_secret_size = secret_size;
-        *ret_blob = TAKE_PTR(blob);
-        *ret_blob_size = blob_size;
-        *ret_serialized_parent = TAKE_PTR(serialized_parent);
-        *ret_serialized_parent_size = serialized_parent_size;
+                *ret_secret = TAKE_STRUCT(generated_secret);
+        *ret_blob = TAKE_STRUCT(blob);
+        *ret_serialized_parent = TAKE_STRUCT(serialized_parent);
 
         return 0;
 #else /* HAVE_OPENSSL */
@@ -5193,21 +5181,16 @@ int tpm2_seal(Tpm2Context *c,
               uint32_t seal_key_handle,
               const TPM2B_DIGEST *policy,
               const char *pin,
-              void **ret_secret,
-              size_t *ret_secret_size,
-              void **ret_blob,
-              size_t *ret_blob_size,
+              struct iovec *ret_secret,
+              struct iovec *ret_blob,
               uint16_t *ret_primary_alg,
-              void **ret_srk_buf,
-              size_t *ret_srk_buf_size) {
+              struct iovec *ret_srk) {
 
         uint16_t primary_alg = 0;
         int r;
 
         assert(ret_secret);
-        assert(ret_secret_size);
         assert(ret_blob);
-        assert(ret_blob_size);
 
         /* So here's what we do here: we connect to the TPM2 chip. It persistently contains a "seed" key that
          * is randomized when the TPM2 is first initialized or reset and remains stable across boots. We
@@ -5227,13 +5210,22 @@ int tpm2_seal(Tpm2Context *c,
 
         usec_t start = now(CLOCK_MONOTONIC);
 
+        TPMA_OBJECT hmac_attributes =
+                        TPMA_OBJECT_FIXEDTPM |
+                        TPMA_OBJECT_FIXEDPARENT;
+
+        /* If protected by PIN, a user-selected low-entropy password, enable DA protection.
+           Without a PIN, the key's left protected only by a PCR policy, which does not benefit
+           from DA protection. */
+        hmac_attributes |= pin ? 0 : TPMA_OBJECT_NODA;
+
         /* We use a keyed hash object (i.e. HMAC) to store the secret key we want to use for unlocking the
          * LUKS2 volume with. We don't ever use for HMAC/keyed hash operations however, we just use it
          * because it's a key type that is universally supported and suitable for symmetric binary blobs. */
         TPMT_PUBLIC hmac_template = {
                 .type = TPM2_ALG_KEYEDHASH,
                 .nameAlg = TPM2_ALG_SHA256,
-                .objectAttributes = TPMA_OBJECT_FIXEDTPM | TPMA_OBJECT_FIXEDPARENT,
+                .objectAttributes = hmac_attributes,
                 .parameters.keyedHashDetail.scheme.scheme = TPM2_ALG_NULL,
                 .unique.keyedHash.size = SHA256_DIGEST_SIZE,
                 .authPolicy = policy ? *policy : TPM2B_DIGEST_MAKE(NULL, TPM2_SHA256_DIGEST_SIZE),
@@ -5262,7 +5254,7 @@ int tpm2_seal(Tpm2Context *c,
                 return log_debug_errno(r, "Failed to generate secret key: %m");
 
         _cleanup_(tpm2_handle_freep) Tpm2Handle *primary_handle = NULL;
-        if (ret_srk_buf) {
+        if (ret_srk) {
                 _cleanup_(Esys_Freep) TPM2B_PUBLIC *primary_public = NULL;
 
                 if (IN_SET(seal_key_handle, 0, TPM2_SRK_HANDLE)) {
@@ -5300,7 +5292,7 @@ int tpm2_seal(Tpm2Context *c,
                 if (seal_key_handle != 0)
                         log_debug("Using primary alg sealing, but seal key handle also provided; ignoring seal key handle.");
 
-                /* TODO: force all callers to provide ret_srk_buf, so we can stop sealing with the legacy templates. */
+                /* TODO: force all callers to provide ret_srk, so we can stop sealing with the legacy templates. */
                 primary_alg = TPM2_ALG_ECC;
 
                 TPM2B_PUBLIC template = {
@@ -5344,47 +5336,46 @@ int tpm2_seal(Tpm2Context *c,
         if (r < 0)
                 return r;
 
-        _cleanup_(erase_and_freep) void *secret = NULL;
-        secret = memdup(hmac_sensitive.data.buffer, hmac_sensitive.data.size);
-        if (!secret)
+        _cleanup_(iovec_done_erase) struct iovec secret = {};
+        secret.iov_base = memdup(hmac_sensitive.data.buffer, hmac_sensitive.data.size);
+        if (!secret.iov_base)
                 return log_oom_debug();
+        secret.iov_len = hmac_sensitive.data.size;
 
         log_debug("Marshalling private and public part of HMAC key.");
 
-        _cleanup_free_ void *blob = NULL;
-        size_t blob_size = 0;
-        r = tpm2_marshal_blob(public, private, /* seed= */ NULL, &blob, &blob_size);
+        _cleanup_(iovec_done) struct iovec blob = {};
+        r = tpm2_marshal_blob(public, private, /* seed= */ NULL, &blob.iov_base, &blob.iov_len);
         if (r < 0)
                 return log_debug_errno(r, "Could not create sealed blob: %m");
 
         if (DEBUG_LOGGING)
                 log_debug("Completed TPM2 key sealing in %s.", FORMAT_TIMESPAN(now(CLOCK_MONOTONIC) - start, 1));
 
-        _cleanup_free_ void *srk_buf = NULL;
-        size_t srk_buf_size = 0;
-        if (ret_srk_buf) {
+        if (ret_srk) {
+                _cleanup_(iovec_done) struct iovec srk = {};
                 _cleanup_(Esys_Freep) void *tmp = NULL;
-                r = tpm2_serialize(c, primary_handle, &tmp, &srk_buf_size);
+                size_t tmp_size;
+
+                r = tpm2_serialize(c, primary_handle, &tmp, &tmp_size);
                 if (r < 0)
                         return r;
 
                 /*
                  * make a copy since we don't want the caller to understand that
                  * ESYS allocated the pointer. It would make tracking what deallocator
-                 * to use for srk_buf in which context a PITA.
+                 * to use for srk in which context a PITA.
                  */
-                srk_buf = memdup(tmp, srk_buf_size);
-                if (!srk_buf)
+                srk.iov_base = memdup(tmp, tmp_size);
+                if (!srk.iov_base)
                         return log_oom_debug();
+                srk.iov_len = tmp_size;
 
-                *ret_srk_buf = TAKE_PTR(srk_buf);
-                *ret_srk_buf_size = srk_buf_size;
+                *ret_srk = TAKE_STRUCT(srk);
         }
 
-        *ret_secret = TAKE_PTR(secret);
-        *ret_secret_size = hmac_sensitive.data.size;
-        *ret_blob = TAKE_PTR(blob);
-        *ret_blob_size = blob_size;
+        *ret_secret = TAKE_STRUCT(secret);
+        *ret_blob = TAKE_STRUCT(blob);
 
         if (ret_primary_alg)
                 *ret_primary_alg = primary_alg;
@@ -5397,31 +5388,24 @@ int tpm2_seal(Tpm2Context *c,
 int tpm2_unseal(Tpm2Context *c,
                 uint32_t hash_pcr_mask,
                 uint16_t pcr_bank,
-                const void *pubkey,
-                size_t pubkey_size,
+                const struct iovec *pubkey,
                 uint32_t pubkey_pcr_mask,
                 JsonVariant *signature,
                 const char *pin,
                 const Tpm2PCRLockPolicy *pcrlock_policy,
                 uint16_t primary_alg,
-                const void *blob,
-                size_t blob_size,
-                const void *known_policy_hash,
-                size_t known_policy_hash_size,
-                const void *srk_buf,
-                size_t srk_buf_size,
-                void **ret_secret,
-                size_t *ret_secret_size) {
+                const struct iovec *blob,
+                const struct iovec *known_policy_hash,
+                const struct iovec *srk,
+                struct iovec *ret_secret) {
 
         TSS2_RC rc;
         int r;
 
-        assert(blob);
-        assert(blob_size > 0);
-        assert(known_policy_hash_size == 0 || known_policy_hash);
-        assert(pubkey_size == 0 || pubkey);
+        assert(iovec_is_set(blob));
+        assert(iovec_is_valid(known_policy_hash));
+        assert(iovec_is_valid(pubkey));
         assert(ret_secret);
-        assert(ret_secret_size);
 
         assert(TPM2_PCR_MASK_VALID(hash_pcr_mask));
         assert(TPM2_PCR_MASK_VALID(pubkey_pcr_mask));
@@ -5439,7 +5423,7 @@ int tpm2_unseal(Tpm2Context *c,
         TPM2B_PUBLIC public;
         TPM2B_PRIVATE private;
         TPM2B_ENCRYPTED_SECRET seed = {};
-        r = tpm2_unmarshal_blob(blob, blob_size, &public, &private, &seed);
+        r = tpm2_unmarshal_blob(blob->iov_base, blob->iov_len, &public, &private, &seed);
         if (r < 0)
                 return log_debug_errno(r, "Could not extract parts from blob: %m");
 
@@ -5452,8 +5436,8 @@ int tpm2_unseal(Tpm2Context *c,
         }
 
         _cleanup_(tpm2_handle_freep) Tpm2Handle *primary_handle = NULL;
-        if (srk_buf) {
-                r = tpm2_deserialize(c, srk_buf, srk_buf_size, &primary_handle);
+        if (iovec_is_set(srk)) {
+                r = tpm2_deserialize(c, srk->iov_base, srk->iov_len, &primary_handle);
                 if (r < 0)
                         return r;
         } else if (primary_alg != 0) {
@@ -5509,14 +5493,13 @@ int tpm2_unseal(Tpm2Context *c,
                 return r;
 
         TPM2B_PUBLIC pubkey_tpm2b;
-        _cleanup_free_ void *fp = NULL;
-        size_t fp_size = 0;
-        if (pubkey) {
-                r = tpm2_tpm2b_public_from_pem(pubkey, pubkey_size, &pubkey_tpm2b);
+        _cleanup_(iovec_done) struct iovec fp = {};
+        if (iovec_is_set(pubkey)) {
+                r = tpm2_tpm2b_public_from_pem(pubkey->iov_base, pubkey->iov_len, &pubkey_tpm2b);
                 if (r < 0)
                         return log_debug_errno(r, "Could not create TPMT_PUBLIC: %m");
 
-                r = tpm2_tpm2b_public_to_fingerprint(&pubkey_tpm2b, &fp, &fp_size);
+                r = tpm2_tpm2b_public_to_fingerprint(&pubkey_tpm2b, &fp.iov_base, &fp.iov_len);
                 if (r < 0)
                         return log_debug_errno(r, "Could not get key fingerprint: %m");
         }
@@ -5554,8 +5537,8 @@ int tpm2_unseal(Tpm2Context *c,
                                 policy_session,
                                 hash_pcr_mask,
                                 pcr_bank,
-                                pubkey ? &pubkey_tpm2b : NULL,
-                                fp, fp_size,
+                                iovec_is_set(pubkey) ? &pubkey_tpm2b : NULL,
+                                fp.iov_base, fp.iov_len,
                                 pubkey_pcr_mask,
                                 signature,
                                 !!pin,
@@ -5566,8 +5549,8 @@ int tpm2_unseal(Tpm2Context *c,
 
                 /* If we know the policy hash to expect, and it doesn't match, we can shortcut things here, and not
                  * wait until the TPM2 tells us to go away. */
-                if (known_policy_hash_size > 0 &&
-                        memcmp_nn(policy_digest->buffer, policy_digest->size, known_policy_hash, known_policy_hash_size) != 0)
+                if (iovec_is_set(known_policy_hash) &&
+                        memcmp_nn(policy_digest->buffer, policy_digest->size, known_policy_hash->iov_base, known_policy_hash->iov_len) != 0)
                                 return log_debug_errno(SYNTHETIC_ERRNO(EPERM),
                                                        "Current policy digest does not match stored policy digest, cancelling "
                                                        "TPM2 authentication attempt.");
@@ -5589,17 +5572,17 @@ int tpm2_unseal(Tpm2Context *c,
                 log_debug("A PCR value changed during the TPM2 policy session, restarting HMAC key unsealing (%u tries left).", i);
         }
 
-        _cleanup_(erase_and_freep) char *secret = NULL;
-        secret = memdup(unsealed->buffer, unsealed->size);
+        _cleanup_(iovec_done_erase) struct iovec secret = {};
+        secret.iov_base = memdup(unsealed->buffer, unsealed->size);
         explicit_bzero_safe(unsealed->buffer, unsealed->size);
-        if (!secret)
+        if (!secret.iov_base)
                 return log_oom_debug();
+        secret.iov_len = unsealed->size;
 
         if (DEBUG_LOGGING)
                 log_debug("Completed TPM2 key unsealing in %s.", FORMAT_TIMESPAN(now(CLOCK_MONOTONIC) - start, 1));
 
-        *ret_secret = TAKE_PTR(secret);
-        *ret_secret_size = unsealed->size;
+        *ret_secret = TAKE_STRUCT(secret);
 
         return 0;
 }
@@ -6958,18 +6941,13 @@ int tpm2_make_luks2_json(
                 int keyslot,
                 uint32_t hash_pcr_mask,
                 uint16_t pcr_bank,
-                const void *pubkey,
-                size_t pubkey_size,
+                const struct iovec *pubkey,
                 uint32_t pubkey_pcr_mask,
                 uint16_t primary_alg,
-                const void *blob,
-                size_t blob_size,
-                const void *policy_hash,
-                size_t policy_hash_size,
-                const void *salt,
-                size_t salt_size,
-                const void *srk_buf,
-                size_t srk_buf_size,
+                const struct iovec *blob,
+                const struct iovec *policy_hash,
+                const struct iovec *salt,
+                const struct iovec *srk,
                 TPM2Flags flags,
                 JsonVariant **ret) {
 
@@ -6977,9 +6955,9 @@ int tpm2_make_luks2_json(
         _cleanup_free_ char *keyslot_as_string = NULL;
         int r;
 
-        assert(blob || blob_size == 0);
-        assert(policy_hash || policy_hash_size == 0);
-        assert(pubkey || pubkey_size == 0);
+        assert(iovec_is_valid(pubkey));
+        assert(iovec_is_valid(blob));
+        assert(iovec_is_valid(policy_hash));
 
         if (asprintf(&keyslot_as_string, "%i", keyslot) < 0)
                 return -ENOMEM;
@@ -7002,17 +6980,17 @@ int tpm2_make_luks2_json(
                        JSON_BUILD_OBJECT(
                                        JSON_BUILD_PAIR("type", JSON_BUILD_CONST_STRING("systemd-tpm2")),
                                        JSON_BUILD_PAIR("keyslots", JSON_BUILD_ARRAY(JSON_BUILD_STRING(keyslot_as_string))),
-                                       JSON_BUILD_PAIR("tpm2-blob", JSON_BUILD_BASE64(blob, blob_size)),
+                                       JSON_BUILD_PAIR("tpm2-blob", JSON_BUILD_IOVEC_BASE64(blob)),
                                        JSON_BUILD_PAIR("tpm2-pcrs", JSON_BUILD_VARIANT(hmj)),
                                        JSON_BUILD_PAIR_CONDITION(!!tpm2_hash_alg_to_string(pcr_bank), "tpm2-pcr-bank", JSON_BUILD_STRING(tpm2_hash_alg_to_string(pcr_bank))),
                                        JSON_BUILD_PAIR_CONDITION(!!tpm2_asym_alg_to_string(primary_alg), "tpm2-primary-alg", JSON_BUILD_STRING(tpm2_asym_alg_to_string(primary_alg))),
-                                       JSON_BUILD_PAIR("tpm2-policy-hash", JSON_BUILD_HEX(policy_hash, policy_hash_size)),
+                                       JSON_BUILD_PAIR("tpm2-policy-hash", JSON_BUILD_IOVEC_HEX(policy_hash)),
                                        JSON_BUILD_PAIR("tpm2-pin", JSON_BUILD_BOOLEAN(flags & TPM2_FLAGS_USE_PIN)),
                                        JSON_BUILD_PAIR("tpm2_pcrlock", JSON_BUILD_BOOLEAN(flags & TPM2_FLAGS_USE_PCRLOCK)),
                                        JSON_BUILD_PAIR_CONDITION(pubkey_pcr_mask != 0, "tpm2_pubkey_pcrs", JSON_BUILD_VARIANT(pkmj)),
-                                       JSON_BUILD_PAIR_CONDITION(pubkey_pcr_mask != 0, "tpm2_pubkey", JSON_BUILD_BASE64(pubkey, pubkey_size)),
-                                       JSON_BUILD_PAIR_CONDITION(salt, "tpm2_salt", JSON_BUILD_BASE64(salt, salt_size)),
-                                       JSON_BUILD_PAIR_CONDITION(srk_buf, "tpm2_srk", JSON_BUILD_BASE64(srk_buf, srk_buf_size))));
+                                       JSON_BUILD_PAIR_CONDITION(pubkey_pcr_mask != 0, "tpm2_pubkey", JSON_BUILD_IOVEC_BASE64(pubkey)),
+                                       JSON_BUILD_PAIR_CONDITION(iovec_is_set(salt), "tpm2_salt", JSON_BUILD_IOVEC_BASE64(salt)),
+                                       JSON_BUILD_PAIR_CONDITION(iovec_is_set(srk), "tpm2_srk", JSON_BUILD_IOVEC_BASE64(srk))));
         if (r < 0)
                 return r;
 
@@ -7027,22 +7005,16 @@ int tpm2_parse_luks2_json(
                 int *ret_keyslot,
                 uint32_t *ret_hash_pcr_mask,
                 uint16_t *ret_pcr_bank,
-                void **ret_pubkey,
-                size_t *ret_pubkey_size,
+                struct iovec *ret_pubkey,
                 uint32_t *ret_pubkey_pcr_mask,
                 uint16_t *ret_primary_alg,
-                void **ret_blob,
-                size_t *ret_blob_size,
-                void **ret_policy_hash,
-                size_t *ret_policy_hash_size,
-                void **ret_salt,
-                size_t *ret_salt_size,
-                void **ret_srk_buf,
-                size_t *ret_srk_buf_size,
+                struct iovec *ret_blob,
+                struct iovec *ret_policy_hash,
+                struct iovec *ret_salt,
+                struct iovec *ret_srk,
                 TPM2Flags *ret_flags) {
 
-        _cleanup_free_ void *blob = NULL, *policy_hash = NULL, *pubkey = NULL, *salt = NULL, *srk_buf = NULL;
-        size_t blob_size = 0, policy_hash_size = 0, pubkey_size = 0, salt_size = 0, srk_buf_size = 0;
+        _cleanup_(iovec_done) struct iovec blob = {}, policy_hash = {}, pubkey = {}, salt = {}, srk = {};
         uint32_t hash_pcr_mask = 0, pubkey_pcr_mask = 0;
         uint16_t primary_alg = TPM2_ALG_ECC; /* ECC was the only supported algorithm in systemd < 250, use that as implied default, for compatibility */
         uint16_t pcr_bank = UINT16_MAX; /* default: pick automatically */
@@ -7107,7 +7079,7 @@ int tpm2_parse_luks2_json(
         if (!w)
                 return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "TPM2 token data lacks 'tpm2-blob' field.");
 
-        r = json_variant_unbase64(w, &blob, &blob_size);
+        r = json_variant_unbase64_iovec(w, &blob);
         if (r < 0)
                 return log_debug_errno(r, "Invalid base64 data in 'tpm2-blob' field.");
 
@@ -7115,7 +7087,7 @@ int tpm2_parse_luks2_json(
         if (!w)
                 return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "TPM2 token data lacks 'tpm2-policy-hash' field.");
 
-        r = json_variant_unhex(w, &policy_hash, &policy_hash_size);
+        r = json_variant_unhex_iovec(w, &policy_hash);
         if (r < 0)
                 return log_debug_errno(r, "Invalid base64 data in 'tpm2-policy-hash' field.");
 
@@ -7137,7 +7109,7 @@ int tpm2_parse_luks2_json(
 
         w = json_variant_by_key(v, "tpm2_salt");
         if (w) {
-                r = json_variant_unbase64(w, &salt, &salt_size);
+                r = json_variant_unbase64_iovec(w, &salt);
                 if (r < 0)
                         return log_debug_errno(r, "Invalid base64 data in 'tpm2_salt' field.");
         }
@@ -7151,7 +7123,7 @@ int tpm2_parse_luks2_json(
 
         w = json_variant_by_key(v, "tpm2_pubkey");
         if (w) {
-                r = json_variant_unbase64(w, &pubkey, &pubkey_size);
+                r = json_variant_unbase64_iovec(w, &pubkey);
                 if (r < 0)
                         return log_debug_errno(r, "Failed to decode PCR public key.");
         } else if (pubkey_pcr_mask != 0)
@@ -7159,7 +7131,7 @@ int tpm2_parse_luks2_json(
 
         w = json_variant_by_key(v, "tpm2_srk");
         if (w) {
-                r = json_variant_unbase64(w, &srk_buf, &srk_buf_size);
+                r = json_variant_unbase64_iovec(w, &srk);
                 if (r < 0)
                         return log_debug_errno(r, "Invalid base64 data in 'tpm2_srk' field.");
         }
@@ -7171,31 +7143,21 @@ int tpm2_parse_luks2_json(
         if (ret_pcr_bank)
                 *ret_pcr_bank = pcr_bank;
         if (ret_pubkey)
-                *ret_pubkey = TAKE_PTR(pubkey);
-        if (ret_pubkey_size)
-                *ret_pubkey_size = pubkey_size;
+                *ret_pubkey = TAKE_STRUCT(pubkey);
         if (ret_pubkey_pcr_mask)
                 *ret_pubkey_pcr_mask = pubkey_pcr_mask;
         if (ret_primary_alg)
                 *ret_primary_alg = primary_alg;
         if (ret_blob)
-                *ret_blob = TAKE_PTR(blob);
-        if (ret_blob_size)
-                *ret_blob_size = blob_size;
+                *ret_blob = TAKE_STRUCT(blob);
         if (ret_policy_hash)
-                *ret_policy_hash = TAKE_PTR(policy_hash);
-        if (ret_policy_hash_size)
-                *ret_policy_hash_size = policy_hash_size;
+                *ret_policy_hash = TAKE_STRUCT(policy_hash);
         if (ret_salt)
-                *ret_salt = TAKE_PTR(salt);
-        if (ret_salt_size)
-                *ret_salt_size = salt_size;
+                *ret_salt = TAKE_STRUCT(salt);
         if (ret_flags)
                 *ret_flags = flags;
-        if (ret_srk_buf)
-                *ret_srk_buf = TAKE_PTR(srk_buf);
-        if (ret_srk_buf_size)
-                *ret_srk_buf_size = srk_buf_size;
+        if (ret_srk)
+                *ret_srk = TAKE_STRUCT(srk);
 
         return 0;
 }
index 55d748159f3f314e300e420d5ed271cdbe9cd7df..e94b345de7647223649295f85da757a76b163740 100644 (file)
@@ -277,7 +277,7 @@ int tpm2_calculate_policy_or(const TPM2B_DIGEST *branches, size_t n_branches, TP
 int tpm2_calculate_policy_super_pcr(Tpm2PCRPrediction *prediction, uint16_t algorithm, TPM2B_DIGEST *pcr_policy);
 int tpm2_calculate_serialize(TPM2_HANDLE handle, const TPM2B_NAME *name, const TPM2B_PUBLIC *public, void **ret_serialized, size_t *ret_serialized_size);
 int tpm2_calculate_sealing_policy(const Tpm2PCRValue *pcr_values, size_t n_pcr_values, const TPM2B_PUBLIC *public, bool use_pin, const Tpm2PCRLockPolicy *policy, TPM2B_DIGEST *digest);
-int tpm2_calculate_seal(TPM2_HANDLE parent_handle, const TPM2B_PUBLIC *parent_public, const TPMA_OBJECT *attributes, const void *secret, size_t secret_size, const TPM2B_DIGEST *policy, const char *pin, void **ret_secret, size_t *ret_secret_size, void **ret_blob, size_t *ret_blob_size, void **ret_serialized_parent, size_t *ret_serialized_parent_size);
+int tpm2_calculate_seal(TPM2_HANDLE parent_handle, const TPM2B_PUBLIC *parent_public, const TPMA_OBJECT *attributes, const struct iovec *secret, const TPM2B_DIGEST *policy, const char *pin, struct iovec *ret_secret, struct iovec *ret_blob, struct iovec *ret_serialized_parent);
 
 int tpm2_get_srk_template(TPMI_ALG_PUBLIC alg, TPMT_PUBLIC *ret_template);
 int tpm2_get_best_srk_template(Tpm2Context *c, TPMT_PUBLIC *ret_template);
@@ -285,8 +285,8 @@ int tpm2_get_best_srk_template(Tpm2Context *c, TPMT_PUBLIC *ret_template);
 int tpm2_get_srk(Tpm2Context *c, const Tpm2Handle *session, TPM2B_PUBLIC **ret_public, TPM2B_NAME **ret_name, TPM2B_NAME **ret_qname, Tpm2Handle **ret_handle);
 int tpm2_get_or_create_srk(Tpm2Context *c, const Tpm2Handle *session, TPM2B_PUBLIC **ret_public, TPM2B_NAME **ret_name, TPM2B_NAME **ret_qname, Tpm2Handle **ret_handle);
 
-int tpm2_seal(Tpm2Context *c, uint32_t seal_key_handle, const TPM2B_DIGEST *policy, const char *pin, void **ret_secret, size_t *ret_secret_size, void **ret_blob, size_t *ret_blob_size, uint16_t *ret_primary_alg, void **ret_srk_buf, size_t *ret_srk_buf_size);
-int tpm2_unseal(Tpm2Context *c, uint32_t hash_pcr_mask, uint16_t pcr_bank, const void *pubkey, size_t pubkey_size, uint32_t pubkey_pcr_mask, JsonVariant *signature, const char *pin, const Tpm2PCRLockPolicy *pcrlock_policy, uint16_t primary_alg, const void *blob, size_t blob_size, const void *policy_hash, size_t policy_hash_size, const void *srk_buf, size_t srk_buf_size, void **ret_secret, size_t *ret_secret_size);
+int tpm2_seal(Tpm2Context *c, uint32_t seal_key_handle, const TPM2B_DIGEST *policy, const char *pin, struct iovec *ret_secret, struct iovec *ret_blob, uint16_t *ret_primary_alg, struct iovec *ret_srk);
+int tpm2_unseal(Tpm2Context *c, uint32_t hash_pcr_mask, uint16_t pcr_bank, const struct iovec *pubkey, uint32_t pubkey_pcr_mask, JsonVariant *signature, const char *pin, const Tpm2PCRLockPolicy *pcrlock_policy, uint16_t primary_alg, const struct iovec *blob, const struct iovec *policy_hash, const struct iovec *srk, struct iovec *ret_secret);
 
 #if HAVE_OPENSSL
 int tpm2_tpm2b_public_to_openssl_pkey(const TPM2B_PUBLIC *public, EVP_PKEY **ret);
@@ -383,8 +383,8 @@ int tpm2_find_device_auto(char **ret);
 int tpm2_make_pcr_json_array(uint32_t pcr_mask, JsonVariant **ret);
 int tpm2_parse_pcr_json_array(JsonVariant *v, uint32_t *ret);
 
-int tpm2_make_luks2_json(int keyslot, uint32_t hash_pcr_mask, uint16_t pcr_bank, const void *pubkey, size_t pubkey_size, uint32_t pubkey_pcr_mask, uint16_t primary_alg, const void *blob, size_t blob_size, const void *policy_hash, size_t policy_hash_size, const void *salt, size_t salt_size, const void *srk_buf, size_t srk_buf_size, TPM2Flags flags, JsonVariant **ret);
-int tpm2_parse_luks2_json(JsonVariant *v, int *ret_keyslot, uint32_t *ret_hash_pcr_mask, uint16_t *ret_pcr_bank, void **ret_pubkey, size_t *ret_pubkey_size, uint32_t *ret_pubkey_pcr_mask, uint16_t *ret_primary_alg, void **ret_blob, size_t *ret_blob_size, void **ret_policy_hash, size_t *ret_policy_hash_size, void **ret_salt, size_t *ret_salt_size, void **ret_srk_buf, size_t *ret_srk_buf_size, TPM2Flags *ret_flags);
+int tpm2_make_luks2_json(int keyslot, uint32_t hash_pcr_mask, uint16_t pcr_bank, const struct iovec *pubkey, uint32_t pubkey_pcr_mask, uint16_t primary_alg, const struct iovec *blob, const struct iovec *policy_hash, const struct iovec *salt, const struct iovec *srk, TPM2Flags flags, JsonVariant **ret);
+int tpm2_parse_luks2_json(JsonVariant *v, int *ret_keyslot, uint32_t *ret_hash_pcr_mask, uint16_t *ret_pcr_bank, struct iovec *ret_pubkey, uint32_t *ret_pubkey_pcr_mask, uint16_t *ret_primary_alg, struct iovec *ret_blob, struct iovec *ret_policy_hash, struct iovec *ret_salt, struct iovec *ret_srk, TPM2Flags *ret_flags);
 
 /* Default to PCR 7 only */
 #define TPM2_PCR_INDEX_DEFAULT UINT32_C(7)
index 3fe3e80b833ba0baa0cf25771ec638b8b6df79a4..654c4d3588169d9f04a4a1b0f02ce4a1e59978e3 100644 (file)
@@ -16,7 +16,7 @@
 #include "rlimit-util.h"
 #include "string-table.h"
 #include "strv.h"
-#include "uid-alloc-range.h"
+#include "uid-classification.h"
 #include "user-record.h"
 #include "user-util.h"
 
@@ -535,43 +535,8 @@ static int json_dispatch_environment(const char *name, JsonVariant *variant, Jso
         return strv_free_and_replace(*l, n);
 }
 
-int json_dispatch_user_disposition(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
-        UserDisposition *disposition = userdata, k;
-
-        if (json_variant_is_null(variant)) {
-                *disposition = _USER_DISPOSITION_INVALID;
-                return 0;
-        }
-
-        if (!json_variant_is_string(variant))
-                return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a string.", strna(name));
-
-        k = user_disposition_from_string(json_variant_string(variant));
-        if (k < 0)
-                return json_log(variant, flags, k, "Disposition type '%s' not known.", json_variant_string(variant));
-
-        *disposition = k;
-        return 0;
-}
-
-static int json_dispatch_storage(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
-        UserStorage *storage = userdata, k;
-
-        if (json_variant_is_null(variant)) {
-                *storage = _USER_STORAGE_INVALID;
-                return 0;
-        }
-
-        if (!json_variant_is_string(variant))
-                return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a string.", strna(name));
-
-        k = user_storage_from_string(json_variant_string(variant));
-        if (k < 0)
-                return json_log(variant, flags, k, "Storage type '%s' not known.", json_variant_string(variant));
-
-        *storage = k;
-        return 0;
-}
+JSON_DISPATCH_ENUM_DEFINE(json_dispatch_user_disposition, UserDisposition, user_disposition_from_string);
+static JSON_DISPATCH_ENUM_DEFINE(json_dispatch_user_storage, UserStorage, user_storage_from_string);
 
 static int json_dispatch_tasks_or_memory_max(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
         uint64_t *limit = userdata, k;
@@ -746,7 +711,7 @@ static int dispatch_pkcs11_key_data(const char *name, JsonVariant *variant, Json
         if (!json_variant_is_string(variant))
                 return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a string.", strna(name));
 
-        r = unbase64mem(json_variant_string(variant), SIZE_MAX, &b, &l);
+        r = unbase64mem(json_variant_string(variant), &b, &l);
         if (r < 0)
                 return json_log(variant, flags, r, "Failed to decode encrypted PKCS#11 key: %m");
 
@@ -813,7 +778,7 @@ static int dispatch_fido2_hmac_credential(const char *name, JsonVariant *variant
         if (!json_variant_is_string(variant))
                 return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a string.", strna(name));
 
-        r = unbase64mem(json_variant_string(variant), SIZE_MAX, &b, &l);
+        r = unbase64mem(json_variant_string(variant), &b, &l);
         if (r < 0)
                 return json_log(variant, flags, r, "Failed to decode FIDO2 credential ID: %m");
 
@@ -843,7 +808,7 @@ static int dispatch_fido2_hmac_credential_array(const char *name, JsonVariant *v
                 if (!array)
                         return log_oom();
 
-                r = unbase64mem(json_variant_string(e), SIZE_MAX, &b, &l);
+                r = unbase64mem(json_variant_string(e), &b, &l);
                 if (r < 0)
                         return json_log(variant, flags, r, "Failed to decode FIDO2 credential ID: %m");
 
@@ -873,7 +838,7 @@ static int dispatch_fido2_hmac_salt_value(const char *name, JsonVariant *variant
         if (!json_variant_is_string(variant))
                 return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a string.", strna(name));
 
-        r = unbase64mem(json_variant_string(variant), SIZE_MAX, &b, &l);
+        r = unbase64mem(json_variant_string(variant), &b, &l);
         if (r < 0)
                 return json_log(variant, flags, r, "Failed to decode FIDO2 salt: %m");
 
@@ -1055,7 +1020,7 @@ static int dispatch_binding(const char *name, JsonVariant *variant, JsonDispatch
                 { "fileSystemUuid",    JSON_VARIANT_STRING,        json_dispatch_id128,          offsetof(UserRecord, file_system_uuid),     0         },
                 { "uid",               JSON_VARIANT_UNSIGNED,      json_dispatch_uid_gid,        offsetof(UserRecord, uid),                  0         },
                 { "gid",               JSON_VARIANT_UNSIGNED,      json_dispatch_uid_gid,        offsetof(UserRecord, gid),                  0         },
-                { "storage",           JSON_VARIANT_STRING,        json_dispatch_storage,        offsetof(UserRecord, storage),              0         },
+                { "storage",           JSON_VARIANT_STRING,        json_dispatch_user_storage,   offsetof(UserRecord, storage),              0         },
                 { "fileSystemType",    JSON_VARIANT_STRING,        json_dispatch_string,         offsetof(UserRecord, file_system_type),     JSON_SAFE },
                 { "luksCipher",        JSON_VARIANT_STRING,        json_dispatch_string,         offsetof(UserRecord, luks_cipher),          JSON_SAFE },
                 { "luksCipherMode",    JSON_VARIANT_STRING,        json_dispatch_string,         offsetof(UserRecord, luks_cipher_mode),     JSON_SAFE },
@@ -1185,7 +1150,7 @@ static int dispatch_per_machine(const char *name, JsonVariant *variant, JsonDisp
                 { "locked",                     JSON_VARIANT_BOOLEAN,       json_dispatch_tristate,               offsetof(UserRecord, locked),                        0         },
                 { "notBeforeUSec",              _JSON_VARIANT_TYPE_INVALID, json_dispatch_uint64,                 offsetof(UserRecord, not_before_usec),               0         },
                 { "notAfterUSec",               _JSON_VARIANT_TYPE_INVALID, json_dispatch_uint64,                 offsetof(UserRecord, not_after_usec),                0         },
-                { "storage",                    JSON_VARIANT_STRING,        json_dispatch_storage,                offsetof(UserRecord, storage),                       0         },
+                { "storage",                    JSON_VARIANT_STRING,        json_dispatch_user_storage,           offsetof(UserRecord, storage),                       0         },
                 { "diskSize",                   _JSON_VARIANT_TYPE_INVALID, json_dispatch_uint64,                 offsetof(UserRecord, disk_size),                     0         },
                 { "diskSizeRelative",           _JSON_VARIANT_TYPE_INVALID, json_dispatch_uint64,                 offsetof(UserRecord, disk_size_relative),            0         },
                 { "skeletonDirectory",          JSON_VARIANT_STRING,        json_dispatch_path,                   offsetof(UserRecord, skeleton_directory),            0         },
@@ -1540,7 +1505,7 @@ int user_record_load(UserRecord *h, JsonVariant *v, UserRecordLoadFlags load_fla
                 { "locked",                     JSON_VARIANT_BOOLEAN,       json_dispatch_tristate,               offsetof(UserRecord, locked),                        0         },
                 { "notBeforeUSec",              _JSON_VARIANT_TYPE_INVALID, json_dispatch_uint64,                 offsetof(UserRecord, not_before_usec),               0         },
                 { "notAfterUSec",               _JSON_VARIANT_TYPE_INVALID, json_dispatch_uint64,                 offsetof(UserRecord, not_after_usec),                0         },
-                { "storage",                    JSON_VARIANT_STRING,        json_dispatch_storage,                offsetof(UserRecord, storage),                       0         },
+                { "storage",                    JSON_VARIANT_STRING,        json_dispatch_user_storage,           offsetof(UserRecord, storage),                       0         },
                 { "diskSize",                   _JSON_VARIANT_TYPE_INVALID, json_dispatch_uint64,                 offsetof(UserRecord, disk_size),                     0         },
                 { "diskSizeRelative",           _JSON_VARIANT_TYPE_INVALID, json_dispatch_uint64,                 offsetof(UserRecord, disk_size_relative),            0         },
                 { "skeletonDirectory",          JSON_VARIANT_STRING,        json_dispatch_path,                   offsetof(UserRecord, skeleton_directory),            0         },
@@ -1625,7 +1590,7 @@ int user_record_load(UserRecord *h, JsonVariant *v, UserRecordLoadFlags load_fla
         if (r < 0)
                 return r;
 
-        r = json_dispatch(h->json, user_dispatch_table, json_flags, h);
+        r = json_dispatch(h->json, user_dispatch_table, json_flags | JSON_ALLOW_EXTENSIONS, h);
         if (r < 0)
                 return r;
 
index f60d48ace4f7297e310988469faea9eb8dbae1db..540573390c877d086c5ebb13f34fac074e28a74b 100644 (file)
@@ -199,7 +199,7 @@ static int userdb_on_query_reply(
 
                 assert_se(!iterator->found_user);
 
-                r = json_dispatch(parameters, dispatch_table, 0, &user_data);
+                r = json_dispatch(parameters, dispatch_table, JSON_ALLOW_EXTENSIONS, &user_data);
                 if (r < 0)
                         goto finish;
 
@@ -256,7 +256,7 @@ static int userdb_on_query_reply(
 
                 assert_se(!iterator->found_group);
 
-                r = json_dispatch(parameters, dispatch_table, 0, &group_data);
+                r = json_dispatch(parameters, dispatch_table, JSON_ALLOW_EXTENSIONS, &group_data);
                 if (r < 0)
                         goto finish;
 
@@ -309,7 +309,7 @@ static int userdb_on_query_reply(
                 assert(!iterator->found_user_name);
                 assert(!iterator->found_group_name);
 
-                r = json_dispatch(parameters, dispatch_table, 0, &membership_data);
+                r = json_dispatch(parameters, dispatch_table, JSON_ALLOW_EXTENSIONS, &membership_data);
                 if (r < 0)
                         goto finish;
 
diff --git a/src/shared/varlink-io.systemd.Hostname.c b/src/shared/varlink-io.systemd.Hostname.c
new file mode 100644 (file)
index 0000000..b2c5e03
--- /dev/null
@@ -0,0 +1,36 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "varlink-io.systemd.Credentials.h"
+
+static VARLINK_DEFINE_METHOD(
+                Describe,
+                VARLINK_DEFINE_OUTPUT(Hostname, VARLINK_STRING, 0),
+                VARLINK_DEFINE_OUTPUT(StaticHostname, VARLINK_STRING, VARLINK_NULLABLE),
+                VARLINK_DEFINE_OUTPUT(PrettyHostname, VARLINK_STRING, VARLINK_NULLABLE),
+                VARLINK_DEFINE_OUTPUT(DefaultHostname, VARLINK_STRING, VARLINK_NULLABLE),
+                VARLINK_DEFINE_OUTPUT(HostnameSource, VARLINK_STRING, 0),
+                VARLINK_DEFINE_OUTPUT(IconName, VARLINK_STRING, VARLINK_NULLABLE),
+                VARLINK_DEFINE_OUTPUT(Chassis, VARLINK_STRING, VARLINK_NULLABLE),
+                VARLINK_DEFINE_OUTPUT(Deployment, VARLINK_STRING, VARLINK_NULLABLE),
+                VARLINK_DEFINE_OUTPUT(Location, VARLINK_STRING, VARLINK_NULLABLE),
+                VARLINK_DEFINE_OUTPUT(KernelName, VARLINK_STRING, 0),
+                VARLINK_DEFINE_OUTPUT(KernelRelease, VARLINK_STRING, 0),
+                VARLINK_DEFINE_OUTPUT(KernelVersion, VARLINK_STRING, 0),
+                VARLINK_DEFINE_OUTPUT(OperatingSystemPrettyName, VARLINK_STRING, VARLINK_NULLABLE),
+                VARLINK_DEFINE_OUTPUT(OperatingSystemCPEName, VARLINK_STRING, VARLINK_NULLABLE),
+                VARLINK_DEFINE_OUTPUT(OperatingSystemHomeURL, VARLINK_STRING, VARLINK_NULLABLE),
+                VARLINK_DEFINE_OUTPUT(OperatingSystemSupportEnd, VARLINK_INT, VARLINK_NULLABLE),
+                VARLINK_DEFINE_OUTPUT(HardwareVendor, VARLINK_STRING, VARLINK_NULLABLE),
+                VARLINK_DEFINE_OUTPUT(HardwareModel, VARLINK_STRING, VARLINK_NULLABLE),
+                VARLINK_DEFINE_OUTPUT(HardwareSerial, VARLINK_STRING, VARLINK_NULLABLE),
+                VARLINK_DEFINE_OUTPUT(FirmwareVersion, VARLINK_STRING, VARLINK_NULLABLE),
+                VARLINK_DEFINE_OUTPUT(FirmwareVendor, VARLINK_STRING, VARLINK_NULLABLE),
+                VARLINK_DEFINE_OUTPUT(FirmwareDate, VARLINK_INT, VARLINK_NULLABLE),
+                VARLINK_DEFINE_OUTPUT(MachineID, VARLINK_STRING, 0),
+                VARLINK_DEFINE_OUTPUT(BootID, VARLINK_STRING, 0),
+                VARLINK_DEFINE_OUTPUT(ProductUUID, VARLINK_STRING, VARLINK_NULLABLE));
+
+VARLINK_DEFINE_INTERFACE(
+                io_systemd_Hostname,
+                "io.systemd.Hostname",
+                &vl_method_Describe);
diff --git a/src/shared/varlink-io.systemd.Hostname.h b/src/shared/varlink-io.systemd.Hostname.h
new file mode 100644 (file)
index 0000000..29bb20e
--- /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_Hostname;
index d95b613eafb05fdf377881ec8e57241d8461d28e..96a58ca768d1b25c686cb6d0206390aa0b1793db 100644 (file)
@@ -78,8 +78,11 @@ VARLINK_DEFINE_METHOD(
                 VARLINK_DEFINE_OUTPUT(ready, VARLINK_BOOL, VARLINK_NULLABLE),
                 /* Subsequent replies */
                 VARLINK_DEFINE_OUTPUT(state, VARLINK_STRING, VARLINK_NULLABLE),
+                VARLINK_DEFINE_OUTPUT(result, VARLINK_STRING, VARLINK_NULLABLE),
                 VARLINK_DEFINE_OUTPUT(rcode, VARLINK_INT, VARLINK_NULLABLE),
                 VARLINK_DEFINE_OUTPUT(errno, VARLINK_INT, VARLINK_NULLABLE),
+                VARLINK_DEFINE_OUTPUT(extendedDNSErrorCode, VARLINK_INT, VARLINK_NULLABLE),
+                VARLINK_DEFINE_OUTPUT(extendedDNSErrorMessage, VARLINK_STRING, VARLINK_NULLABLE),
                 VARLINK_DEFINE_OUTPUT_BY_TYPE(question, ResourceKey, VARLINK_NULLABLE|VARLINK_ARRAY),
                 VARLINK_DEFINE_OUTPUT_BY_TYPE(collectedQuestions, ResourceKey, VARLINK_NULLABLE|VARLINK_ARRAY),
                 VARLINK_DEFINE_OUTPUT_BY_TYPE(answer, Answer, VARLINK_NULLABLE|VARLINK_ARRAY));
index 0d8ad281fa6436508abd91c99fca6bc888c73dde..627b062ab0cb8088084f4d1c47d798bdcb65f40e 100644 (file)
@@ -40,7 +40,9 @@ static VARLINK_DEFINE_ERROR(InvalidReply);
 static VARLINK_DEFINE_ERROR(QueryAborted);
 static VARLINK_DEFINE_ERROR(
                 DNSSECValidationFailed,
-                VARLINK_DEFINE_FIELD(result, VARLINK_STRING, 0));
+                VARLINK_DEFINE_FIELD(result, VARLINK_STRING, 0),
+                VARLINK_DEFINE_FIELD(extendedDNSErrorCode, VARLINK_INT, VARLINK_NULLABLE),
+                VARLINK_DEFINE_FIELD(extendedDNSErrorMessage, VARLINK_STRING, VARLINK_NULLABLE));
 static VARLINK_DEFINE_ERROR(NoTrustAnchor);
 static VARLINK_DEFINE_ERROR(ResourceRecordTypeUnsupported);
 static VARLINK_DEFINE_ERROR(NetworkDown);
@@ -48,7 +50,9 @@ static VARLINK_DEFINE_ERROR(NoSource);
 static VARLINK_DEFINE_ERROR(StubLoop);
 static VARLINK_DEFINE_ERROR(
                 DNSError,
-                VARLINK_DEFINE_FIELD(rcode, VARLINK_INT, 0));
+                VARLINK_DEFINE_FIELD(rcode, VARLINK_INT, 0),
+                VARLINK_DEFINE_FIELD(extendedDNSErrorCode, VARLINK_INT, VARLINK_NULLABLE),
+                VARLINK_DEFINE_FIELD(extendedDNSErrorMessage, VARLINK_STRING, VARLINK_NULLABLE));
 static VARLINK_DEFINE_ERROR(CNAMELoop);
 static VARLINK_DEFINE_ERROR(BadAddressSize);
 
index 12a27fcb8dfc43061af25d12cc50e24dd1cbf7e9..67ed7652336e5d344b80faee04977b963e895c30 100644 (file)
@@ -171,6 +171,7 @@ struct Varlink {
         JsonVariant *current;
         VarlinkSymbol *current_method;
 
+        int peer_pidfd;
         struct ucred ucred;
         bool ucred_acquired:1;
 
@@ -361,6 +362,8 @@ static int varlink_new(Varlink **ret) {
                 .timeout = VARLINK_DEFAULT_TIMEOUT_USEC,
 
                 .af = -1,
+
+                .peer_pidfd = -EBADF,
         };
 
         *ret = v;
@@ -447,6 +450,10 @@ int varlink_connect_exec(Varlink **ret, const char *_command, char **_argv) {
         if (socketpair(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0, pair) < 0)
                 return log_debug_errno(errno, "Failed to allocate AF_UNIX socket pair: %m");
 
+        r = fd_nonblock(pair[1], false);
+        if (r < 0)
+                return log_debug_errno(r, "Failed to disable O_NONBLOCK for varlink socket: %m");
+
         r = safe_fork_full(
                         "(sd-vlexec)",
                         /* stdio_fds= */ NULL,
@@ -504,39 +511,120 @@ int varlink_connect_exec(Varlink **ret, const char *_command, char **_argv) {
         return 0;
 }
 
+static int varlink_connect_ssh(Varlink **ret, const char *where) {
+        _cleanup_close_pair_ int pair[2] = EBADF_PAIR;
+        _cleanup_(sigkill_waitp) pid_t pid = 0;
+        int r;
+
+        assert_return(ret, -EINVAL);
+        assert_return(where, -EINVAL);
+
+        /* Connects to an SSH server via OpenSSH 9.4's -W switch to connect to a remote AF_UNIX socket. For
+         * now we do not expose this function directly, but only via varlink_connect_url(). */
+
+        const char *ssh = secure_getenv("SYSTEMD_SSH") ?: "ssh";
+        if (!path_is_valid(ssh))
+                return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "SSH path is not valid, refusing: %s", ssh);
+
+        const char *e = strchr(where, ':');
+        if (!e)
+                return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "SSH specification lacks a : separator between host and path, refusing: %s", where);
+
+        _cleanup_free_ char *h = strndup(where, e - where);
+        if (!h)
+                return log_oom_debug();
+
+        _cleanup_free_ char *c = strdup(e + 1);
+        if (!c)
+                return log_oom_debug();
+
+        if (!path_is_absolute(c))
+                return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Remote AF_UNIX socket path is not absolute, refusing: %s", c);
+
+        _cleanup_free_ char *p = NULL;
+        r = path_simplify_alloc(c, &p);
+        if (r < 0)
+                return r;
+
+        if (!path_is_normalized(p))
+                return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Specified path is not normalized, refusing: %s", p);
+
+        log_debug("Forking off SSH child process '%s -W %s %s'.", ssh, p, h);
+
+        if (socketpair(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0, pair) < 0)
+                return log_debug_errno(errno, "Failed to allocate AF_UNIX socket pair: %m");
+
+        r = safe_fork_full(
+                        "(sd-vlssh)",
+                        /* stdio_fds= */ (int[]) { pair[1], pair[1], STDERR_FILENO },
+                        /* except_fds= */ NULL,
+                        /* n_except_fds= */ 0,
+                        FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG_SIGTERM|FORK_REOPEN_LOG|FORK_LOG|FORK_RLIMIT_NOFILE_SAFE|FORK_REARRANGE_STDIO,
+                        &pid);
+        if (r < 0)
+                return log_debug_errno(r, "Failed to spawn process: %m");
+        if (r == 0) {
+                /* Child */
+
+                execlp(ssh, "ssh", "-W", p, h, NULL);
+                log_debug_errno(errno, "Failed to invoke %s: %m", ssh);
+                _exit(EXIT_FAILURE);
+        }
+
+        pair[1] = safe_close(pair[1]);
+
+        Varlink *v;
+        r = varlink_new(&v);
+        if (r < 0)
+                return log_debug_errno(r, "Failed to create varlink object: %m");
+
+        v->fd = TAKE_FD(pair[0]);
+        v->af = AF_UNIX;
+        v->exec_pid = TAKE_PID(pid);
+        varlink_set_state(v, VARLINK_IDLE_CLIENT);
+
+        *ret = v;
+        return 0;
+}
+
 int varlink_connect_url(Varlink **ret, const char *url) {
         _cleanup_free_ char *c = NULL;
         const char *p;
-        bool exec;
+        enum {
+                SCHEME_UNIX,
+                SCHEME_EXEC,
+                SCHEME_SSH,
+        } scheme;
         int r;
 
         assert_return(ret, -EINVAL);
         assert_return(url, -EINVAL);
 
-        // FIXME: Add support for vsock:, ssh-exec:, ssh-unix: URL schemes here. (The latter with OpenSSH
-        // 9.4's -W switch for referencing remote AF_UNIX sockets.)
+        // FIXME: Maybe add support for vsock: and ssh-exec: URL schemes here.
 
-        /* The Varlink URL scheme is a bit underdefined. We support only the unix: transport for now, plus an
-         * exec: transport we made up ourselves. Strictly speaking this shouldn't even be called URL, since
-         * it has nothing to do with Internet URLs by RFC. */
+        /* The Varlink URL scheme is a bit underdefined. We support only the spec-defined unix: transport for
+         * now, plus exec:, ssh: transports we made up ourselves. Strictly speaking this shouldn't even be
+         * called "URL", since it has nothing to do with Internet URLs by RFC. */
 
         p = startswith(url, "unix:");
         if (p)
-                exec = false;
-        else {
-                p = startswith(url, "exec:");
-                if (!p)
-                        return log_debug_errno(SYNTHETIC_ERRNO(EPROTONOSUPPORT), "URL scheme not supported.");
-
-                exec = true;
-        }
+                scheme = SCHEME_UNIX;
+        else if ((p = startswith(url, "exec:")))
+                scheme = SCHEME_EXEC;
+        else if ((p = startswith(url, "ssh:")))
+                scheme = SCHEME_SSH;
+        else
+                return log_debug_errno(SYNTHETIC_ERRNO(EPROTONOSUPPORT), "URL scheme not supported.");
 
         /* The varlink.org reference C library supports more than just file system paths. We might want to
          * support that one day too. For now simply refuse that. */
         if (p[strcspn(p, ";?#")] != '\0')
                 return log_debug_errno(SYNTHETIC_ERRNO(EPROTONOSUPPORT), "URL parameterization with ';', '?', '#' not supported.");
 
-        if (exec || p[0] != '@') { /* no validity checks for abstract namespace */
+        if (scheme == SCHEME_SSH)
+                return varlink_connect_ssh(ret, p);
+
+        if (scheme == SCHEME_EXEC || p[0] != '@') { /* no path validity checks for abstract namespace sockets */
 
                 if (!path_is_absolute(p))
                         return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Specified path not absolute, refusing.");
@@ -549,7 +637,7 @@ int varlink_connect_url(Varlink **ret, const char *url) {
                         return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Specified path is not normalized, refusing.");
         }
 
-        if (exec)
+        if (scheme == SCHEME_EXEC)
                 return varlink_connect_exec(ret, c, NULL);
 
         return varlink_connect_address(ret, c ?: p);
@@ -638,6 +726,8 @@ static void varlink_clear(Varlink *v) {
                 sigterm_wait(v->exec_pid);
                 v->exec_pid = 0;
         }
+
+        v->peer_pidfd = safe_close(v->peer_pidfd);
 }
 
 static Varlink* varlink_destroy(Varlink *v) {
@@ -956,10 +1046,6 @@ static int varlink_parse_message(Varlink *v) {
 
         sz = e - begin + 1;
 
-        varlink_log(v, "New incoming message: %s", begin); /* FIXME: should we output the whole message here before validation?
-                                                            * This may produce a non-printable journal entry if the message
-                                                            * is invalid. We may also expose privileged information. */
-
         r = json_parse(begin, 0, &v->current, NULL, NULL);
         if (r < 0) {
                 /* If we encounter a parse failure flush all data. We cannot possibly recover from this,
@@ -1477,6 +1563,48 @@ finish:
         return r;
 }
 
+int varlink_dispatch_again(Varlink *v) {
+        int r;
+
+        assert_return(v, -EINVAL);
+
+        /* If a method call handler could not process the method call just yet (for example because it needed
+         * some Polkit authentication first), then it can leave the call unanswered, do its thing, and then
+         * ask to be dispatched a second time, via this call. It will then be called again, for the same
+         * message */
+
+        if (v->state == VARLINK_DISCONNECTED)
+                return varlink_log_errno(v, SYNTHETIC_ERRNO(ENOTCONN), "Not connected.");
+        if (v->state != VARLINK_PENDING_METHOD)
+                return varlink_log_errno(v, SYNTHETIC_ERRNO(EBUSY), "Connection has no pending method.");
+
+        varlink_set_state(v, VARLINK_IDLE_SERVER);
+
+        r = sd_event_source_set_enabled(v->defer_event_source, SD_EVENT_ON);
+        if (r < 0)
+                return varlink_log_errno(v, r, "Failed to enable deferred event source: %m");
+
+        return 0;
+}
+
+int varlink_get_current_parameters(Varlink *v, JsonVariant **ret) {
+        JsonVariant *p;
+
+        assert_return(v, -EINVAL);
+
+        if (!v->current)
+                return -ENODATA;
+
+        p = json_variant_by_key(v->current, "parameters");
+        if (!p)
+                return -ENODATA;
+
+        if (ret)
+                *ret = json_variant_ref(p);
+
+        return 0;
+}
+
 static void handle_revents(Varlink *v, int revents) {
         assert(v);
 
@@ -1721,12 +1849,17 @@ Varlink* varlink_flush_close_unref(Varlink *v) {
 
 static int varlink_format_json(Varlink *v, JsonVariant *m) {
         _cleanup_(erase_and_freep) char *text = NULL;
+        bool sensitive = false;
         int r;
 
         assert(v);
         assert(m);
 
-        r = json_variant_format(m, 0, &text);
+        r = json_variant_format(m, JSON_FORMAT_REFUSE_SENSITIVE, &text);
+        if (r == -EPERM) {
+                sensitive = true;
+                r = json_variant_format(m, /* flags= */ 0, &text);
+        }
         if (r < 0)
                 return r;
         assert(text[r] == '\0');
@@ -1734,7 +1867,7 @@ static int varlink_format_json(Varlink *v, JsonVariant *m) {
         if (v->output_buffer_size + r + 1 > VARLINK_BUFFER_MAX)
                 return -ENOBUFS;
 
-        varlink_log(v, "Sending message: %s", text);
+        varlink_log(v, "Sending message: %s", sensitive ? "<sensitive data>" : text);
 
         if (v->output_buffer_size == 0) {
 
@@ -1765,7 +1898,7 @@ static int varlink_format_json(Varlink *v, JsonVariant *m) {
                 v->output_buffer_index = 0;
         }
 
-        if (json_variant_is_sensitive(m))
+        if (sensitive)
                 v->output_buffer_sensitive = true; /* Propagate sensitive flag */
         else
                 text = mfree(text); /* No point in the erase_and_free() destructor declared above */
@@ -2591,6 +2724,54 @@ int varlink_get_peer_pid(Varlink *v, pid_t *ret) {
         return 0;
 }
 
+static int varlink_acquire_pidfd(Varlink *v) {
+        assert(v);
+
+        if (v->peer_pidfd >= 0)
+                return 0;
+
+        v->peer_pidfd = getpeerpidfd(v->fd);
+        if (v->peer_pidfd < 0)
+                return v->peer_pidfd;
+
+        return 0;
+}
+
+int varlink_get_peer_pidref(Varlink *v, PidRef *ret) {
+        int r;
+
+        assert_return(v, -EINVAL);
+        assert_return(ret, -EINVAL);
+
+        /* Returns r > 0 if we acquired the pidref via SO_PEERPIDFD (i.e. if we can use it for
+         * authentication). Returns == 0 if we didn't, and the pidref should not be used for
+         * authentication. */
+
+        r = varlink_acquire_pidfd(v);
+        if (r < 0)
+                return r;
+
+        if (v->peer_pidfd < 0) {
+                pid_t pid;
+
+                r = varlink_get_peer_pid(v, &pid);
+                if (r < 0)
+                        return r;
+
+                r = pidref_set_pid(ret, pid);
+                if (r < 0)
+                        return r;
+
+                return 0; /* didn't get pidfd securely */
+        }
+
+        r = pidref_set_pidfd(ret, v->peer_pidfd);
+        if (r < 0)
+                return r;
+
+        return 1; /* got pidfd securely */
+}
+
 int varlink_set_relative_timeout(Varlink *v, usec_t timeout) {
         assert_return(v, -EINVAL);
         assert_return(timeout > 0, -EINVAL);
index c60f695be78ec77acd062b76fefa985355680a9c..a971762a511c65ed9b8af5c4f80e4e3804886180 100644 (file)
@@ -4,6 +4,7 @@
 #include "sd-event.h"
 
 #include "json.h"
+#include "pidref.h"
 #include "time-util.h"
 #include "varlink-idl.h"
 
@@ -116,6 +117,12 @@ int varlink_error_errno(Varlink *v, int error);
 int varlink_notify(Varlink *v, JsonVariant *parameters);
 int varlink_notifyb(Varlink *v, ...);
 
+/* Ask for the current message to be dispatched again */
+int varlink_dispatch_again(Varlink *v);
+
+/* Get the currently processed incoming message */
+int varlink_get_current_parameters(Varlink *v, JsonVariant **ret);
+
 /* Parsing incoming data via json_dispatch() and generate a nice error on parse errors */
 int varlink_dispatch(Varlink *v, JsonVariant *parameters, const JsonDispatch table[], void *userdata);
 
@@ -139,6 +146,7 @@ void* varlink_get_userdata(Varlink *v);
 
 int varlink_get_peer_uid(Varlink *v, uid_t *ret);
 int varlink_get_peer_pid(Varlink *v, pid_t *ret);
+int varlink_get_peer_pidref(Varlink *v, PidRef *ret);
 
 int varlink_set_relative_timeout(Varlink *v, usec_t usec);
 
@@ -214,6 +222,9 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(VarlinkServer *, varlink_server_unref);
 /* This one we invented, and use for generically propagating system errors (errno) to clients */
 #define VARLINK_ERROR_SYSTEM "io.systemd.System"
 
+/* This one we invented and is a weaker version of "org.varlink.service.PermissionDenied", and indicates that if user would allow interactive auth, we might allow access */
+#define VARLINK_ERROR_INTERACTIVE_AUTHENTICATION_REQUIRED "io.systemd.InteractiveAuthenticationRequired"
+
 /* These are errors defined in the Varlink spec */
 #define VARLINK_ERROR_INTERFACE_NOT_FOUND "org.varlink.service.InterfaceNotFound"
 #define VARLINK_ERROR_METHOD_NOT_FOUND "org.varlink.service.MethodNotFound"
diff --git a/src/shared/vpick.c b/src/shared/vpick.c
new file mode 100644 (file)
index 0000000..ab1f428
--- /dev/null
@@ -0,0 +1,694 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <sys/stat.h>
+
+#include "architecture.h"
+#include "chase.h"
+#include "fd-util.h"
+#include "fs-util.h"
+#include "parse-util.h"
+#include "path-util.h"
+#include "recurse-dir.h"
+#include "vpick.h"
+
+void pick_result_done(PickResult *p) {
+        assert(p);
+
+        free(p->path);
+        safe_close(p->fd);
+        free(p->version);
+
+        *p = PICK_RESULT_NULL;
+}
+
+static int format_fname(
+                const PickFilter *filter,
+                PickFlags flags,
+                char **ret) {
+
+        _cleanup_free_ char *fn = NULL;
+        int r;
+
+        assert(filter);
+        assert(ret);
+
+        if (FLAGS_SET(flags, PICK_TRIES) || !filter->version) /* Underspecified? */
+                return -ENOEXEC;
+
+        /* The format for names we match goes like this:
+         *
+         *        <basename><suffix>
+         *  or:
+         *        <basename>_<version><suffix>
+         *  or:
+         *        <basename>_<version>_<architecture><suffix>
+         *  or:
+         *        <basename>_<architecture><suffix>
+         *
+         * (Note that basename can be empty, in which case the leading "_" is suppressed)
+         *
+         * Examples: foo.raw, foo_1.3-7.raw, foo_1.3-7_x86-64.raw, foo_x86-64.raw
+         *
+         * Why use "_" as separator here? Primarily because it is not used by Semver 2.0. In RPM it is used
+         * for "unsortable" versions, i.e. doesn't show up in "sortable" versions, which we matter for this
+         * usecase here. In Debian the underscore is not allowed (and it uses it itself for separating
+         * fields).
+         *
+         * This is very close to Debian's way to name packages, but allows arbitrary suffixes, and makes the
+         * architecture field redundant.
+         *
+         * Compare with RPM's "NEVRA" concept. Here we have "BVAS" (basename, version, architecture, suffix).
+         */
+
+        if (filter->basename) {
+                fn = strdup(filter->basename);
+                if (!fn)
+                        return -ENOMEM;
+        }
+
+        if (filter->version) {
+                if (isempty(fn)) {
+                        r = free_and_strdup(&fn, filter->version);
+                        if (r < 0)
+                                return r;
+                } else if (!strextend(&fn, "_", filter->version))
+                        return -ENOMEM;
+        }
+
+        if (FLAGS_SET(flags, PICK_ARCHITECTURE) && filter->architecture >= 0) {
+                const char *as = ASSERT_PTR(architecture_to_string(filter->architecture));
+                if (isempty(fn)) {
+                        r = free_and_strdup(&fn, as);
+                        if (r < 0)
+                                return r;
+                } else if (!strextend(&fn, "_", as))
+                        return -ENOMEM;
+        }
+
+        if (filter->suffix && !strextend(&fn, filter->suffix))
+                return -ENOMEM;
+
+        if (!filename_is_valid(fn))
+                return -EINVAL;
+
+        *ret = TAKE_PTR(fn);
+        return 0;
+}
+
+static int errno_from_mode(uint32_t type_mask, mode_t found) {
+        /* Returns the most appropriate error code if we are lookging for an inode of type of those in the
+         * 'type_mask' but found 'found' instead.
+         *
+         * type_mask is a mask of 1U << DT_REG, 1U << DT_DIR, … flags, while found is a S_IFREG, S_IFDIR, …
+         * mode value. */
+
+        if (type_mask == 0) /* type doesn't matter */
+                return 0;
+
+        if (FLAGS_SET(type_mask, UINT32_C(1) << IFTODT(found)))
+                return 0;
+
+        if (type_mask == (UINT32_C(1) << DT_BLK))
+                return -ENOTBLK;
+        if (type_mask == (UINT32_C(1) << DT_DIR))
+                return -ENOTDIR;
+        if (type_mask == (UINT32_C(1) << DT_SOCK))
+                return -ENOTSOCK;
+
+        if (S_ISLNK(found))
+                return -ELOOP;
+        if (S_ISDIR(found))
+                return -EISDIR;
+
+        return -EBADF;
+}
+
+static int pin_choice(
+                const char *toplevel_path,
+                int toplevel_fd,
+                const char *inode_path,
+                int _inode_fd, /* we always take ownership of the fd, even on failure */
+                unsigned tries_left,
+                unsigned tries_done,
+                const PickFilter *filter,
+                PickFlags flags,
+                PickResult *ret) {
+
+        _cleanup_close_ int inode_fd = TAKE_FD(_inode_fd);
+        _cleanup_free_ char *resolved_path = NULL;
+        int r;
+
+        assert(toplevel_fd >= 0 || toplevel_fd == AT_FDCWD);
+        assert(inode_path);
+        assert(filter);
+
+        toplevel_path = strempty(toplevel_path);
+
+        if (inode_fd < 0 || FLAGS_SET(flags, PICK_RESOLVE)) {
+                r = chaseat(toplevel_fd,
+                            inode_path,
+                            CHASE_AT_RESOLVE_IN_ROOT,
+                            FLAGS_SET(flags, PICK_RESOLVE) ? &resolved_path : 0,
+                            inode_fd < 0 ? &inode_fd : NULL);
+                if (r < 0)
+                        return r;
+
+                if (resolved_path)
+                        inode_path = resolved_path;
+        }
+
+        struct stat st;
+        if (fstat(inode_fd, &st) < 0)
+                return log_debug_errno(errno, "Failed to stat discovered inode '%s/%s': %m", toplevel_path, inode_path);
+
+        if (filter->type_mask != 0 &&
+            !FLAGS_SET(filter->type_mask, UINT32_C(1) << IFTODT(st.st_mode)))
+                return log_debug_errno(
+                                SYNTHETIC_ERRNO(errno_from_mode(filter->type_mask, st.st_mode)),
+                                "Inode '%s/%s' has wrong type, found '%s'.",
+                                toplevel_path, inode_path,
+                                inode_type_to_string(st.st_mode));
+
+        _cleanup_(pick_result_done) PickResult result = {
+                .fd = TAKE_FD(inode_fd),
+                .st = st,
+                .architecture = filter->architecture,
+                .tries_left = tries_left,
+                .tries_done = tries_done,
+        };
+
+        result.path = strdup(inode_path);
+        if (!result.path)
+                return log_oom_debug();
+
+        if (filter->version) {
+                result.version = strdup(filter->version);
+                if (!result.version)
+                        return log_oom_debug();
+        }
+
+        *ret = TAKE_PICK_RESULT(result);
+        return 1;
+}
+
+static int parse_tries(const char *s, unsigned *ret_tries_left, unsigned *ret_tries_done) {
+        unsigned left, done;
+        size_t n;
+
+        assert(s);
+        assert(ret_tries_left);
+        assert(ret_tries_done);
+
+        if (s[0] != '+')
+                goto nomatch;
+
+        s++;
+
+        n = strspn(s, DIGITS);
+        if (n == 0)
+                goto nomatch;
+
+        if (s[n] == 0) {
+                if (safe_atou(s, &left) < 0)
+                        goto nomatch;
+
+                done = 0;
+        } else if (s[n] == '-') {
+                _cleanup_free_ char *c = NULL;
+
+                c = strndup(s, n);
+                if (!c)
+                        return -ENOMEM;
+
+                if (safe_atou(c, &left) < 0)
+                        goto nomatch;
+
+                s += n + 1;
+
+                if (!in_charset(s, DIGITS))
+                        goto nomatch;
+
+                if (safe_atou(s, &done) < 0)
+                        goto nomatch;
+        } else
+                goto nomatch;
+
+        *ret_tries_left = left;
+        *ret_tries_done = done;
+        return 1;
+
+nomatch:
+        *ret_tries_left = *ret_tries_done = UINT_MAX;
+        return 0;
+}
+
+static int make_choice(
+                const char *toplevel_path,
+                int toplevel_fd,
+                const char *inode_path,
+                int _inode_fd, /* we always take ownership of the fd, even on failure */
+                const PickFilter *filter,
+                PickFlags flags,
+                PickResult *ret) {
+
+        static const Architecture local_architectures[] = {
+                /* In order of preference */
+                native_architecture(),
+#ifdef ARCHITECTURE_SECONDARY
+                ARCHITECTURE_SECONDARY,
+#endif
+                _ARCHITECTURE_INVALID, /* accept any arch, as last resort */
+        };
+
+        _cleanup_free_ DirectoryEntries *de = NULL;
+        _cleanup_free_ char *best_version = NULL, *best_filename = NULL, *p = NULL, *j = NULL;
+        _cleanup_close_ int dir_fd = -EBADF, object_fd = -EBADF, inode_fd = TAKE_FD(_inode_fd);
+        const Architecture *architectures;
+        unsigned best_tries_left = UINT_MAX, best_tries_done = UINT_MAX;
+        size_t n_architectures, best_architecture_index = SIZE_MAX;
+        int r;
+
+        assert(toplevel_fd >= 0 || toplevel_fd == AT_FDCWD);
+        assert(inode_path);
+        assert(filter);
+
+        toplevel_path = strempty(toplevel_path);
+
+        if (inode_fd < 0) {
+                r = chaseat(toplevel_fd, inode_path, CHASE_AT_RESOLVE_IN_ROOT, NULL, &inode_fd);
+                if (r < 0)
+                        return r;
+        }
+
+        /* Maybe the filter is fully specified? Then we can generate the file name directly */
+        r = format_fname(filter, flags, &j);
+        if (r >= 0) {
+                _cleanup_free_ char *object_path = NULL;
+
+                /* Yay! This worked! */
+                p = path_join(inode_path, j);
+                if (!p)
+                        return log_oom_debug();
+
+                r = chaseat(toplevel_fd, p, CHASE_AT_RESOLVE_IN_ROOT, &object_path, &object_fd);
+                if (r < 0) {
+                        if (r != -ENOENT)
+                                return log_debug_errno(r, "Failed to open '%s/%s': %m", toplevel_path, p);
+
+                        *ret = PICK_RESULT_NULL;
+                        return 0;
+                }
+
+                return pin_choice(
+                                toplevel_path,
+                                toplevel_fd,
+                                FLAGS_SET(flags, PICK_RESOLVE) ? object_path : p,
+                                TAKE_FD(object_fd), /* unconditionally pass ownership of the fd */
+                                /* tries_left= */ UINT_MAX,
+                                /* tries_done= */ UINT_MAX,
+                                filter,
+                                flags & ~PICK_RESOLVE,
+                                ret);
+
+        } else if (r != -ENOEXEC)
+                return log_debug_errno(r, "Failed to format file name: %m");
+
+        /* Underspecified, so we do our enumeration dance */
+
+        /* Convert O_PATH to a regular directory fd */
+        dir_fd = fd_reopen(inode_fd, O_DIRECTORY|O_RDONLY|O_CLOEXEC);
+        if (dir_fd < 0)
+                return log_debug_errno(dir_fd, "Failed to reopen '%s/%s' as directory: %m", toplevel_path, inode_path);
+
+        r = readdir_all(dir_fd, 0, &de);
+        if (r < 0)
+                return log_debug_errno(r, "Failed to read directory '%s/%s': %m", toplevel_path, inode_path);
+
+        if (filter->architecture < 0) {
+                architectures = local_architectures;
+                n_architectures = ELEMENTSOF(local_architectures);
+        } else {
+                architectures = &filter->architecture;
+                n_architectures = 1;
+        }
+
+        FOREACH_ARRAY(entry, de->entries, de->n_entries) {
+                unsigned found_tries_done = UINT_MAX, found_tries_left = UINT_MAX;
+                _cleanup_free_ char *chopped = NULL;
+                size_t found_architecture_index = SIZE_MAX;
+                const char *e;
+
+                if (!isempty(filter->basename)) {
+                        e = startswith((*entry)->d_name, filter->basename);
+                        if (!e)
+                                continue;
+
+                        if (e[0] != '_')
+                                continue;
+
+                        e++;
+                } else
+                        e = (*entry)->d_name;
+
+                if (!isempty(filter->suffix)) {
+                        const char *sfx;
+
+                        sfx = endswith(e, filter->suffix);
+                        if (!sfx)
+                                continue;
+
+                        chopped = strndup(e, sfx - e);
+                        if (!chopped)
+                                return log_oom_debug();
+
+                        e = chopped;
+                }
+
+                if (FLAGS_SET(flags, PICK_TRIES)) {
+                        char *plus = strrchr(e, '+');
+                        if (plus) {
+                                r = parse_tries(plus, &found_tries_left, &found_tries_done);
+                                if (r < 0)
+                                        return r;
+                                if (r > 0) /* Found and parsed, now chop off */
+                                        *plus = 0;
+                        }
+                }
+
+                if (FLAGS_SET(flags, PICK_ARCHITECTURE)) {
+                        char *underscore = strrchr(e, '_');
+                        Architecture a;
+
+                        a = underscore ? architecture_from_string(underscore + 1) : _ARCHITECTURE_INVALID;
+
+                        for (size_t i = 0; i < n_architectures; i++)
+                                if (architectures[i] == a) {
+                                        found_architecture_index = i;
+                                        break;
+                                }
+
+                        if (found_architecture_index == SIZE_MAX) { /* No matching arch found */
+                                log_debug("Found entry with architecture '%s' which is not what we are looking for, ignoring entry.", a < 0 ? "any" : architecture_to_string(a));
+                                continue;
+                        }
+
+                        /* Chop off architecture from string */
+                        if (underscore)
+                                *underscore = 0;
+                }
+
+                if (!version_is_valid(e)) {
+                        log_debug("Version string '%s' of entry '%s' is invalid, ignoring entry.", e, (*entry)->d_name);
+                        continue;
+                }
+
+                if (filter->version && !streq(filter->version, e)) {
+                        log_debug("Found entry with version string '%s', but was looking for '%s', ignoring entry.", e, filter->version);
+                        continue;
+                }
+
+                if (best_filename) { /* Already found one matching entry? Then figure out the better one */
+                        int d = 0;
+
+                        /* First, prefer entries with tries left over those without */
+                        if (FLAGS_SET(flags, PICK_TRIES))
+                                d = CMP(found_tries_left != 0, best_tries_left != 0);
+
+                        /* Second, prefer newer versions */
+                        if (d == 0)
+                                d = strverscmp_improved(e, best_version);
+
+                        /* Third, prefer native architectures over secondary architectures */
+                        if (d == 0 &&
+                            FLAGS_SET(flags, PICK_ARCHITECTURE) &&
+                            found_architecture_index != SIZE_MAX && best_architecture_index != SIZE_MAX)
+                                d = -CMP(found_architecture_index, best_architecture_index);
+
+                        /* Fourth, prefer entries with more tries left */
+                        if (FLAGS_SET(flags, PICK_TRIES)) {
+                                if (d == 0)
+                                        d = CMP(found_tries_left, best_tries_left);
+
+                                /* Fifth, prefer entries with fewer attempts done so far */
+                                if (d == 0)
+                                        d = -CMP(found_tries_done, best_tries_done);
+                        }
+
+                        /* Last, just compare the filenames as strings */
+                        if (d == 0)
+                                d = strcmp((*entry)->d_name, best_filename);
+
+                        if (d < 0) {
+                                log_debug("Found entry '%s' but previously found entry '%s' matches better, hence skipping entry.", (*entry)->d_name, best_filename);
+                                continue;
+                        }
+                }
+
+                r = free_and_strdup_warn(&best_version, e);
+                if (r < 0)
+                        return r;
+
+                r = free_and_strdup_warn(&best_filename, (*entry)->d_name);
+                if (r < 0)
+                        return r;
+
+                best_architecture_index = found_architecture_index;
+                best_tries_left = found_tries_left;
+                best_tries_done = found_tries_done;
+        }
+
+        if (!best_filename) { /* Everything was good, but we didn't find any suitable entry */
+                *ret = PICK_RESULT_NULL;
+                return 0;
+        }
+
+        p = path_join(inode_path, best_filename);
+        if (!p)
+                return log_oom_debug();
+
+        object_fd = openat(dir_fd, best_filename, O_CLOEXEC|O_PATH);
+        if (object_fd < 0)
+                return log_debug_errno(errno, "Failed to open '%s/%s': %m", toplevel_path, p);
+
+        return pin_choice(
+                        toplevel_path,
+                        toplevel_fd,
+                        p,
+                        TAKE_FD(object_fd),
+                        best_tries_left,
+                        best_tries_done,
+                        &(const PickFilter) {
+                                .type_mask = filter->type_mask,
+                                .basename = filter->basename,
+                                .version = empty_to_null(best_version),
+                                .architecture = best_architecture_index != SIZE_MAX ? architectures[best_architecture_index] : _ARCHITECTURE_INVALID,
+                                .suffix = filter->suffix,
+                        },
+                        flags,
+                        ret);
+}
+
+int path_pick(const char *toplevel_path,
+              int toplevel_fd,
+              const char *path,
+              const PickFilter *filter,
+              PickFlags flags,
+              PickResult *ret) {
+
+        _cleanup_free_ char *filter_bname = NULL, *dir = NULL, *parent = NULL, *fname = NULL;
+        const char *filter_suffix, *enumeration_path;
+        uint32_t filter_type_mask;
+        int r;
+
+        assert(toplevel_fd >= 0 || toplevel_fd == AT_FDCWD);
+        assert(path);
+
+        toplevel_path = strempty(toplevel_path);
+
+        /* Given a path, resolve .v/ subdir logic (if used!), and returns the choice made. This supports
+         * three ways to be called:
+         *
+         * • with a path referring a directory of any name, and filter→basename *explicitly* specified, in
+         *   which case we'll use a pattern "<filter→basename>_*<filter→suffix>" on the directory's files.
+         *
+         * • with no filter→basename explicitly specified and a path referring to a directory named in format
+         *   "<somestring><filter→suffix>.v" . In this case the filter basename to search for inside the dir
+         *   is derived from the directory name. Example: "/foo/bar/baz.suffix.v" → we'll search for
+         *   "/foo/bar/baz.suffix.v/baz_*.suffix".
+         *
+         * • with a path whose penultimate component ends in ".v/". In this case the final component of the
+         *   path refers to the pattern. Example: "/foo/bar/baz.v/waldo__.suffix" → we'll search for
+         *   "/foo/bar/baz.v/waldo_*.suffix".
+         */
+
+        /* Explicit basename specified, then shortcut things and do .v mode regardless of the path name. */
+        if (filter->basename)
+                return make_choice(
+                                toplevel_path,
+                                toplevel_fd,
+                                path,
+                                /* inode_fd= */ -EBADF,
+                                filter,
+                                flags,
+                                ret);
+
+        r = path_extract_filename(path, &fname);
+        if (r < 0) {
+                if (r != -EADDRNOTAVAIL) /* root dir or "." */
+                        return r;
+
+                /* If there's no path element we can derive a pattern off, the don't */
+                goto bypass;
+        }
+
+        /* Remember if the path ends in a dash suffix */
+        bool slash_suffix = r == O_DIRECTORY;
+
+        const char *e = endswith(fname, ".v");
+        if (e) {
+                /* So a path in the form /foo/bar/baz.v is specified. In this case our search basename is
+                 * "baz", possibly with a suffix chopped off if there's one specified. */
+                filter_bname = strndup(fname, e - fname);
+                if (!filter_bname)
+                        return -ENOMEM;
+
+                if (filter->suffix) {
+                        /* Chop off suffix, if specified */
+                        char *f = endswith(filter_bname, filter->suffix);
+                        if (f)
+                                *f = 0;
+                }
+
+                filter_suffix = filter->suffix;
+                filter_type_mask = filter->type_mask;
+
+                enumeration_path = path;
+        } else {
+                /* The path does not end in '.v', hence see if the last element is a pattern. */
+
+                char *wildcard = strrstr(fname, "___");
+                if (!wildcard)
+                        goto bypass; /* Not a pattern, then bypass */
+
+                /* We found the '___' wildcard, hence everything after it is our filter suffix, and
+                 * everything before is our filter basename */
+                *wildcard = 0;
+                filter_suffix = empty_to_null(wildcard + 3);
+
+                filter_bname = TAKE_PTR(fname);
+
+                r = path_extract_directory(path, &dir);
+                if (r < 0) {
+                        if (!IN_SET(r, -EDESTADDRREQ, -EADDRNOTAVAIL)) /* only filename specified (no dir), or root or "." */
+                                return r;
+
+                        goto bypass; /* No dir extractable, can't check if parent ends in ".v" */
+                }
+
+                r = path_extract_filename(dir, &parent);
+                if (r < 0) {
+                        if (r != -EADDRNOTAVAIL) /* root dir or "." */
+                                return r;
+
+                        goto bypass; /* Cannot extract fname from parent dir, can't check if it ends in ".v" */
+                }
+
+                e = endswith(parent, ".v");
+                if (!e)
+                        goto bypass; /* Doesn't end in ".v", shortcut */
+
+                filter_type_mask = filter->type_mask;
+                if (slash_suffix) {
+                        /* If the pattern is suffixed by a / then we are looking for directories apparently. */
+                        if (filter_type_mask != 0 && !FLAGS_SET(filter_type_mask, UINT32_C(1) << DT_DIR))
+                                return log_debug_errno(SYNTHETIC_ERRNO(errno_from_mode(filter_type_mask, S_IFDIR)),
+                                                       "Specified pattern ends in '/', but not looking for directories, refusing.");
+                        filter_type_mask = UINT32_C(1) << DT_DIR;
+                }
+
+                enumeration_path = dir;
+        }
+
+        return make_choice(
+                        toplevel_path,
+                        toplevel_fd,
+                        enumeration_path,
+                        /* inode_fd= */ -EBADF,
+                        &(const PickFilter) {
+                                .type_mask = filter_type_mask,
+                                .basename = filter_bname,
+                                .version = filter->version,
+                                .architecture = filter->architecture,
+                                .suffix = filter_suffix,
+                        },
+                        flags,
+                        ret);
+
+bypass:
+        /* Don't make any choice, but just use the passed path literally */
+        return pin_choice(
+                        toplevel_path,
+                        toplevel_fd,
+                        path,
+                        /* inode_fd= */ -EBADF,
+                        /* tries_left= */ UINT_MAX,
+                        /* tries_done= */ UINT_MAX,
+                        filter,
+                        flags,
+                        ret);
+}
+
+int path_pick_update_warn(
+                char **path,
+                const PickFilter *filter,
+                PickFlags flags,
+                PickResult *ret_result) {
+
+        _cleanup_(pick_result_done) PickResult result = PICK_RESULT_NULL;
+        int r;
+
+        assert(path);
+        assert(*path);
+
+        /* This updates the first argument if needed! */
+
+        r = path_pick(/* toplevel_path= */ NULL,
+                      /* toplevel_fd= */ AT_FDCWD,
+                      *path,
+                      filter,
+                      flags,
+                      &result);
+        if (r == -ENOENT) {
+                log_debug("Path '%s' doesn't exist, leaving as is.", *path);
+                *ret_result = PICK_RESULT_NULL;
+                return 0;
+        }
+        if (r < 0)
+                return log_error_errno(r, "Failed to pick version on path '%s': %m", *path);
+        if (r == 0)
+                return log_error_errno(SYNTHETIC_ERRNO(ENOENT), "No matching entries in versioned directory '%s' found.", *path);
+
+        log_debug("Resolved versioned directory pattern '%s' to file '%s' as version '%s'.", result.path, *path, strna(result.version));
+
+        if (ret_result) {
+                r = free_and_strdup_warn(path, result.path);
+                if (r < 0)
+                        return r;
+
+                *ret_result = TAKE_PICK_RESULT(result);
+        } else
+                free_and_replace(*path, result.path);
+
+        return 1;
+}
+
+const PickFilter pick_filter_image_raw = {
+        .type_mask = (UINT32_C(1) << DT_REG) | (UINT32_C(1) << DT_BLK),
+        .architecture = _ARCHITECTURE_INVALID,
+        .suffix = ".raw",
+};
+
+const PickFilter pick_filter_image_dir = {
+        .type_mask = UINT32_C(1) << DT_DIR,
+        .architecture = _ARCHITECTURE_INVALID,
+};
diff --git a/src/shared/vpick.h b/src/shared/vpick.h
new file mode 100644 (file)
index 0000000..591852e
--- /dev/null
@@ -0,0 +1,59 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include <sys/types.h>
+
+#include "architecture.h"
+
+typedef enum PickFlags {
+        PICK_ARCHITECTURE = 1 << 0,   /* Look for an architecture suffix */
+        PICK_TRIES        = 1 << 1,   /* Look for tries left/tries done counters */
+        PICK_RESOLVE      = 1 << 2,   /* Return the fully resolved (chased) path, rather than the path to the entry */
+} PickFlags;
+
+typedef struct PickFilter {
+        uint32_t type_mask;           /* A mask of 1U << DT_REG, 1U << DT_DIR, … */
+        const char *basename;         /* Can be overridden by search pattern */
+        const char *version;
+        Architecture architecture;
+        const char *suffix;           /* Can be overridden by search pattern */
+} PickFilter;
+
+typedef struct PickResult {
+        char *path;
+        int fd; /* O_PATH */
+        struct stat st;
+        char *version;
+        Architecture architecture;
+        unsigned tries_left;
+        unsigned tries_done;
+} PickResult;
+
+#define PICK_RESULT_NULL                                \
+        (const PickResult) {                            \
+                .fd = -EBADF,                           \
+                .st.st_mode = MODE_INVALID,             \
+                .architecture = _ARCHITECTURE_INVALID,  \
+                .tries_left = UINT_MAX,                 \
+                .tries_done = UINT_MAX,                 \
+        }
+
+#define TAKE_PICK_RESULT(pick) TAKE_GENERIC(pick, PickResult, PICK_RESULT_NULL)
+
+void pick_result_done(PickResult *p);
+
+int path_pick(const char *toplevel_path,
+              int toplevel_fd,
+              const char *path,
+              const PickFilter *filter,
+              PickFlags flags,
+              PickResult *ret);
+
+int path_pick_update_warn(
+                char **path,
+                const PickFilter *filter,
+                PickFlags flags,
+                PickResult *ret);
+
+extern const PickFilter pick_filter_image_raw;
+extern const PickFilter pick_filter_image_dir;
index 4c1a96871832fe45fcb2b22f1d15c3553b903af3..2d79f7147a8cef9750045ade46a558191d4154a6 100644 (file)
@@ -261,12 +261,15 @@ static int update_pretimeout(void) {
 
 static int update_timeout(void) {
         int r;
+        usec_t previous_timeout;
 
         assert(watchdog_timeout > 0);
 
         if (watchdog_fd < 0)
                 return 0;
 
+        previous_timeout = watchdog_timeout;
+
         if (watchdog_timeout != USEC_INFINITY) {
                 r = watchdog_set_timeout();
                 if (r < 0) {
@@ -281,8 +284,12 @@ static int update_timeout(void) {
 
         if (watchdog_timeout == USEC_INFINITY) {
                 r = watchdog_read_timeout();
-                if (r < 0)
-                        return log_error_errno(r, "Failed to query watchdog HW timeout: %m");
+                if (r < 0) {
+                        if (!ERRNO_IS_NOT_SUPPORTED(r))
+                                return log_error_errno(r, "Failed to query watchdog HW timeout: %m");
+                        log_info("Reading watchdog timeout is not supported, reusing the configured timeout.");
+                        watchdog_timeout = previous_timeout;
+                }
         }
 
         /* If the watchdog timeout was changed, the pretimeout could have been
index 9ea8f7ae87eb57fc830003006b423d6f68afcfc0..99cf6c297f8dda4d76f6eda6f5f5c215bc074b90 100644 (file)
@@ -49,7 +49,11 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
                 opterr = 0; /* do not print errors */
         }
 
+        /* We need to reset some global state manually here since libfuzzer feeds a single process with
+         * multiple inputs, so we might carry over state from previous invocations that can trigger
+         * certain asserts. */
         optind = 0; /* this tells the getopt machinery to reinitialize */
+        arg_transport = BUS_TRANSPORT_LOCAL;
 
         r = systemctl_dispatch_parse_argv(strv_length(argv), argv);
         if (r < 0)
index ff018d8f6ca9843388ca581c74b0a3f731713c13..f5dc7cd1ad85e160963334eb58a5fa49697b6d8d 100644 (file)
@@ -404,7 +404,7 @@ int pattern_match(const char *pattern, const char *s, InstanceMetadata *ret) {
                         if (strlen(t) != sizeof(found.sha256sum) * 2)
                                 goto nope;
 
-                        r = unhexmem(t, sizeof(found.sha256sum) * 2, &d, &l);
+                        r = unhexmem_full(t, sizeof(found.sha256sum) * 2, /* secure = */ false, &d, &l);
                         if (r == -ENOMEM)
                                 return r;
                         if (r < 0)
index e4bdd882848368ae130edf014d8d7e0ad04db4e8..96422626f4c61af5d0089ba4d3c0507fd0d84fff 100644 (file)
@@ -398,7 +398,7 @@ static int resource_load_from_web(
                 if (p[0] == '\\')
                         return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "File names with escapes not supported in manifest at line %zu, refusing.", line_nr);
 
-                r = unhexmem(p, 64, &h, &hlen);
+                r = unhexmem_full(p, 64, /* secure = */ false, &h, &hlen);
                 if (r < 0)
                         return log_error_errno(r, "Failed to parse digest at manifest line %zu, refusing.", line_nr);
 
index 8376868136b741d14ab3aaf2cdcfe75676535260..e7dcb865c53f0d944fe7616a6a235d289dfafa6b 100644 (file)
@@ -35,7 +35,7 @@
 #include "strv.h"
 #include "sync-util.h"
 #include "tmpfile-util-label.h"
-#include "uid-alloc-range.h"
+#include "uid-classification.h"
 #include "uid-range.h"
 #include "user-util.h"
 #include "utf8.h"
@@ -117,7 +117,7 @@ typedef struct Context {
         Set *names;
 
         uid_t search_uid;
-        UidRange *uid_range;
+        UIDRange *uid_range;
 
         UGIDAllocationRange login_defs;
         bool login_defs_need_warning;
index 1230bafab98c508ca5d19dae1c6310e48a624ad7..49b021cb8abac5dd2cdc746c9254b07b02cb61ca 100644 (file)
@@ -107,6 +107,7 @@ simple_tests += files(
         'test-install-file.c',
         'test-install-root.c',
         'test-io-util.c',
+        'test-iovec-util.c',
         'test-journal-importer.c',
         'test-kbd-util.c',
         'test-limits-util.c',
@@ -170,7 +171,7 @@ simple_tests += files(
         'test-terminal-util.c',
         'test-tmpfile-util.c',
         'test-udev-util.c',
-        'test-uid-alloc-range.c',
+        'test-uid-classification.c',
         'test-uid-range.c',
         'test-umask-util.c',
         'test-unaligned.c',
@@ -178,6 +179,7 @@ simple_tests += files(
         'test-user-util.c',
         'test-utf8.c',
         'test-verbs.c',
+        'test-vpick.c',
         'test-web-util.c',
         'test-xattr-util.c',
         'test-xml.c',
index 66208463606c8a04b1c1ad2a1350e2bffe307abd..be83690ee506a4282a2396848dfa7dcb58d03f28 100644 (file)
@@ -41,7 +41,7 @@
 #include "tests.h"
 #include "tmpfile-util.h"
 #include "tomoyo-util.h"
-#include "uid-alloc-range.h"
+#include "uid-classification.h"
 #include "user-util.h"
 #include "virt.h"
 
index acb198c1c1d946eea9b0c727b1d03e7712ffef13..e56a2f38e382fec6d5760b4da2819083aebf2dbf 100644 (file)
@@ -2,10 +2,13 @@
 
 #include "creds-util.h"
 #include "fileio.h"
+#include "id128-util.h"
+#include "iovec-util.h"
 #include "path-util.h"
 #include "rm-rf.h"
 #include "tests.h"
 #include "tmpfile-util.h"
+#include "tpm2-util.h"
 
 TEST(read_credential_strings) {
         _cleanup_free_ char *x = NULL, *y = NULL, *saved = NULL, *p = NULL;
@@ -39,18 +42,12 @@ TEST(read_credential_strings) {
 
         assert_se(read_credential_strings_many("foo", &x, "bar", &y) == 0);
         assert_se(x == NULL);
-        assert_se(streq(y, "piff"));
+        assert_se(streq(y, "paff"));
 
         p = mfree(p);
         assert_se(p = path_join(tmp, "foo"));
         assert_se(write_string_file(p, "knurz", WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_AVOID_NEWLINE) >= 0);
 
-        assert_se(read_credential_strings_many("foo", &x, "bar", &y) >= 0);
-        assert_se(streq(x, "knurz"));
-        assert_se(streq(y, "piff"));
-
-        y = mfree(y);
-
         assert_se(read_credential_strings_many("foo", &x, "bar", &y) >= 0);
         assert_se(streq(x, "knurz"));
         assert_se(streq(y, "paff"));
@@ -61,7 +58,9 @@ TEST(read_credential_strings) {
         assert_se(fwrite("x\0y", 1, 3, f) == 3); /* embedded NUL byte should result in EBADMSG when reading back with read_credential_strings_many() */
         f = safe_fclose(f);
 
-        assert_se(read_credential_strings_many("bazz", &x, "foo", &y) == -EBADMSG);
+        y = mfree(y);
+
+        assert_se(read_credential_strings_many("bazz", &x, "bar", &y) == -EBADMSG);
         assert_se(streq(x, "knurz"));
         assert_se(streq(y, "paff"));
 
@@ -118,4 +117,103 @@ TEST(credential_glob_valid) {
         assert_se(credential_glob_valid(buf));
 }
 
+static void test_encrypt_decrypt_with(sd_id128_t mode) {
+        static const struct iovec plaintext = CONST_IOVEC_MAKE_STRING("this is a super secret string");
+        int r;
+
+        log_notice("Running encryption/decryption test with mode " SD_ID128_FORMAT_STR ".", SD_ID128_FORMAT_VAL(mode));
+
+        _cleanup_(iovec_done) struct iovec encrypted = {};
+        r = encrypt_credential_and_warn(
+                        mode,
+                        "foo",
+                        /* timestamp= */ USEC_INFINITY,
+                        /* not_after=*/ USEC_INFINITY,
+                        /* tpm2_device= */ NULL,
+                        /* tpm2_hash_pcr_mask= */ 0,
+                        /* tpm2_pubkey_path= */ NULL,
+                        /* tpm2_pubkey_pcr_mask= */ 0,
+                        &plaintext,
+                        CREDENTIAL_ALLOW_NULL,
+                        &encrypted);
+        if (ERRNO_IS_NEG_MACHINE_ID_UNSET(r)) {
+                log_notice_errno(r, "Skipping test encryption mode " SD_ID128_FORMAT_STR ", because /etc/machine-id is not initialized.", SD_ID128_FORMAT_VAL(mode));
+                return;
+        }
+        if (ERRNO_IS_NEG_NOT_SUPPORTED(r)) {
+                log_notice_errno(r, "Skipping test encryption mode " SD_ID128_FORMAT_STR ", because encrypted credentials are not supported.", SD_ID128_FORMAT_VAL(mode));
+                return;
+        }
+
+        assert_se(r >= 0);
+
+        _cleanup_(iovec_done) struct iovec decrypted = {};
+        r = decrypt_credential_and_warn(
+                        "bar",
+                        /* validate_timestamp= */ USEC_INFINITY,
+                        /* tpm2_device= */ NULL,
+                        /* tpm2_signature_path= */ NULL,
+                        &encrypted,
+                        CREDENTIAL_ALLOW_NULL,
+                        &decrypted);
+        assert_se(r == -EREMOTE); /* name didn't match */
+
+        r = decrypt_credential_and_warn(
+                        "foo",
+                        /* validate_timestamp= */ USEC_INFINITY,
+                        /* tpm2_device= */ NULL,
+                        /* tpm2_signature_path= */ NULL,
+                        &encrypted,
+                        CREDENTIAL_ALLOW_NULL,
+                        &decrypted);
+        assert_se(r >= 0);
+
+        assert_se(iovec_memcmp(&plaintext, &decrypted) == 0);
+}
+
+static bool try_tpm2(void) {
+#if HAVE_TPM2
+        _cleanup_(tpm2_context_unrefp) Tpm2Context *tpm2_context = NULL;
+        int r;
+
+        r = tpm2_context_new(/* device= */ NULL, &tpm2_context);
+        if (r < 0)
+                log_notice_errno(r, "Failed to create TPM2 context, assuming no TPM2 support or privileges: %m");
+
+        return r >= 0;
+#else
+        return false;
+#endif
+}
+
+TEST(credential_encrypt_decrypt) {
+        _cleanup_(rm_rf_physical_and_freep) char *d = NULL;
+        _cleanup_free_ char *j = NULL;
+
+        test_encrypt_decrypt_with(CRED_AES256_GCM_BY_NULL);
+
+        assert_se(mkdtemp_malloc(NULL, &d) >= 0);
+        j = path_join(d, "secret");
+        assert_se(j);
+
+        const char *e = getenv("SYSTEMD_CREDENTIAL_SECRET");
+        _cleanup_free_ char *ec = NULL;
+
+        if (e)
+                assert_se(ec = strdup(e));
+
+        assert_se(setenv("SYSTEMD_CREDENTIAL_SECRET", j, true) >= 0);
+
+        test_encrypt_decrypt_with(CRED_AES256_GCM_BY_HOST);
+
+        if (try_tpm2()) {
+                test_encrypt_decrypt_with(CRED_AES256_GCM_BY_TPM2_HMAC);
+                test_encrypt_decrypt_with(CRED_AES256_GCM_BY_HOST_AND_TPM2_HMAC);
+        }
+
+        if (ec)
+                assert_se(setenv("SYSTEMD_CREDENTIAL_SECRET", ec, true) >= 0);
+
+}
+
 DEFINE_TEST_MAIN(LOG_INFO);
index b75576ab4c508495cbe0508ca44d70a04d642178..ed56710d9e761e2d77c16d999e065f0dc6e81404 100644 (file)
@@ -25,6 +25,7 @@ int main(int argc, char *argv[]) {
         assert_se(mkdir_p(f, 0755) >= 0);
 
         assert_se(make_inaccessible_nodes(f, 1, 1) >= 0);
+        assert_se(make_inaccessible_nodes(f, 1, 1) >= 0); /* 2nd call should be a clean NOP */
 
         f = prefix_roota(p, "/run/systemd/inaccessible/reg");
         assert_se(stat(f, &st) >= 0);
index cf77e7c022fd3b30fbfaec0869259a89b2a39e55..e03d179d4e671b86b1546bef57ebd732485630c3 100644 (file)
@@ -241,10 +241,10 @@ int main(int argc, char *argv[]) {
         assert_se( unit_has_dependency(manager_get_unit(m, "non-existing-on-failure.target"), UNIT_ATOM_ON_FAILURE_OF, a));
         assert_se( unit_has_dependency(a, UNIT_ATOM_ON_SUCCESS, manager_get_unit(m, "non-existing-on-success.target")));
         assert_se( unit_has_dependency(manager_get_unit(m, "non-existing-on-success.target"), UNIT_ATOM_ON_SUCCESS_OF, a));
-        assert_se(!unit_has_dependency(a, UNIT_ATOM_ON_FAILURE, manager_get_unit(m, "basic.target")));
-        assert_se(!unit_has_dependency(a, UNIT_ATOM_ON_SUCCESS, manager_get_unit(m, "basic.target")));
-        assert_se(!unit_has_dependency(a, UNIT_ATOM_ON_FAILURE_OF, manager_get_unit(m, "basic.target")));
-        assert_se(!unit_has_dependency(a, UNIT_ATOM_ON_SUCCESS_OF, manager_get_unit(m, "basic.target")));
+        assert_se(!unit_has_dependency(a, UNIT_ATOM_ON_FAILURE, manager_get_unit(m, SPECIAL_BASIC_TARGET)));
+        assert_se(!unit_has_dependency(a, UNIT_ATOM_ON_SUCCESS, manager_get_unit(m, SPECIAL_BASIC_TARGET)));
+        assert_se(!unit_has_dependency(a, UNIT_ATOM_ON_FAILURE_OF, manager_get_unit(m, SPECIAL_BASIC_TARGET)));
+        assert_se(!unit_has_dependency(a, UNIT_ATOM_ON_SUCCESS_OF, manager_get_unit(m, SPECIAL_BASIC_TARGET)));
         assert_se(!unit_has_dependency(a, UNIT_ATOM_PROPAGATES_RELOAD_TO, manager_get_unit(m, "non-existing-on-failure.target")));
 
         assert_se(unit_has_name(a, "a.service"));
index 6d51ad7b53e2374715fc7e2e912e8c2362ab1148..126ca14c66055a796ae4c05049a35edb872def1b 100644 (file)
@@ -39,6 +39,7 @@
 static char *user_runtime_unit_dir = NULL;
 static bool can_unshare;
 static bool have_net_dummy;
+static bool have_netns;
 static unsigned n_ran_tests = 0;
 
 STATIC_DESTRUCTOR_REGISTER(user_runtime_unit_dir, freep);
@@ -1111,6 +1112,9 @@ static void test_exec_networknamespacepath(Manager *m) {
         if (!have_net_dummy)
                 return (void)log_notice("Skipping %s, dummy network interface not available", __func__);
 
+        if (!have_netns)
+                return (void)log_notice("Skipping %s, network namespace not available", __func__);
+
         r = find_executable("ip", NULL);
         if (r < 0) {
                 log_notice_errno(r, "Skipping %s, could not find ip binary: %m", __func__);
@@ -1452,8 +1456,8 @@ static int intro(void) {
 
         if (have_net_dummy) {
                 /* Create a network namespace and a dummy interface in it for NetworkNamespacePath= */
-                (void) system("ip netns add test-execute-netns");
-                (void) system("ip netns exec test-execute-netns ip link add dummy-test-ns type dummy");
+                have_netns = system("ip netns add test-execute-netns") == 0;
+                have_netns = have_netns && system("ip netns exec test-execute-netns ip link add dummy-test-ns type dummy") == 0;
         }
 
         return EXIT_SUCCESS;
index f88400866037ff4be40b7af54631836d5674600d..c5abf54513aaaf59abe4aec18df6fd9fdbe5363a 100644 (file)
@@ -82,7 +82,7 @@ static void test_hexmem_one(const char *in, const char *expected) {
         log_debug("hexmem(\"%s\") → \"%s\" (expected: \"%s\")", strnull(in), result, expected);
         assert_se(streq(result, expected));
 
-        assert_se(unhexmem(result, SIZE_MAX, &mem, &len) >= 0);
+        assert_se(unhexmem(result, &mem, &len) >= 0);
         assert_se(memcmp_safe(mem, in, len) == 0);
 }
 
@@ -97,7 +97,7 @@ static void test_unhexmem_one(const char *s, size_t l, int retval) {
         _cleanup_free_ void *mem = NULL;
         size_t len;
 
-        assert_se(unhexmem(s, l, &mem, &len) == retval);
+        assert_se(unhexmem_full(s, l, /* secure = */ false, &mem, &len) == retval);
         if (retval == 0) {
                 char *answer;
 
@@ -318,7 +318,7 @@ TEST(base64mem_linebreak) {
                 assert_se(encoded);
                 assert_se((size_t) l == strlen(encoded));
 
-                assert_se(unbase64mem(encoded, SIZE_MAX, &decoded, &decoded_size) >= 0);
+                assert_se(unbase64mem(encoded, &decoded, &decoded_size) >= 0);
                 assert_se(decoded_size == n);
                 assert_se(memcmp(data, decoded, n) == 0);
 
@@ -452,7 +452,7 @@ static void test_unbase64mem_one(const char *input, const char *output, int ret)
         _cleanup_free_ void *buffer = NULL;
         size_t size = 0;
 
-        assert_se(unbase64mem(input, SIZE_MAX, &buffer, &size) == ret);
+        assert_se(unbase64mem(input, &buffer, &size) == ret);
         if (ret >= 0) {
                 assert_se(size == strlen(output));
                 assert_se(memcmp(buffer, output, size) == 0);
@@ -533,12 +533,12 @@ TEST(base64withwithouturl) {
         size_t size;
 
         /* This is regular base64 */
-        assert_se(unbase64mem("zKFyIq7aZn4EpuCCmpcF9jPgD8JFE1g/xfT0Mas8X4M0WycyigRsQ4IH4yysufus0AORQsuk3oeGhRC7t1tLyKD0Ih0VcYedv5+p8e6itqrIwzecu98+rNyUVDhWBzS0PMwxEw==", SIZE_MAX, &buffer, &size) >= 0);
+        assert_se(unbase64mem("zKFyIq7aZn4EpuCCmpcF9jPgD8JFE1g/xfT0Mas8X4M0WycyigRsQ4IH4yysufus0AORQsuk3oeGhRC7t1tLyKD0Ih0VcYedv5+p8e6itqrIwzecu98+rNyUVDhWBzS0PMwxEw==", &buffer, &size) >= 0);
         assert_se(memcmp_nn(plaintext, sizeof(plaintext), buffer, size) == 0);
         buffer = mfree(buffer);
 
         /* This is the same but in base64url */
-        assert_se(unbase64mem("zKFyIq7aZn4EpuCCmpcF9jPgD8JFE1g_xfT0Mas8X4M0WycyigRsQ4IH4yysufus0AORQsuk3oeGhRC7t1tLyKD0Ih0VcYedv5-p8e6itqrIwzecu98-rNyUVDhWBzS0PMwxEw==", SIZE_MAX, &buffer, &size) >= 0);
+        assert_se(unbase64mem("zKFyIq7aZn4EpuCCmpcF9jPgD8JFE1g_xfT0Mas8X4M0WycyigRsQ4IH4yysufus0AORQsuk3oeGhRC7t1tLyKD0Ih0VcYedv5-p8e6itqrIwzecu98-rNyUVDhWBzS0PMwxEw==", &buffer, &size) >= 0);
         assert_se(memcmp_nn(plaintext, sizeof(plaintext), buffer, size) == 0);
 
         /* Hint: use xxd -i to generate the static C array from some data, and basenc --base64 + basenc
diff --git a/src/test/test-iovec-util.c b/src/test/test-iovec-util.c
new file mode 100644 (file)
index 0000000..e7cc6e4
--- /dev/null
@@ -0,0 +1,57 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "iovec-util.h"
+#include "tests.h"
+
+TEST(iovec_memcmp) {
+        struct iovec iov1 = CONST_IOVEC_MAKE_STRING("abcdef"), iov2 = IOVEC_MAKE_STRING("bcdefg"), empty = {};
+
+        struct iovec iov1_truncated = iov1;
+        iov1_truncated.iov_len /= 2;
+
+        assert_se(iovec_memcmp(NULL, NULL) == 0);
+        assert_se(iovec_memcmp(&iov1, &iov1) == 0);
+        assert_se(iovec_memcmp(&iov2, &iov2) == 0);
+        assert_se(iovec_memcmp(&empty, &empty) == 0);
+        assert_se(iovec_memcmp(&iov1_truncated, &iov1_truncated) == 0);
+        assert_se(iovec_memcmp(&empty, NULL) == 0);
+        assert_se(iovec_memcmp(NULL, &empty) == 0);
+        assert_se(iovec_memcmp(&iov1, &iov2) < 0);
+        assert_se(iovec_memcmp(&iov2, &iov1) > 0);
+        assert_se(iovec_memcmp(&iov1, &empty) > 0);
+        assert_se(iovec_memcmp(&empty, &iov1) < 0);
+        assert_se(iovec_memcmp(&iov2, &empty) > 0);
+        assert_se(iovec_memcmp(&empty, &iov2) < 0);
+        assert_se(iovec_memcmp(&iov1_truncated, &empty) > 0);
+        assert_se(iovec_memcmp(&empty, &iov1_truncated) < 0);
+        assert_se(iovec_memcmp(&iov1, &iov1_truncated) > 0);
+        assert_se(iovec_memcmp(&iov1_truncated, &iov1) < 0);
+        assert_se(iovec_memcmp(&iov2, &iov1_truncated) > 0);
+        assert_se(iovec_memcmp(&iov1_truncated, &iov2) < 0);
+
+        _cleanup_(iovec_done) struct iovec copy = {};
+
+        assert_se(iovec_memdup(&iov1, &copy));
+        assert_se(iovec_memcmp(&iov1, &copy) == 0);
+}
+
+TEST(iovec_set_and_valid) {
+        struct iovec empty = {},
+                filled = CONST_IOVEC_MAKE_STRING("waldo"),
+                half = { .iov_base = (char*) "piff", .iov_len = 0 },
+                invalid = { .iov_base = NULL, .iov_len = 47 };
+
+        assert_se(!iovec_is_set(NULL));
+        assert_se(!iovec_is_set(&empty));
+        assert_se(iovec_is_set(&filled));
+        assert_se(!iovec_is_set(&half));
+        assert_se(!iovec_is_set(&invalid));
+
+        assert_se(iovec_is_valid(NULL));
+        assert_se(iovec_is_valid(&empty));
+        assert_se(iovec_is_valid(&filled));
+        assert_se(iovec_is_valid(&half));
+        assert_se(!iovec_is_valid(&invalid));
+}
+
+DEFINE_TEST_MAIN(LOG_INFO);
index c120a702c66a634f2d30e4beaa07e718bbe39623..333fbe6cf2a30c320f13178dba1243b2c3598f17 100644 (file)
@@ -6,9 +6,11 @@
 #include "escape.h"
 #include "fd-util.h"
 #include "fileio.h"
+#include "iovec-util.h"
 #include "json-internal.h"
 #include "json.h"
 #include "math-util.h"
+#include "string-table.h"
 #include "string-util.h"
 #include "strv.h"
 #include "tests.h"
@@ -104,6 +106,17 @@ static void test_variant_one(const char *data, Test test) {
         assert_se(json_variant_has_type(w, json_variant_type(v)));
         assert_se(json_variant_equal(v, w));
 
+        s = mfree(s);
+        r = json_variant_format(w, JSON_FORMAT_REFUSE_SENSITIVE, &s);
+        assert_se(r == -EPERM);
+        assert_se(!s);
+
+        s = mfree(s);
+        r = json_variant_format(w, JSON_FORMAT_PRETTY, &s);
+        assert_se(r >= 0);
+        assert_se(s);
+        assert_se((size_t) r == strlen(s));
+
         s = mfree(s);
         w = json_variant_unref(w);
 
@@ -813,4 +826,168 @@ TEST(json_dispatch) {
         assert_se(foobar.l == INT16_MIN);
 }
 
+typedef enum mytestenum {
+        myfoo, mybar, mybaz, _mymax, _myinvalid = -EINVAL,
+} mytestenum;
+
+static const char *mytestenum_table[_mymax] = {
+        [myfoo] = "myfoo",
+        [mybar] = "mybar",
+        [mybaz] = "mybaz",
+};
+
+DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(mytestenum, mytestenum);
+
+static JSON_DISPATCH_ENUM_DEFINE(dispatch_mytestenum, mytestenum, mytestenum_from_string);
+
+TEST(json_dispatch_enum_define) {
+
+        struct data {
+                mytestenum a, b, c, d;
+        } data = {
+                .a = _myinvalid,
+                .b = _myinvalid,
+                .c = _myinvalid,
+                .d = mybar,
+        };
+
+        _cleanup_(json_variant_unrefp) JsonVariant *j = NULL;
+
+        assert_se(json_build(&j, JSON_BUILD_OBJECT(
+                                             JSON_BUILD_PAIR("a", JSON_BUILD_STRING("mybaz")),
+                                             JSON_BUILD_PAIR("b", JSON_BUILD_STRING("mybar")),
+                                             JSON_BUILD_PAIR("c", JSON_BUILD_STRING("myfoo")),
+                                             JSON_BUILD_PAIR("d", JSON_BUILD_NULL))) >= 0);
+
+        assert_se(json_dispatch(j,
+                                (const JsonDispatch[]) {
+                                        { "a", _JSON_VARIANT_TYPE_INVALID, dispatch_mytestenum, offsetof(struct data, a), 0 },
+                                        { "b", _JSON_VARIANT_TYPE_INVALID, dispatch_mytestenum, offsetof(struct data, b), 0 },
+                                        { "c", _JSON_VARIANT_TYPE_INVALID, dispatch_mytestenum, offsetof(struct data, c), 0 },
+                                        { "d", _JSON_VARIANT_TYPE_INVALID, dispatch_mytestenum, offsetof(struct data, d), 0 },
+                                        {},
+                                },
+                                /* flags= */ 0,
+                                &data) >= 0);
+
+        assert(data.a == mybaz);
+        assert(data.b == mybar);
+        assert(data.c == myfoo);
+        assert(data.d < 0);
+}
+
+TEST(json_sensitive) {
+        _cleanup_(json_variant_unrefp) JsonVariant *a = NULL, *b = NULL, *v = NULL;
+        _cleanup_free_ char *s = NULL;
+        int r;
+
+        assert_se(json_build(&a, JSON_BUILD_STRV(STRV_MAKE("foo", "bar", "baz", "bar", "baz", "foo", "qux", "baz"))) >= 0);
+        assert_se(json_build(&b, JSON_BUILD_STRV(STRV_MAKE("foo", "bar", "baz", "qux"))) >= 0);
+
+        json_variant_sensitive(a);
+
+        assert_se(json_variant_format(a, JSON_FORMAT_REFUSE_SENSITIVE, &s) == -EPERM);
+        assert_se(!s);
+
+        r = json_variant_format(b, JSON_FORMAT_REFUSE_SENSITIVE, &s);
+        assert_se(r >= 0);
+        assert_se(s);
+        assert_se((size_t) r == strlen(s));
+        s = mfree(s);
+
+        assert_se(json_build(&v, JSON_BUILD_OBJECT(
+                                             JSON_BUILD_PAIR("c", JSON_BUILD_INTEGER(INT64_MIN)),
+                                             JSON_BUILD_PAIR("d", JSON_BUILD_STRING("-9223372036854775808")),
+                                             JSON_BUILD_PAIR("e", JSON_BUILD_EMPTY_OBJECT))) >= 0);
+        json_variant_dump(v, JSON_FORMAT_COLOR|JSON_FORMAT_PRETTY, NULL, NULL);
+
+        r = json_variant_format(v, JSON_FORMAT_REFUSE_SENSITIVE, &s);
+        assert_se(r >= 0);
+        assert_se(s);
+        assert_se((size_t) r == strlen(s));
+        s = mfree(s);
+        v = json_variant_unref(v);
+
+        assert_se(json_build(&v, JSON_BUILD_OBJECT(
+                                             JSON_BUILD_PAIR_VARIANT("b", b),
+                                             JSON_BUILD_PAIR("c", JSON_BUILD_INTEGER(INT64_MIN)),
+                                             JSON_BUILD_PAIR("d", JSON_BUILD_STRING("-9223372036854775808")),
+                                             JSON_BUILD_PAIR("e", JSON_BUILD_EMPTY_OBJECT))) >= 0);
+        json_variant_dump(v, JSON_FORMAT_COLOR|JSON_FORMAT_PRETTY, NULL, NULL);
+
+        r = json_variant_format(v, JSON_FORMAT_REFUSE_SENSITIVE, &s);
+        assert_se(r >= 0);
+        assert_se(s);
+        assert_se((size_t) r == strlen(s));
+        s = mfree(s);
+        v = json_variant_unref(v);
+
+        assert_se(json_build(&v, JSON_BUILD_OBJECT(
+                                             JSON_BUILD_PAIR_VARIANT("b", b),
+                                             JSON_BUILD_PAIR_VARIANT("a", a),
+                                             JSON_BUILD_PAIR("c", JSON_BUILD_INTEGER(INT64_MIN)),
+                                             JSON_BUILD_PAIR("d", JSON_BUILD_STRING("-9223372036854775808")),
+                                             JSON_BUILD_PAIR("e", JSON_BUILD_EMPTY_OBJECT))) >= 0);
+        json_variant_dump(v, JSON_FORMAT_COLOR|JSON_FORMAT_PRETTY, NULL, NULL);
+
+        assert_se(json_variant_format(v, JSON_FORMAT_REFUSE_SENSITIVE, &s) == -EPERM);
+        assert_se(!s);
+        v = json_variant_unref(v);
+
+        assert_se(json_build(&v, JSON_BUILD_OBJECT(
+                                             JSON_BUILD_PAIR_VARIANT("b", b),
+                                             JSON_BUILD_PAIR("c", JSON_BUILD_INTEGER(INT64_MIN)),
+                                             JSON_BUILD_PAIR_VARIANT("a", a),
+                                             JSON_BUILD_PAIR("d", JSON_BUILD_STRING("-9223372036854775808")),
+                                             JSON_BUILD_PAIR("e", JSON_BUILD_EMPTY_OBJECT))) >= 0);
+        json_variant_dump(v, JSON_FORMAT_COLOR|JSON_FORMAT_PRETTY, NULL, NULL);
+
+        assert_se(json_variant_format(v, JSON_FORMAT_REFUSE_SENSITIVE, &s) == -EPERM);
+        assert_se(!s);
+        v = json_variant_unref(v);
+
+        assert_se(json_build(&v, JSON_BUILD_OBJECT(
+                                             JSON_BUILD_PAIR_VARIANT("b", b),
+                                             JSON_BUILD_PAIR("c", JSON_BUILD_INTEGER(INT64_MIN)),
+                                             JSON_BUILD_PAIR("d", JSON_BUILD_STRING("-9223372036854775808")),
+                                             JSON_BUILD_PAIR_VARIANT("a", a),
+                                             JSON_BUILD_PAIR("e", JSON_BUILD_EMPTY_OBJECT))) >= 0);
+        json_variant_dump(v, JSON_FORMAT_COLOR|JSON_FORMAT_PRETTY, NULL, NULL);
+
+        assert_se(json_variant_format(v, JSON_FORMAT_REFUSE_SENSITIVE, &s) == -EPERM);
+        assert_se(!s);
+        v = json_variant_unref(v);
+
+        assert_se(json_build(&v, JSON_BUILD_OBJECT(
+                                             JSON_BUILD_PAIR_VARIANT("b", b),
+                                             JSON_BUILD_PAIR("c", JSON_BUILD_INTEGER(INT64_MIN)),
+                                             JSON_BUILD_PAIR("d", JSON_BUILD_STRING("-9223372036854775808")),
+                                             JSON_BUILD_PAIR("e", JSON_BUILD_EMPTY_OBJECT),
+                                             JSON_BUILD_PAIR_VARIANT("a", a))) >= 0);
+        json_variant_dump(v, JSON_FORMAT_COLOR|JSON_FORMAT_PRETTY, NULL, NULL);
+
+        assert_se(json_variant_format(v, JSON_FORMAT_REFUSE_SENSITIVE, &s) == -EPERM);
+        assert_se(!s);
+}
+
+TEST(json_iovec) {
+        struct iovec iov1 = CONST_IOVEC_MAKE_STRING("üxknürz"), iov2 = CONST_IOVEC_MAKE_STRING("wuffwuffmiau");
+
+        _cleanup_(json_variant_unrefp) JsonVariant *j = NULL;
+        assert_se(json_build(&j, JSON_BUILD_OBJECT(
+                                             JSON_BUILD_PAIR("nr1", JSON_BUILD_IOVEC_BASE64(&iov1)),
+                                             JSON_BUILD_PAIR("nr2", JSON_BUILD_IOVEC_HEX(&iov2)))) >= 0);
+
+        json_variant_dump(j, JSON_FORMAT_PRETTY_AUTO|JSON_FORMAT_COLOR_AUTO, /* f= */ NULL, /* prefix= */ NULL);
+
+        _cleanup_(iovec_done) struct iovec a = {}, b = {};
+        assert_se(json_variant_unbase64_iovec(json_variant_by_key(j, "nr1"), &a) >= 0);
+        assert_se(json_variant_unhex_iovec(json_variant_by_key(j, "nr2"), &b) >= 0);
+
+        assert_se(iovec_memcmp(&iov1, &a) == 0);
+        assert_se(iovec_memcmp(&iov2, &b) == 0);
+        assert_se(iovec_memcmp(&iov2, &a) < 0);
+        assert_se(iovec_memcmp(&iov1, &b) > 0);
+}
+
 DEFINE_TEST_MAIN(LOG_DEBUG);
index 052e2514f43849374fa4b13df21a40a65137a94b..49438713791fd2b39ca435da9cb8b5cfc94da0e5 100644 (file)
@@ -37,6 +37,7 @@ static void test_invalid_item(const char *str) {
 
 TEST(valid_items) {
         test_valid_item("any", AF_UNSPEC, 0, 0, 0);
+        test_valid_item("0-65535", AF_UNSPEC, 0, 0, 0);
         test_valid_item("ipv4", AF_INET, 0, 0, 0);
         test_valid_item("ipv6", AF_INET6, 0, 0, 0);
         test_valid_item("ipv4:any", AF_INET, 0, 0, 0);
@@ -45,6 +46,7 @@ TEST(valid_items) {
         test_valid_item("udp", AF_UNSPEC, IPPROTO_UDP, 0, 0);
         test_valid_item("tcp:any", AF_UNSPEC, IPPROTO_TCP, 0, 0);
         test_valid_item("udp:any", AF_UNSPEC, IPPROTO_UDP, 0, 0);
+        test_valid_item("0", AF_UNSPEC, 0, 1, 0);
         test_valid_item("6666", AF_UNSPEC, 0, 1, 6666);
         test_valid_item("6666-6667", AF_UNSPEC, 0, 2, 6666);
         test_valid_item("65535", AF_UNSPEC, 0, 1, 65535);
@@ -61,6 +63,7 @@ TEST(valid_items) {
         test_valid_item("ipv6:tcp:6666", AF_INET6, IPPROTO_TCP, 1, 6666);
         test_valid_item("ipv6:udp:6666-6667", AF_INET6, IPPROTO_UDP, 2, 6666);
         test_valid_item("ipv6:tcp:any", AF_INET6, IPPROTO_TCP, 0, 0);
+        test_valid_item("ipv6:tcp:0", AF_INET6, IPPROTO_TCP, 1, 0);
 }
 
 TEST(invalid_items) {
@@ -77,9 +80,7 @@ TEST(invalid_items) {
         test_invalid_item("ipv6::");
         test_invalid_item("ipv6:ipv6");
         test_invalid_item("ipv6:icmp");
-        test_invalid_item("ipv6:tcp:0");
         test_invalid_item("65536");
-        test_invalid_item("0-65535");
         test_invalid_item("ipv6:tcp:6666-6665");
         test_invalid_item("ipv6:tcp:6666-100000");
         test_invalid_item("ipv6::6666");
index 957e2141ef2a7b5c5620027d9fccf020e98a6e00..027b2a401eb1d2a3dda7662938818effbfb29d09 100644 (file)
@@ -946,6 +946,27 @@ TEST(is_reaper_process) {
         }
 }
 
+TEST(pid_get_start_time) {
+        _cleanup_(pidref_done) PidRef pidref = PIDREF_NULL;
+
+        assert_se(pidref_set_self(&pidref) >= 0);
+
+        uint64_t start_time;
+        assert_se(pidref_get_start_time(&pidref, &start_time) >= 0);
+        log_info("our starttime: %" PRIu64, start_time);
+
+        _cleanup_(pidref_done_sigkill_wait) PidRef child = PIDREF_NULL;
+
+        assert_se(pidref_safe_fork("(stub)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS, &child) >= 0);
+
+        uint64_t start_time2;
+        assert_se(pidref_get_start_time(&child, &start_time2) >= 0);
+
+        log_info("child starttime: %" PRIu64, start_time2);
+
+        assert_se(start_time2 >= start_time);
+}
+
 static int intro(void) {
         log_show_color(true);
         return EXIT_SUCCESS;
index 5aca207fa42c9202f87287a3c9c56d999d24abae..95137ffcf12e198a7b1cb8d321d8a16a43877794 100644 (file)
@@ -180,6 +180,21 @@ TEST(dir_is_empty) {
         assert_se(dir_is_empty_at(AT_FDCWD, empty_dir, /* ignore_hidden_or_backup= */ false) > 0);
 }
 
+TEST(inode_type_from_string) {
+        static const mode_t types[] = {
+                S_IFREG,
+                S_IFDIR,
+                S_IFLNK,
+                S_IFCHR,
+                S_IFBLK,
+                S_IFIFO,
+                S_IFSOCK,
+        };
+
+        FOREACH_ARRAY(m, types, ELEMENTSOF(types))
+                assert_se(inode_type_from_string(inode_type_to_string(*m)) == *m);
+}
+
 static int intro(void) {
         log_show_color(true);
         return EXIT_SUCCESS;
index a8fd45df733e77aed6d42e46a971e725c11fb53c..e78e299ed293c2d753589e49ac18b9278a6d1f7a 100644 (file)
@@ -1324,4 +1324,27 @@ TEST(strlevenshtein) {
         assert_se(strlevenshtein("sunday", "saturday") == 3);
 }
 
+TEST(strrstr) {
+        assert_se(!strrstr(NULL, NULL));
+        assert_se(!strrstr("foo", NULL));
+        assert_se(!strrstr(NULL, "foo"));
+
+        const char *p = "foo";
+        assert_se(strrstr(p, "foo") == p);
+        assert_se(strrstr(p, "fo") == p);
+        assert_se(strrstr(p, "f") == p);
+        assert_se(strrstr(p, "oo") == p + 1);
+        assert_se(strrstr(p, "o") == p + 2);
+        assert_se(strrstr(p, "") == p + strlen(p));
+        assert_se(!strrstr(p, "bar"));
+
+        p = "xoxoxox";
+        assert_se(strrstr(p, "") == p + strlen(p));
+        assert_se(strrstr(p, "x") == p + 6);
+        assert_se(strrstr(p, "ox") == p + 5);
+        assert_se(strrstr(p, "xo") == p + 4);
+        assert_se(strrstr(p, "xox") == p + 4);
+        assert_se(!strrstr(p, "xx"));
+}
+
 DEFINE_TEST_MAIN(LOG_DEBUG);
index cfd662b329d952d091469b9d4d2e0693c0b89b12..f70e2aa862d3d1f607b8fc91975a7831aab320cb 100644 (file)
@@ -1006,4 +1006,12 @@ TEST(strv_find_first_field) {
         assert_se(streq_ptr(strv_find_first_field(STRV_MAKE("i", "k", "l", "m", "d", "c", "a", "b"), haystack), "j"));
 }
 
+TEST(endswith_strv) {
+        assert_se(streq_ptr(endswith_strv("waldo", STRV_MAKE("xxx", "yyy", "ldo", "zzz")), "ldo"));
+        assert_se(streq_ptr(endswith_strv("waldo", STRV_MAKE("xxx", "yyy", "zzz")), NULL));
+        assert_se(streq_ptr(endswith_strv("waldo", STRV_MAKE("waldo")), "waldo"));
+        assert_se(streq_ptr(endswith_strv("waldo", STRV_MAKE("w", "o", "ldo")), "o"));
+        assert_se(streq_ptr(endswith_strv("waldo", STRV_MAKE("knurz", "", "waldo")), ""));
+}
+
 DEFINE_TEST_MAIN(LOG_INFO);
index 19881c6e91f447bbd8dbef3de8316bb389a49836..eeaf0b7b8854a36aea553c9aa91d566057473cbc 100644 (file)
@@ -1100,42 +1100,38 @@ static void calculate_seal_and_unseal(
         assert_se(asprintf(&secret_string, "The classified documents are in room %x", parent_index) > 0);
         size_t secret_size = strlen(secret_string) + 1;
 
-        _cleanup_free_ void *blob = NULL;
-        size_t blob_size = 0;
-        _cleanup_free_ void *serialized_parent = NULL;
-        size_t serialized_parent_size;
+        _cleanup_(iovec_done) struct iovec blob = {}, serialized_parent = {};
         assert_se(tpm2_calculate_seal(
                         parent_index,
                         parent_public,
                         /* attributes= */ NULL,
-                        secret_string, secret_size,
+                        &IOVEC_MAKE(secret_string, secret_size),
                         /* policy= */ NULL,
                         /* pin= */ NULL,
-                        /* ret_secret= */ NULL, /* ret_secret_size= */ 0,
-                        &blob, &blob_size,
-                        &serialized_parent, &serialized_parent_size) >= 0);
+                        /* ret_secret= */ NULL,
+                        &blob,
+                        &serialized_parent) >= 0);
 
-        _cleanup_free_ void *unsealed_secret = NULL;
-        size_t unsealed_secret_size;
+        _cleanup_(iovec_done) struct iovec unsealed_secret = {};
         assert_se(tpm2_unseal(
                         c,
                         /* hash_pcr_mask= */ 0,
                         /* pcr_bank= */ 0,
-                        /* pubkey= */ NULL, /* pubkey_size= */ 0,
+                        /* pubkey= */ NULL,
                         /* pubkey_pcr_mask= */ 0,
                         /* signature= */ NULL,
                         /* pin= */ NULL,
                         /* pcrlock_policy= */ NULL,
                         /* primary_alg= */ 0,
-                        blob, blob_size,
-                        /* known_policy_hash= */ NULL, /* known_policy_hash_size= */ 0,
-                        serialized_parent, serialized_parent_size,
-                        &unsealed_secret, &unsealed_secret_size) >= 0);
+                        &blob,
+                        /* known_policy_hash= */ NULL,
+                        &serialized_parent,
+                        &unsealed_secret) >= 0);
 
-        assert_se(memcmp_nn(secret_string, secret_size, unsealed_secret, unsealed_secret_size) == 0);
+        assert_se(memcmp_nn(secret_string, secret_size, unsealed_secret.iov_base, unsealed_secret.iov_len) == 0);
 
-        char unsealed_string[unsealed_secret_size];
-        assert_se(snprintf(unsealed_string, unsealed_secret_size, "%s", (char*) unsealed_secret) == (int) unsealed_secret_size - 1);
+        char unsealed_string[unsealed_secret.iov_len];
+        assert_se(snprintf(unsealed_string, unsealed_secret.iov_len, "%s", (char*) unsealed_secret.iov_base) == (int) unsealed_secret.iov_len - 1);
         log_debug("Unsealed secret is: %s", unsealed_string);
 }
 
@@ -1187,34 +1183,33 @@ static void check_seal_unseal_for_handle(Tpm2Context *c, TPM2_HANDLE handle) {
 
         log_debug("Check seal/unseal for handle 0x%" PRIx32, handle);
 
-        _cleanup_free_ void *secret = NULL, *blob = NULL, *srk = NULL, *unsealed_secret = NULL;
-        size_t secret_size, blob_size, srk_size, unsealed_secret_size;
+        _cleanup_(iovec_done) struct iovec secret = {}, blob = {}, srk = {}, unsealed_secret = {};
         assert_se(tpm2_seal(
                         c,
                         handle,
                         &policy,
                         /* pin= */ NULL,
-                        &secret, &secret_size,
-                        &blob, &blob_size,
+                        &secret,
+                        &blob,
                         /* ret_primary_alg= */ NULL,
-                        &srk, &srk_size) >= 0);
+                        &srk) >= 0);
 
         assert_se(tpm2_unseal(
                         c,
                         /* hash_pcr_mask= */ 0,
                         /* pcr_bank= */ 0,
-                        /* pubkey= */ NULL, /* pubkey_size= */ 0,
+                        /* pubkey= */ NULL,
                         /* pubkey_pcr_mask= */ 0,
                         /* signature= */ NULL,
                         /* pin= */ NULL,
                         /* pcrlock_policy= */ NULL,
                         /* primary_alg= */ 0,
-                        blob, blob_size,
-                        /* policy_hash= */ NULL, /* policy_hash_size= */ 0,
-                        srk, srk_size,
-                        &unsealed_secret, &unsealed_secret_size) >= 0);
+                        &blob,
+                        /* policy_hash= */ NULL,
+                        &srk,
+                        &unsealed_secret) >= 0);
 
-        assert_se(memcmp_nn(secret, secret_size, unsealed_secret, unsealed_secret_size) == 0);
+        assert_se(iovec_memcmp(&secret, &unsealed_secret) == 0);
 }
 
 static void check_seal_unseal(Tpm2Context *c) {
similarity index 99%
rename from src/test/test-uid-alloc-range.c
rename to src/test/test-uid-classification.c
index cd06463cefbbb26c9b4ebee76a714a0505b6c987..9c7500a00e588cdad57d0767705e32e838262097 100644 (file)
@@ -9,7 +9,7 @@
 #include "fs-util.h"
 #include "tests.h"
 #include "tmpfile-util.h"
-#include "uid-alloc-range.h"
+#include "uid-classification.h"
 
 static void test_read_login_defs_one(const char *path) {
         log_info("/* %s(\"%s\") */", __func__, path ?: "<custom>");
index aabbd2425ccb4a464a1237d9daceea4c52c1c27c..10ed8cc784383691bfcc820f7339e6b6e9c48360 100644 (file)
@@ -14,7 +14,7 @@
 #include "virt.h"
 
 TEST(uid_range) {
-        _cleanup_(uid_range_freep) UidRange *p = NULL;
+        _cleanup_(uid_range_freep) UIDRange *p = NULL;
         uid_t search;
 
         assert_se(uid_range_covers(p, 0, 0));
@@ -93,7 +93,7 @@ TEST(uid_range) {
 }
 
 TEST(load_userns) {
-        _cleanup_(uid_range_freep) UidRange *p = NULL;
+        _cleanup_(uid_range_freep) UIDRange *p = NULL;
         _cleanup_(unlink_and_freep) char *fn = NULL;
         _cleanup_fclose_ FILE *f = NULL;
         int r;
@@ -134,7 +134,7 @@ TEST(load_userns) {
 }
 
 TEST(uid_range_coalesce) {
-        _cleanup_(uid_range_freep) UidRange *p = NULL;
+        _cleanup_(uid_range_freep) UIDRange *p = NULL;
 
         for (size_t i = 0; i < 10; i++) {
                 assert_se(uid_range_add_internal(&p, i * 10, 10, /* coalesce = */ false) >= 0);
diff --git a/src/test/test-vpick.c b/src/test/test-vpick.c
new file mode 100644 (file)
index 0000000..1a288e8
--- /dev/null
@@ -0,0 +1,171 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "fd-util.h"
+#include "fileio.h"
+#include "fs-util.h"
+#include "mkdir.h"
+#include "path-util.h"
+#include "rm-rf.h"
+#include "tests.h"
+#include "tmpfile-util.h"
+#include "vpick.h"
+
+TEST(path_pick) {
+        _cleanup_(rm_rf_physical_and_freep) char *p = NULL;
+        _cleanup_close_ int dfd = -EBADF, sub_dfd = -EBADF;
+
+        dfd = mkdtemp_open(NULL, O_DIRECTORY|O_CLOEXEC, &p);
+        assert(dfd >= 0);
+
+        sub_dfd = open_mkdir_at(dfd, "foo.v", O_CLOEXEC, 0777);
+        assert(sub_dfd >= 0);
+
+        assert_se(write_string_file_at(sub_dfd, "foo_5.5.raw", "5.5", WRITE_STRING_FILE_CREATE) >= 0);
+        assert_se(write_string_file_at(sub_dfd, "foo_55.raw", "55", WRITE_STRING_FILE_CREATE) >= 0);
+        assert_se(write_string_file_at(sub_dfd, "foo_5.raw", "5", WRITE_STRING_FILE_CREATE) >= 0);
+        assert_se(write_string_file_at(sub_dfd, "foo_5_ia64.raw", "5", WRITE_STRING_FILE_CREATE) >= 0);
+        assert_se(write_string_file_at(sub_dfd, "foo_7.raw", "7", WRITE_STRING_FILE_CREATE) >= 0);
+        assert_se(write_string_file_at(sub_dfd, "foo_7_x86-64.raw", "7 64bit", WRITE_STRING_FILE_CREATE) >= 0);
+        assert_se(write_string_file_at(sub_dfd, "foo_55_x86-64.raw", "55 64bit", WRITE_STRING_FILE_CREATE) >= 0);
+        assert_se(write_string_file_at(sub_dfd, "foo_55_x86.raw", "55 32bit", WRITE_STRING_FILE_CREATE) >= 0);
+        assert_se(write_string_file_at(sub_dfd, "foo_99_x86.raw", "99 32bit", WRITE_STRING_FILE_CREATE) >= 0);
+
+        /* Let's add an entry for sparc (which is a valid arch, but almost certainly not what we test
+         * on). This entry should hence always be ignored */
+        if (native_architecture() != ARCHITECTURE_SPARC)
+                assert_se(write_string_file_at(sub_dfd, "foo_100_sparc.raw", "100 sparc", WRITE_STRING_FILE_CREATE) >= 0);
+
+        assert_se(write_string_file_at(sub_dfd, "quux_1_s390.raw", "waldo1", WRITE_STRING_FILE_CREATE) >= 0);
+        assert_se(write_string_file_at(sub_dfd, "quux_2_s390+4-6.raw", "waldo2", WRITE_STRING_FILE_CREATE) >= 0);
+        assert_se(write_string_file_at(sub_dfd, "quux_3_s390+0-10.raw", "waldo3", WRITE_STRING_FILE_CREATE) >= 0);
+
+        _cleanup_free_ char *pp = NULL;
+        pp = path_join(p, "foo.v");
+        assert_se(pp);
+
+        _cleanup_(pick_result_done) PickResult result = PICK_RESULT_NULL;
+
+        PickFilter filter = {
+                .architecture = _ARCHITECTURE_INVALID,
+                .suffix = ".raw",
+        };
+
+        if (IN_SET(native_architecture(), ARCHITECTURE_X86, ARCHITECTURE_X86_64)) {
+                assert_se(path_pick(NULL, AT_FDCWD, pp, &filter, PICK_ARCHITECTURE|PICK_TRIES, &result) > 0);
+                assert_se(S_ISREG(result.st.st_mode));
+                assert_se(streq_ptr(result.version, "99"));
+                assert_se(result.architecture == ARCHITECTURE_X86);
+                assert_se(endswith(result.path, "/foo_99_x86.raw"));
+
+                pick_result_done(&result);
+        }
+
+        filter.architecture = ARCHITECTURE_X86_64;
+        assert_se(path_pick(NULL, AT_FDCWD, pp, &filter, PICK_ARCHITECTURE|PICK_TRIES, &result) > 0);
+        assert_se(S_ISREG(result.st.st_mode));
+        assert_se(streq_ptr(result.version, "55"));
+        assert_se(result.architecture == ARCHITECTURE_X86_64);
+        assert_se(endswith(result.path, "/foo_55_x86-64.raw"));
+        pick_result_done(&result);
+
+        filter.architecture = ARCHITECTURE_IA64;
+        assert_se(path_pick(NULL, AT_FDCWD, pp, &filter, PICK_ARCHITECTURE|PICK_TRIES, &result) > 0);
+        assert_se(S_ISREG(result.st.st_mode));
+        assert_se(streq_ptr(result.version, "5"));
+        assert_se(result.architecture == ARCHITECTURE_IA64);
+        assert_se(endswith(result.path, "/foo_5_ia64.raw"));
+        pick_result_done(&result);
+
+        filter.architecture = _ARCHITECTURE_INVALID;
+        filter.version = "5";
+        assert_se(path_pick(NULL, AT_FDCWD, pp, &filter, PICK_ARCHITECTURE|PICK_TRIES, &result) > 0);
+        assert_se(S_ISREG(result.st.st_mode));
+        assert_se(streq_ptr(result.version, "5"));
+        if (native_architecture() != ARCHITECTURE_IA64) {
+                assert_se(result.architecture == _ARCHITECTURE_INVALID);
+                assert_se(endswith(result.path, "/foo_5.raw"));
+        }
+        pick_result_done(&result);
+
+        filter.architecture = ARCHITECTURE_IA64;
+        assert_se(path_pick(NULL, AT_FDCWD, pp, &filter, PICK_ARCHITECTURE|PICK_TRIES, &result) > 0);
+        assert_se(S_ISREG(result.st.st_mode));
+        assert_se(streq_ptr(result.version, "5"));
+        assert_se(result.architecture == ARCHITECTURE_IA64);
+        assert_se(endswith(result.path, "/foo_5_ia64.raw"));
+        pick_result_done(&result);
+
+        filter.architecture = ARCHITECTURE_CRIS;
+        assert_se(path_pick(NULL, AT_FDCWD, pp, &filter, PICK_ARCHITECTURE|PICK_TRIES, &result) == 0);
+        assert_se(result.st.st_mode == MODE_INVALID);
+        assert_se(!result.version);
+        assert_se(result.architecture < 0);
+        assert_se(!result.path);
+
+        assert_se(unlinkat(sub_dfd, "foo_99_x86.raw", 0) >= 0);
+
+        filter.architecture = _ARCHITECTURE_INVALID;
+        filter.version = NULL;
+        if (IN_SET(native_architecture(), ARCHITECTURE_X86_64, ARCHITECTURE_X86)) {
+                assert_se(path_pick(NULL, AT_FDCWD, pp, &filter, PICK_ARCHITECTURE|PICK_TRIES, &result) > 0);
+                assert_se(S_ISREG(result.st.st_mode));
+                assert_se(streq_ptr(result.version, "55"));
+
+                if (native_architecture() == ARCHITECTURE_X86_64) {
+                        assert_se(result.architecture == ARCHITECTURE_X86_64);
+                        assert_se(endswith(result.path, "/foo_55_x86-64.raw"));
+                } else {
+                        assert_se(result.architecture == ARCHITECTURE_X86);
+                        assert_se(endswith(result.path, "/foo_55_x86.raw"));
+                }
+                pick_result_done(&result);
+        }
+
+        /* Test explicit patterns in last component of path not being .v */
+        free(pp);
+        pp = path_join(p, "foo.v/foo___.raw");
+        assert_se(pp);
+
+        if (IN_SET(native_architecture(), ARCHITECTURE_X86, ARCHITECTURE_X86_64)) {
+                assert_se(path_pick(NULL, AT_FDCWD, pp, &filter, PICK_ARCHITECTURE|PICK_TRIES, &result) > 0);
+                assert_se(S_ISREG(result.st.st_mode));
+                assert_se(streq_ptr(result.version, "55"));
+                assert_se(result.architecture == native_architecture());
+                assert_se(endswith(result.path, ".raw"));
+                assert_se(strrstr(result.path, "/foo_55_x86"));
+                pick_result_done(&result);
+        }
+
+        /* Specify an explicit path */
+        free(pp);
+        pp = path_join(p, "foo.v/foo_5.raw");
+        assert_se(pp);
+
+        filter.type_mask = UINT32_C(1) << DT_DIR;
+        assert_se(path_pick(NULL, AT_FDCWD, pp, &filter, PICK_ARCHITECTURE|PICK_TRIES, &result) == -ENOTDIR);
+
+        filter.type_mask = UINT32_C(1) << DT_REG;
+        assert_se(path_pick(NULL, AT_FDCWD, pp, &filter, PICK_ARCHITECTURE|PICK_TRIES, &result) > 0);
+        assert_se(S_ISREG(result.st.st_mode));
+        assert_se(!result.version);
+        assert_se(result.architecture == _ARCHITECTURE_INVALID);
+        assert_se(path_equal(result.path, pp));
+        pick_result_done(&result);
+
+        free(pp);
+        pp = path_join(p, "foo.v");
+        assert_se(pp);
+
+        filter.architecture = ARCHITECTURE_S390;
+        filter.basename = "quux";
+
+        assert_se(path_pick(NULL, AT_FDCWD, pp, &filter, PICK_ARCHITECTURE|PICK_TRIES, &result) > 0);
+        assert_se(S_ISREG(result.st.st_mode));
+        assert_se(streq_ptr(result.version, "2"));
+        assert_se(result.tries_left == 4);
+        assert_se(result.tries_done == 6);
+        assert_se(endswith(result.path, "quux_2_s390+4-6.raw"));
+        assert_se(result.architecture == ARCHITECTURE_S390);
+}
+
+DEFINE_TEST_MAIN(LOG_DEBUG);
index 53c2a6fb710cd4afb1f1c19f51e9087d58537166..a7dcb832065a99d0b4275b7448f7b1b8f3f07548 100644 (file)
@@ -22,6 +22,7 @@
 #include "clock-util.h"
 #include "conf-files.h"
 #include "constants.h"
+#include "daemon-util.h"
 #include "fd-util.h"
 #include "fileio-label.h"
 #include "fileio.h"
@@ -119,7 +120,7 @@ static void context_clear(Context *c) {
         assert(c);
 
         free(c->zone);
-        bus_verify_polkit_async_registry_free(c->polkit_registry);
+        hashmap_free(c->polkit_registry);
         sd_bus_message_unref(c->cache);
 
         sd_bus_slot_unref(c->slot_job_removed);
@@ -1118,21 +1119,15 @@ static int run(int argc, char *argv[]) {
 
         umask(0022);
 
-        assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, -1) >= 0);
-
         r = sd_event_default(&event);
         if (r < 0)
                 return log_error_errno(r, "Failed to allocate event loop: %m");
 
         (void) sd_event_set_watchdog(event, true);
 
-        r = sd_event_add_signal(event, NULL, SIGINT, NULL, NULL);
-        if (r < 0)
-                return log_error_errno(r, "Failed to install SIGINT handler: %m");
-
-        r = sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL);
+        r = sd_event_set_signal_exit(event, true);
         if (r < 0)
-                return log_error_errno(r, "Failed to install SIGTERM handler: %m");
+                return log_error_errno(r, "Failed to install SIGINT/SIGTERM handlers: %m");
 
         r = connect_bus(&context, event, &bus);
         if (r < 0)
@@ -1148,6 +1143,10 @@ static int run(int argc, char *argv[]) {
         if (r < 0)
                 return r;
 
+        r = sd_notify(false, NOTIFY_READY);
+        if (r < 0)
+                log_warning_errno(r, "Failed to send readiness notification, ignoring: %m");
+
         r = bus_event_loop_with_idle(event, bus, "org.freedesktop.timedate1", DEFAULT_EXIT_USEC, NULL, NULL);
         if (r < 0)
                 return log_error_errno(r, "Failed to run event loop: %m");
index 1317bc0f76421176cff647c1e5b901290da58d13..bb37de9f2534838a3b47ffb28e68d4c20cd22f0a 100644 (file)
@@ -658,8 +658,7 @@ static int manager_listen_setup(Manager *m) {
         if (r < 0)
                 return r;
 
-        if (addr.sa.sa_family == AF_INET)
-                (void) setsockopt_int(m->server_socket, IPPROTO_IP, IP_TOS, IPTOS_LOWDELAY);
+        (void) socket_set_option(m->server_socket, addr.sa.sa_family, IP_TOS, IPV6_TCLASS, IPTOS_DSCP_EF);
 
         return sd_event_add_io(m->event, &m->event_receive, m->server_socket, EPOLLIN, manager_receive_response, m);
 }
@@ -962,7 +961,7 @@ Manager* manager_free(Manager *m) {
 
         sd_bus_flush_close_unref(m->bus);
 
-        bus_verify_polkit_async_registry_free(m->polkit_registry);
+        hashmap_free(m->polkit_registry);
 
         return mfree(m);
 }
index 230ec09b97904b485c056fc791b345ca79a914c8..de785370ed12dd95bcd37a3a1e266074eee94644 100644 (file)
@@ -45,7 +45,6 @@
 #include "log.h"
 #include "macro.h"
 #include "main-func.h"
-#include "missing_stat.h"
 #include "missing_syscall.h"
 #include "mkdir-label.h"
 #include "mount-util.h"
@@ -64,6 +63,7 @@
 #include "set.h"
 #include "sort-util.h"
 #include "specifier.h"
+#include "stat-util.h"
 #include "stdio-util.h"
 #include "string-table.h"
 #include "string-util.h"
@@ -82,6 +82,7 @@ typedef enum OperationMask {
         OPERATION_CREATE = 1 << 0,
         OPERATION_REMOVE = 1 << 1,
         OPERATION_CLEAN  = 1 << 2,
+        OPERATION_PURGE  = 1 << 3,
 } OperationMask;
 
 typedef enum ItemType {
@@ -217,8 +218,8 @@ typedef struct Context {
         Set *unix_sockets;
 } Context;
 
-STATIC_DESTRUCTOR_REGISTER(arg_include_prefixes, freep);
-STATIC_DESTRUCTOR_REGISTER(arg_exclude_prefixes, freep);
+STATIC_DESTRUCTOR_REGISTER(arg_include_prefixes, strv_freep);
+STATIC_DESTRUCTOR_REGISTER(arg_exclude_prefixes, strv_freep);
 STATIC_DESTRUCTOR_REGISTER(arg_root, freep);
 STATIC_DESTRUCTOR_REGISTER(arg_image, freep);
 STATIC_DESTRUCTOR_REGISTER(arg_image_policy, image_policy_freep);
@@ -378,23 +379,41 @@ static int user_config_paths(char*** ret) {
         return 0;
 }
 
+static bool needs_purge(ItemType t) {
+        return IN_SET(t,
+                      COPY_FILES,
+                      TRUNCATE_FILE,
+                      CREATE_FILE,
+                      WRITE_FILE,
+                      EMPTY_DIRECTORY,
+                      CREATE_SUBVOLUME,
+                      CREATE_SUBVOLUME_INHERIT_QUOTA,
+                      CREATE_SUBVOLUME_NEW_QUOTA,
+                      CREATE_CHAR_DEVICE,
+                      CREATE_BLOCK_DEVICE,
+                      CREATE_SYMLINK,
+                      CREATE_FIFO,
+                      CREATE_DIRECTORY,
+                      TRUNCATE_DIRECTORY);
+}
+
 static bool needs_glob(ItemType t) {
         return IN_SET(t,
                       WRITE_FILE,
-                      IGNORE_PATH,
-                      IGNORE_DIRECTORY_PATH,
-                      REMOVE_PATH,
-                      RECURSIVE_REMOVE_PATH,
                       EMPTY_DIRECTORY,
-                      ADJUST_MODE,
-                      RELABEL_PATH,
-                      RECURSIVE_RELABEL_PATH,
                       SET_XATTR,
                       RECURSIVE_SET_XATTR,
                       SET_ACL,
                       RECURSIVE_SET_ACL,
                       SET_ATTRIBUTE,
-                      RECURSIVE_SET_ATTRIBUTE);
+                      RECURSIVE_SET_ATTRIBUTE,
+                      IGNORE_PATH,
+                      IGNORE_DIRECTORY_PATH,
+                      REMOVE_PATH,
+                      RECURSIVE_REMOVE_PATH,
+                      RELABEL_PATH,
+                      RECURSIVE_RELABEL_PATH,
+                      ADJUST_MODE);
 }
 
 static bool takes_ownership(ItemType t) {
@@ -402,7 +421,6 @@ static bool takes_ownership(ItemType t) {
                       CREATE_FILE,
                       TRUNCATE_FILE,
                       CREATE_DIRECTORY,
-                      EMPTY_DIRECTORY,
                       TRUNCATE_DIRECTORY,
                       CREATE_SUBVOLUME,
                       CREATE_SUBVOLUME_INHERIT_QUOTA,
@@ -413,6 +431,7 @@ static bool takes_ownership(ItemType t) {
                       CREATE_BLOCK_DEVICE,
                       COPY_FILES,
                       WRITE_FILE,
+                      EMPTY_DIRECTORY,
                       IGNORE_PATH,
                       IGNORE_DIRECTORY_PATH,
                       REMOVE_PATH,
@@ -537,18 +556,6 @@ static DIR* opendir_nomod(const char *path) {
         return xopendirat_nomod(AT_FDCWD, path);
 }
 
-static nsec_t load_statx_timestamp_nsec(const struct statx_timestamp *ts) {
-        assert(ts);
-
-        if (ts->tv_sec < 0)
-                return NSEC_INFINITY;
-
-        if ((nsec_t) ts->tv_sec >= (UINT64_MAX - ts->tv_nsec) / NSEC_PER_SEC)
-                return NSEC_INFINITY;
-
-        return ts->tv_sec * NSEC_PER_SEC + ts->tv_nsec;
-}
-
 static bool needs_cleanup(
                 nsec_t atime,
                 nsec_t btime,
@@ -691,10 +698,10 @@ static int dir_cleanup(
                         }
                 }
 
-                atime_nsec = FLAGS_SET(sx.stx_mask, STATX_ATIME) ? load_statx_timestamp_nsec(&sx.stx_atime) : 0;
-                mtime_nsec = FLAGS_SET(sx.stx_mask, STATX_MTIME) ? load_statx_timestamp_nsec(&sx.stx_mtime) : 0;
-                ctime_nsec = FLAGS_SET(sx.stx_mask, STATX_CTIME) ? load_statx_timestamp_nsec(&sx.stx_ctime) : 0;
-                btime_nsec = FLAGS_SET(sx.stx_mask, STATX_BTIME) ? load_statx_timestamp_nsec(&sx.stx_btime) : 0;
+                atime_nsec = FLAGS_SET(sx.stx_mask, STATX_ATIME) ? statx_timestamp_load_nsec(&sx.stx_atime) : 0;
+                mtime_nsec = FLAGS_SET(sx.stx_mask, STATX_MTIME) ? statx_timestamp_load_nsec(&sx.stx_mtime) : 0;
+                ctime_nsec = FLAGS_SET(sx.stx_mask, STATX_CTIME) ? statx_timestamp_load_nsec(&sx.stx_ctime) : 0;
+                btime_nsec = FLAGS_SET(sx.stx_mask, STATX_BTIME) ? statx_timestamp_load_nsec(&sx.stx_btime) : 0;
 
                 sub_path = path_join(p, de->d_name);
                 if (!sub_path) {
@@ -2839,6 +2846,33 @@ static int create_item(Context *c, Item *i) {
         return 0;
 }
 
+static int purge_item_instance(Context *c, Item *i, const char *instance, CreationMode creation) {
+        int r;
+
+        /* FIXME: we probably should use dir_cleanup() here instead of rm_rf() so that 'x' is honoured. */
+        log_debug("rm -rf \"%s\"", instance);
+        r = rm_rf(instance, REMOVE_ROOT|REMOVE_SUBVOLUME|REMOVE_PHYSICAL);
+        if (r < 0 && r != -ENOENT)
+                return log_error_errno(r, "rm_rf(%s): %m", instance);
+
+        return 0;
+}
+
+static int purge_item(Context *c, Item *i) {
+
+        assert(i);
+
+        if (!needs_purge(i->type))
+                return 0;
+
+        log_debug("Running purge owned action for entry %c %s", (char) i->type, i->path);
+
+        if (needs_glob(i->type))
+                return glob_item(c, i, purge_item_instance);
+
+        return purge_item_instance(c, i, i->path, CREATION_EXISTING);
+}
+
 static int remove_item_instance(
                 Context *c,
                 Item *i,
@@ -2988,8 +3022,8 @@ static int clean_item_instance(
         }
 
         return dir_cleanup(c, i, instance, d,
-                           load_statx_timestamp_nsec(&sx.stx_atime),
-                           load_statx_timestamp_nsec(&sx.stx_mtime),
+                           statx_timestamp_load_nsec(&sx.stx_atime),
+                           statx_timestamp_load_nsec(&sx.stx_mtime),
                            cutoff * NSEC_PER_USEC,
                            sx.stx_dev_major, sx.stx_dev_minor, mountpoint,
                            MAX_DEPTH, i->keep_first_level,
@@ -3005,16 +3039,16 @@ static int clean_item(Context *c, Item *i) {
         switch (i->type) {
 
         case CREATE_DIRECTORY:
+        case TRUNCATE_DIRECTORY:
         case CREATE_SUBVOLUME:
         case CREATE_SUBVOLUME_INHERIT_QUOTA:
         case CREATE_SUBVOLUME_NEW_QUOTA:
-        case TRUNCATE_DIRECTORY:
-        case IGNORE_PATH:
         case COPY_FILES:
                 clean_item_instance(c, i, i->path, CREATION_EXISTING);
                 return 0;
 
         case EMPTY_DIRECTORY:
+        case IGNORE_PATH:
         case IGNORE_DIRECTORY_PATH:
                 return glob_item(c, i, clean_item_instance);
 
@@ -3031,7 +3065,7 @@ static int process_item(
         OperationMask todo;
         _cleanup_free_ char *_path = NULL;
         const char *path;
-        int r, q, p;
+        int r;
 
         assert(c);
         assert(i);
@@ -3067,12 +3101,11 @@ static int process_item(
         if (i->allow_failure)
                 r = 0;
 
-        q = FLAGS_SET(operation, OPERATION_REMOVE) ? remove_item(c, i) : 0;
-        p = FLAGS_SET(operation, OPERATION_CLEAN) ? clean_item(c, i) : 0;
+        RET_GATHER(r, FLAGS_SET(operation, OPERATION_REMOVE) ? remove_item(c, i) : 0);
+        RET_GATHER(r, FLAGS_SET(operation, OPERATION_CLEAN) ? clean_item(c, i) : 0);
+        RET_GATHER(r, FLAGS_SET(operation, OPERATION_PURGE) ? purge_item(c, i) : 0);
 
-        return r < 0 ? r :
-                q < 0 ? q :
-                p;
+        return r;
 }
 
 static int process_item_array(
@@ -3091,13 +3124,13 @@ static int process_item_array(
                 r = process_item_array(c, array->parent, operation & OPERATION_CREATE);
 
         /* Clean up all children first */
-        if ((operation & (OPERATION_REMOVE|OPERATION_CLEAN)) && !set_isempty(array->children)) {
+        if ((operation & (OPERATION_REMOVE|OPERATION_CLEAN|OPERATION_PURGE)) && !set_isempty(array->children)) {
                 ItemArray *cc;
 
                 SET_FOREACH(cc, array->children) {
                         int k;
 
-                        k = process_item_array(c, cc, operation & (OPERATION_REMOVE|OPERATION_CLEAN));
+                        k = process_item_array(c, cc, operation & (OPERATION_REMOVE|OPERATION_CLEAN|OPERATION_PURGE));
                         if (k < 0 && r == 0)
                                 r = k;
                 }
@@ -3762,7 +3795,8 @@ static int parse_line(
                 _cleanup_free_ void *data = NULL;
                 size_t data_size = 0;
 
-                r = unbase64mem(item_binary_argument(&i), item_binary_argument_size(&i), &data, &data_size);
+                r = unbase64mem_full(item_binary_argument(&i), item_binary_argument_size(&i), /* secure = */ false,
+                                     &data, &data_size);
                 if (r < 0)
                         return log_syntax(NULL, LOG_ERR, fname, line, r, "Failed to base64 decode specified argument '%s': %m", i.argument);
 
@@ -3982,6 +4016,7 @@ static int help(void) {
                "     --remove               Remove marked files/directories\n"
                "     --boot                 Execute actions only safe at boot\n"
                "     --graceful             Quietly ignore unknown users or groups\n"
+               "     --purge                Delete all files owned by the configuration files\n"
                "     --prefix=PATH          Only apply rules with the specified prefix\n"
                "     --exclude-prefix=PATH  Ignore rules with the specified prefix\n"
                "  -E                        Ignore rules prefixed with /dev, /proc, /run, /sys\n"
@@ -4009,6 +4044,7 @@ static int parse_argv(int argc, char *argv[]) {
                 ARG_CREATE,
                 ARG_CLEAN,
                 ARG_REMOVE,
+                ARG_PURGE,
                 ARG_BOOT,
                 ARG_GRACEFUL,
                 ARG_PREFIX,
@@ -4029,6 +4065,7 @@ static int parse_argv(int argc, char *argv[]) {
                 { "create",         no_argument,         NULL, ARG_CREATE         },
                 { "clean",          no_argument,         NULL, ARG_CLEAN          },
                 { "remove",         no_argument,         NULL, ARG_REMOVE         },
+                { "purge",          no_argument,         NULL, ARG_PURGE          },
                 { "boot",           no_argument,         NULL, ARG_BOOT           },
                 { "graceful",       no_argument,         NULL, ARG_GRACEFUL       },
                 { "prefix",         required_argument,   NULL, ARG_PREFIX         },
@@ -4084,17 +4121,21 @@ static int parse_argv(int argc, char *argv[]) {
                         arg_boot = true;
                         break;
 
+                case ARG_PURGE:
+                        arg_operation |= OPERATION_PURGE;
+                        break;
+
                 case ARG_GRACEFUL:
                         arg_graceful = true;
                         break;
 
                 case ARG_PREFIX:
-                        if (strv_push(&arg_include_prefixes, optarg) < 0)
+                        if (strv_extend(&arg_include_prefixes, optarg) < 0)
                                 return log_oom();
                         break;
 
                 case ARG_EXCLUDE_PREFIX:
-                        if (strv_push(&arg_exclude_prefixes, optarg) < 0)
+                        if (strv_extend(&arg_exclude_prefixes, optarg) < 0)
                                 return log_oom();
                         break;
 
@@ -4151,7 +4192,7 @@ static int parse_argv(int argc, char *argv[]) {
 
         if (arg_operation == 0 && arg_cat_flags == CAT_CONFIG_OFF)
                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
-                                       "You need to specify at least one of --clean, --create, or --remove.");
+                                       "You need to specify at least one of --clean, --create, --remove, or --purge.");
 
         if (arg_replace && arg_cat_flags != CAT_CONFIG_OFF)
                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
@@ -4402,6 +4443,7 @@ static int run(int argc, char *argv[]) {
         bool invalid_config = false;
         ItemArray *a;
         enum {
+                PHASE_PURGE,
                 PHASE_REMOVE_AND_CLEAN,
                 PHASE_CREATE,
                 _PHASE_MAX
@@ -4537,7 +4579,9 @@ static int run(int argc, char *argv[]) {
         for (phase = 0; phase < _PHASE_MAX; phase++) {
                 OperationMask op;
 
-                if (phase == PHASE_REMOVE_AND_CLEAN)
+                if (phase == PHASE_PURGE)
+                        op = arg_operation & OPERATION_PURGE;
+                else if (phase == PHASE_REMOVE_AND_CLEAN)
                         op = arg_operation & (OPERATION_REMOVE|OPERATION_CLEAN);
                 else if (phase == PHASE_CREATE)
                         op = arg_operation & OPERATION_CREATE;
index 1ba8a7fc93bc89a91bdcbf0a1af03a59a9c02bbe..723b31898a923e066e6089527ab5ffc5434b999e 100644 (file)
@@ -1,10 +1,10 @@
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 
 #include "generator.h"
+#include "parse-util.h"
 #include "proc-cmdline.h"
 #include "special.h"
 #include "tpm2-util.h"
-#include "parse-util.h"
 
 /* A small generator that enqueues tpm2.target as synchronization point if the TPM2 device hasn't shown up
  * yet, but the firmware reports it to exist. This is supposed to deal with systems where the TPM2 driver
index 240f16e251130ceeb0daafb1def7d44ef5401ee1..42d7cc7ee21a721b1439e00d8c158300cb811b44 100644 (file)
@@ -38,6 +38,9 @@ Match.Credential,                          config_parse_net_condition,
 Match.Architecture,                        config_parse_net_condition,            CONDITION_ARCHITECTURE,        offsetof(LinkConfig, conditions)
 Match.Firmware,                            config_parse_net_condition,            CONDITION_FIRMWARE,            offsetof(LinkConfig, conditions)
 Link.Description,                          config_parse_string,                   0,                             offsetof(LinkConfig, description)
+Link.Property,                             config_parse_udev_property,            0,                             offsetof(LinkConfig, properties)
+Link.ImportProperty,                       config_parse_udev_property_name,       0,                             offsetof(LinkConfig, import_properties)
+Link.UnsetProperty,                        config_parse_udev_property_name,       0,                             offsetof(LinkConfig, unset_properties)
 Link.MACAddressPolicy,                     config_parse_mac_address_policy,       0,                             offsetof(LinkConfig, mac_address_policy)
 Link.MACAddress,                           config_parse_hw_addr,                  0,                             offsetof(LinkConfig, hw_addr)
 Link.NamePolicy,                           config_parse_name_policy,              0,                             offsetof(LinkConfig, name_policy)
index 910ec2709e5827da0e7f128f6730c12dd1f447a8..a8b2cc23a2c57747030de4f73c63b636c59f14c8 100644 (file)
@@ -15,6 +15,8 @@
 #include "creds-util.h"
 #include "device-private.h"
 #include "device-util.h"
+#include "env-util.h"
+#include "escape.h"
 #include "ethtool-util.h"
 #include "fd-util.h"
 #include "fileio.h"
 #include "path-util.h"
 #include "proc-cmdline.h"
 #include "random-util.h"
+#include "specifier.h"
 #include "stat-util.h"
 #include "string-table.h"
 #include "string-util.h"
 #include "strv.h"
+#include "udev-builtin.h"
 #include "utf8.h"
 
+static const Specifier link_specifier_table[] = {
+        COMMON_SYSTEM_SPECIFIERS,
+        COMMON_TMP_SPECIFIERS,
+        {}
+};
+
 struct LinkConfigContext {
         LIST_HEAD(LinkConfig, configs);
         int ethtool_fd;
@@ -53,6 +63,9 @@ static LinkConfig* link_config_free(LinkConfig *config) {
         condition_free_list(config->conditions);
 
         free(config->description);
+        strv_free(config->properties);
+        strv_free(config->import_properties);
+        strv_free(config->unset_properties);
         free(config->name_policy);
         free(config->name);
         strv_free(config->alternative_names);
@@ -363,18 +376,20 @@ Link *link_free(Link *link) {
                 return NULL;
 
         sd_device_unref(link->device);
+        sd_device_unref(link->device_db_clone);
         free(link->kind);
         strv_free(link->altnames);
         return mfree(link);
 }
 
-int link_new(LinkConfigContext *ctx, sd_netlink **rtnl, sd_device *device, Link **ret) {
+int link_new(LinkConfigContext *ctx, sd_netlink **rtnl, sd_device *device, sd_device *device_db_clone, Link **ret) {
         _cleanup_(link_freep) Link *link = NULL;
         int r;
 
         assert(ctx);
         assert(rtnl);
         assert(device);
+        assert(device_db_clone);
         assert(ret);
 
         link = new(Link, 1);
@@ -383,6 +398,7 @@ int link_new(LinkConfigContext *ctx, sd_netlink **rtnl, sd_device *device, Link
 
         *link = (Link) {
                 .device = sd_device_ref(device),
+                .device_db_clone = sd_device_ref(device_db_clone),
         };
 
         r = sd_device_get_sysname(device, &link->ifname);
@@ -921,21 +937,69 @@ static int link_apply_sr_iov_config(Link *link, sd_netlink **rtnl) {
         return 0;
 }
 
-int link_apply_config(LinkConfigContext *ctx, sd_netlink **rtnl, Link *link) {
-        int r;
+static int link_apply_udev_properties(Link *link, bool test) {
+        LinkConfig *config;
+        sd_device *device;
 
-        assert(ctx);
-        assert(rtnl);
         assert(link);
 
-        if (!IN_SET(link->action, SD_DEVICE_ADD, SD_DEVICE_BIND, SD_DEVICE_MOVE)) {
-                log_link_debug(link, "Skipping to apply .link settings on '%s' uevent.",
-                               device_action_to_string(link->action));
+        config = ASSERT_PTR(link->config);
+        device = ASSERT_PTR(link->device);
 
-                link->new_name = link->ifname;
-                return 0;
+        /* 1. apply ImportProperty=. */
+        STRV_FOREACH(p, config->import_properties)
+                (void) udev_builtin_import_property(device, link->device_db_clone, test, *p);
+
+        /* 2. apply Property=. */
+        STRV_FOREACH(p, config->properties) {
+                _cleanup_free_ char *key = NULL;
+                const char *eq;
+
+                eq = strchr(*p, '=');
+                if (!eq)
+                        continue;
+
+                key = strndup(*p, eq - *p);
+                if (!key)
+                        return log_oom();
+
+                (void) udev_builtin_add_property(device, test, key, eq + 1);
         }
 
+        /* 3. apply UnsetProperty=. */
+        STRV_FOREACH(p, config->unset_properties)
+                (void) udev_builtin_add_property(device, test, *p, NULL);
+
+        /* 4. set the default properties. */
+        (void) udev_builtin_add_property(device, test, "ID_NET_LINK_FILE", config->filename);
+
+        _cleanup_free_ char *joined = NULL;
+        STRV_FOREACH(d, config->dropins) {
+                _cleanup_free_ char *escaped = NULL;
+
+                escaped = xescape(*d, ":");
+                if (!escaped)
+                        return log_oom();
+
+                if (!strextend_with_separator(&joined, ":", escaped))
+                        return log_oom();
+        }
+
+        (void) udev_builtin_add_property(device, test, "ID_NET_LINK_FILE_DROPINS", joined);
+
+        if (link->new_name)
+                (void) udev_builtin_add_property(device, test, "ID_NET_NAME", link->new_name);
+
+        return 0;
+}
+
+int link_apply_config(LinkConfigContext *ctx, sd_netlink **rtnl, Link *link, bool test) {
+        int r;
+
+        assert(ctx);
+        assert(rtnl);
+        assert(link);
+
         r = link_apply_ethtool_settings(link, &ctx->ethtool_fd);
         if (r < 0)
                 return r;
@@ -956,9 +1020,149 @@ int link_apply_config(LinkConfigContext *ctx, sd_netlink **rtnl, Link *link) {
         if (r < 0)
                 return r;
 
+        r = link_apply_udev_properties(link, test);
+        if (r < 0)
+                return r;
+
         return 0;
 }
 
+int config_parse_udev_property(
+                const char *unit,
+                const char *filename,
+                unsigned line,
+                const char *section,
+                unsigned section_line,
+                const char *lvalue,
+                int ltype,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
+
+        char ***properties = ASSERT_PTR(data);
+        int r;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+
+        if (isempty(rvalue)) {
+                /* Empty assignment resets the list */
+                *properties = strv_free(*properties);
+                return 0;
+        }
+
+        for (const char *p = rvalue;; ) {
+                _cleanup_free_ char *word = NULL, *resolved = NULL, *key = NULL;
+                const char *eq;
+
+                r = extract_first_word(&p, &word, NULL, EXTRACT_CUNESCAPE|EXTRACT_UNQUOTE);
+                if (r == -ENOMEM)
+                        return log_oom();
+                if (r < 0) {
+                        log_syntax(unit, LOG_WARNING, filename, line, r,
+                                   "Invalid syntax, ignoring assignment: %s", rvalue);
+                        return 0;
+                }
+                if (r == 0)
+                        return 0;
+
+                r = specifier_printf(word, SIZE_MAX, link_specifier_table, NULL, NULL, &resolved);
+                if (r < 0) {
+                        log_syntax(unit, LOG_WARNING, filename, line, r,
+                                   "Failed to resolve specifiers in %s, ignoring assignment: %m", word);
+                        continue;
+                }
+
+                /* The restriction for udev property is not clear. Let's apply the one for environment variable here. */
+                if (!env_assignment_is_valid(resolved)) {
+                        log_syntax(unit, LOG_WARNING, filename, line, 0,
+                                   "Invalid udev property, ignoring assignment: %s", word);
+                        continue;
+                }
+
+                assert_se(eq = strchr(resolved, '='));
+                key = strndup(resolved, eq - resolved);
+                if (!key)
+                        return log_oom();
+
+                if (!device_property_can_set(key)) {
+                        log_syntax(unit, LOG_WARNING, filename, line, 0,
+                                   "Invalid udev property name '%s', ignoring assignment: %s", key, resolved);
+                        continue;
+                }
+
+                r = strv_env_replace_consume(properties, TAKE_PTR(resolved));
+                if (r < 0)
+                        return log_error_errno(r, "Failed to update properties: %m");
+        }
+}
+
+int config_parse_udev_property_name(
+                const char *unit,
+                const char *filename,
+                unsigned line,
+                const char *section,
+                unsigned section_line,
+                const char *lvalue,
+                int ltype,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
+
+        char ***properties = ASSERT_PTR(data);
+        int r;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+
+        if (isempty(rvalue)) {
+                /* Empty assignment resets the list */
+                *properties = strv_free(*properties);
+                return 0;
+        }
+
+        for (const char *p = rvalue;; ) {
+                _cleanup_free_ char *word = NULL, *resolved = NULL;
+
+                r = extract_first_word(&p, &word, NULL, EXTRACT_CUNESCAPE|EXTRACT_UNQUOTE);
+                if (r == -ENOMEM)
+                        return log_oom();
+                if (r < 0) {
+                        log_syntax(unit, LOG_WARNING, filename, line, r,
+                                   "Invalid syntax, ignoring assignment: %s", rvalue);
+                        return 0;
+                }
+                if (r == 0)
+                        return 0;
+
+                r = specifier_printf(word, SIZE_MAX, link_specifier_table, NULL, NULL, &resolved);
+                if (r < 0) {
+                        log_syntax(unit, LOG_WARNING, filename, line, r,
+                                   "Failed to resolve specifiers in %s, ignoring assignment: %m", word);
+                        continue;
+                }
+
+                /* The restriction for udev property is not clear. Let's apply the one for environment variable here. */
+                if (!env_name_is_valid(resolved)) {
+                        log_syntax(unit, LOG_WARNING, filename, line, 0,
+                                   "Invalid udev property name, ignoring assignment: %s", resolved);
+                        continue;
+                }
+
+                if (!device_property_can_set(resolved)) {
+                        log_syntax(unit, LOG_WARNING, filename, line, 0,
+                                   "Invalid udev property name, ignoring assignment: %s", resolved);
+                        continue;
+                }
+
+                r = strv_consume(properties, TAKE_PTR(resolved));
+                if (r < 0)
+                        return log_error_errno(r, "Failed to update properties: %m");
+        }
+}
+
 int config_parse_ifalias(
                 const char *unit,
                 const char *filename,
index bab9d12970a024a01ced97056fb73ee63b56dc4f..98cadc212e1fe9fe8380843eda3ba5ccc595a386 100644 (file)
@@ -31,6 +31,7 @@ typedef struct Link {
 
         LinkConfig *config;
         sd_device *device;
+        sd_device *device_db_clone;
         sd_device_action_t action;
 
         char *kind;
@@ -51,6 +52,9 @@ struct LinkConfig {
         LIST_HEAD(Condition, conditions);
 
         char *description;
+        char **properties;
+        char **import_properties;
+        char **unset_properties;
         struct hw_addr_data hw_addr;
         MACAddressPolicy mac_address_policy;
         NamePolicy *name_policy;
@@ -95,12 +99,12 @@ int link_load_one(LinkConfigContext *ctx, const char *filename);
 int link_config_load(LinkConfigContext *ctx);
 bool link_config_should_reload(LinkConfigContext *ctx);
 
-int link_new(LinkConfigContext *ctx, sd_netlink **rtnl, sd_device *device, Link **ret);
+int link_new(LinkConfigContext *ctx, sd_netlink **rtnl, sd_device *device, sd_device *device_db_clone, Link **ret);
 Link *link_free(Link *link);
 DEFINE_TRIVIAL_CLEANUP_FUNC(Link*, link_free);
 
 int link_get_config(LinkConfigContext *ctx, Link *link);
-int link_apply_config(LinkConfigContext *ctx, sd_netlink **rtnl, Link *link);
+int link_apply_config(LinkConfigContext *ctx, sd_netlink **rtnl, Link *link, bool test);
 
 const char *mac_address_policy_to_string(MACAddressPolicy p) _const_;
 MACAddressPolicy mac_address_policy_from_string(const char *p) _pure_;
@@ -108,6 +112,8 @@ MACAddressPolicy mac_address_policy_from_string(const char *p) _pure_;
 /* gperf lookup function */
 const struct ConfigPerfItem* link_config_gperf_lookup(const char *key, GPERF_LEN_TYPE length);
 
+CONFIG_PARSER_PROTOTYPE(config_parse_udev_property);
+CONFIG_PARSER_PROTOTYPE(config_parse_udev_property_name);
 CONFIG_PARSER_PROTOTYPE(config_parse_ifalias);
 CONFIG_PARSER_PROTOTYPE(config_parse_rx_tx_queues);
 CONFIG_PARSER_PROTOTYPE(config_parse_txqueuelen);
index a308a211fb0d99298ac9bc077536574633812497..fc614443efadcd69730ee03bdefe3374f76f6076 100644 (file)
@@ -1,8 +1,8 @@
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 
 #include "alloc-util.h"
+#include "device-private.h"
 #include "device-util.h"
-#include "escape.h"
 #include "errno-util.h"
 #include "link-config.h"
 #include "log.h"
@@ -15,13 +15,33 @@ static LinkConfigContext *ctx = NULL;
 static int builtin_net_setup_link(UdevEvent *event, int argc, char **argv, bool test) {
         sd_device *dev = ASSERT_PTR(ASSERT_PTR(event)->dev);
         _cleanup_(link_freep) Link *link = NULL;
-        _cleanup_free_ char *joined = NULL;
         int r;
 
         if (argc > 1)
                 return log_device_error_errno(dev, SYNTHETIC_ERRNO(EINVAL), "This program takes no arguments.");
 
-        r = link_new(ctx, &event->rtnl, dev, &link);
+        sd_device_action_t action;
+        r = sd_device_get_action(dev, &action);
+        if (r < 0)
+                return log_device_error_errno(dev, r, "Failed to get action: %m");
+
+        if (!IN_SET(action, SD_DEVICE_ADD, SD_DEVICE_BIND, SD_DEVICE_MOVE)) {
+                log_device_debug(dev, "Skipping to apply .link settings on '%s' uevent.",
+                                 device_action_to_string(action));
+
+                /* Import previously assigned .link file name. */
+                (void) udev_builtin_import_property(dev, event->dev_db_clone, test, "ID_NET_LINK_FILE");
+                (void) udev_builtin_import_property(dev, event->dev_db_clone, test, "ID_NET_LINK_FILE_DROPINS");
+
+                /* Set ID_NET_NAME= with the current interface name. */
+                const char *value;
+                if (sd_device_get_sysname(dev, &value) >= 0)
+                        (void) udev_builtin_add_property(dev, test, "ID_NET_NAME", value);
+
+                return 0;
+        }
+
+        r = link_new(ctx, &event->rtnl, dev, event->dev_db_clone, &link);
         if (r == -ENODEV) {
                 log_device_debug_errno(dev, r, "Link vanished while getting information, ignoring.");
                 return 0;
@@ -39,31 +59,14 @@ static int builtin_net_setup_link(UdevEvent *event, int argc, char **argv, bool
                 return log_device_error_errno(dev, r, "Failed to get link config: %m");
         }
 
-        r = link_apply_config(ctx, &event->rtnl, link);
+        r = link_apply_config(ctx, &event->rtnl, link, test);
         if (r == -ENODEV)
                 log_device_debug_errno(dev, r, "Link vanished while applying configuration, ignoring.");
         else if (r < 0)
                 log_device_warning_errno(dev, r, "Could not apply link configuration, ignoring: %m");
 
-        udev_builtin_add_property(dev, test, "ID_NET_LINK_FILE", link->config->filename);
-        if (link->new_name)
-                udev_builtin_add_property(dev, test, "ID_NET_NAME", link->new_name);
-
         event->altnames = TAKE_PTR(link->altnames);
 
-        STRV_FOREACH(d, link->config->dropins) {
-                _cleanup_free_ char *escaped = NULL;
-
-                escaped = xescape(*d, ":");
-                if (!escaped)
-                        return log_oom();
-
-                if (!strextend_with_separator(&joined, ":", escaped))
-                        return log_oom();
-        }
-
-        udev_builtin_add_property(dev, test, "ID_NET_LINK_FILE_DROPINS", joined);
-
         return 0;
 }
 
index ebeadc3f3d105d52a48cb85ff266f5f9de0d7b14..c4057d63c35dfedf14044147b2c34756ad8a1367 100644 (file)
@@ -644,7 +644,6 @@ static int find_real_nvme_parent(sd_device *dev, sd_device **ret) {
 
 static void add_id_with_usb_revision(sd_device *dev, bool test, char *path) {
         char *p;
-        int r;
 
         assert(dev);
         assert(path);
@@ -660,9 +659,7 @@ static void add_id_with_usb_revision(sd_device *dev, bool test, char *path) {
         if (p[1] != '-')
                 return;
 
-        r = udev_builtin_add_property(dev, test, "ID_PATH_WITH_USB_REVISION", path);
-        if (r < 0)
-                log_device_debug_errno(dev, r, "Failed to add ID_PATH_WITH_USB_REVISION property, ignoring: %m");
+        (void) udev_builtin_add_property(dev, test, "ID_PATH_WITH_USB_REVISION", path);
 
         /* Drop the USB revision specifier for backward compatibility. */
         memmove(p - 1, p + 1, strlen(p + 1) + 1);
@@ -671,7 +668,6 @@ static void add_id_with_usb_revision(sd_device *dev, bool test, char *path) {
 static void add_id_tag(sd_device *dev, bool test, const char *path) {
         char tag[UDEV_NAME_SIZE];
         size_t i = 0;
-        int r;
 
         /* compose valid udev tag name */
         for (const char *p = path; *p; p++) {
@@ -697,9 +693,7 @@ static void add_id_tag(sd_device *dev, bool test, const char *path) {
                 i--;
         tag[i] = '\0';
 
-        r = udev_builtin_add_property(dev, test, "ID_PATH_TAG", tag);
-        if (r < 0)
-                log_device_debug_errno(dev, r, "Failed to add ID_PATH_TAG property, ignoring: %m");
+        (void) udev_builtin_add_property(dev, test, "ID_PATH_TAG", tag);
 }
 
 static int builtin_path_id(UdevEvent *event, int argc, char *argv[], bool test) {
@@ -859,9 +853,7 @@ static int builtin_path_id(UdevEvent *event, int argc, char *argv[], bool test)
 
         add_id_with_usb_revision(dev, test, path);
 
-        r = udev_builtin_add_property(dev, test, "ID_PATH", path);
-        if (r < 0)
-                log_device_debug_errno(dev, r, "Failed to add ID_PATH property, ignoring: %m");
+        (void) udev_builtin_add_property(dev, test, "ID_PATH", path);
 
         add_id_tag(dev, test, path);
 
@@ -871,7 +863,7 @@ static int builtin_path_id(UdevEvent *event, int argc, char *argv[], bool test)
          * ID_PATH_ATA_COMPAT
          */
         if (compat_path)
-                udev_builtin_add_property(dev, test, "ID_PATH_ATA_COMPAT", compat_path);
+                (void) udev_builtin_add_property(dev, test, "ID_PATH_ATA_COMPAT", compat_path);
 
         return 0;
 }
index bcc2018c6fb7d01f43f6b5ab77f9f5cd0bbcc73d..6caea8eccee8cec4a60f80464d35ca686e901818 100644 (file)
@@ -154,3 +154,26 @@ int udev_builtin_add_propertyf(sd_device *dev, bool test, const char *key, const
 
         return udev_builtin_add_property(dev, test, key, val);
 }
+
+int udev_builtin_import_property(sd_device *dev, sd_device *src, bool test, const char *key) {
+        const char *val;
+        int r;
+
+        assert(dev);
+        assert(key);
+
+        if (!src)
+                return 0;
+
+        r = sd_device_get_property_value(src, key, &val);
+        if (r == -ENOENT)
+                return 0;
+        if (r < 0)
+                return log_device_debug_errno(src, r, "Failed to get property \"%s\", ignoring: %m", key);
+
+        r = udev_builtin_add_property(dev, test, key, val);
+        if (r < 0)
+                return r;
+
+        return 1;
+}
index fcd41d615de4d63c72658c1990b7f1480a0e2a3e..c7a48b0201cee858ad96230dfa198c9e08b71122 100644 (file)
@@ -84,5 +84,6 @@ void udev_builtin_list(void);
 bool udev_builtin_should_reload(void);
 int udev_builtin_add_property(sd_device *dev, bool test, const char *key, const char *val);
 int udev_builtin_add_propertyf(sd_device *dev, bool test, const char *key, const char *valf, ...) _printf_(4, 5);
+int udev_builtin_import_property(sd_device *dev, sd_device *src, bool test, const char *key);
 int udev_builtin_hwdb_lookup(sd_device *dev, const char *prefix, const char *modalias,
                              const char *filter, bool test);
index 1cf215ed46cb6e2782b3cc93566208451cb5c748..31944c224723126f77f462583389b40551d09f67 100644 (file)
@@ -332,6 +332,34 @@ static int on_event_timeout_warning(sd_event_source *s, uint64_t usec, void *use
         return 1;
 }
 
+static usec_t extra_timeout_usec(void) {
+        static usec_t saved = 10 * USEC_PER_SEC;
+        static bool parsed = false;
+        usec_t timeout;
+        const char *e;
+        int r;
+
+        if (parsed)
+                return saved;
+
+        parsed = true;
+
+        e = getenv("SYSTEMD_UDEV_EXTRA_TIMEOUT_SEC");
+        if (!e)
+                return saved;
+
+        r = parse_sec(e, &timeout);
+        if (r < 0)
+                log_debug_errno(r, "Failed to parse $SYSTEMD_UDEV_EXTRA_TIMEOUT_SEC=%s, ignoring: %m", e);
+
+        if (timeout > 5 * USEC_PER_HOUR) /* Add an arbitrary upper bound */
+                log_debug("Parsed $SYSTEMD_UDEV_EXTRA_TIMEOUT_SEC=%s is too large, ignoring.", e);
+        else
+                saved = timeout;
+
+        return saved;
+}
+
 static void worker_attach_event(Worker *worker, Event *event) {
         Manager *manager = ASSERT_PTR(ASSERT_PTR(worker)->manager);
         sd_event *e = ASSERT_PTR(manager->event);
@@ -349,8 +377,12 @@ static void worker_attach_event(Worker *worker, Event *event) {
                                           udev_warn_timeout(manager->timeout_usec), USEC_PER_SEC,
                                           on_event_timeout_warning, event);
 
+        /* Manager.timeout_usec is also used as the timeout for running programs specified in
+         * IMPORT{program}=, PROGRAM=, or RUN=. Here, let's add an extra time before the manager
+         * kills a worker, to make it possible that the worker detects timed out of spawned programs,
+         * kills them, and finalizes the event. */
         (void) sd_event_add_time_relative(e, &event->timeout_event, CLOCK_MONOTONIC,
-                                          manager->timeout_usec, USEC_PER_SEC,
+                                          usec_add(manager->timeout_usec, extra_timeout_usec()), USEC_PER_SEC,
                                           on_event_timeout, event);
 }
 
index 9d01e5866c41b0aff16eca49f083b0d5189f10a6..3ec675746bc6e0282070b65ea84740c6882425c6 100644 (file)
@@ -691,9 +691,7 @@ static int parse_token(UdevRuleLine *rule_line, const char *key, char *attr, Ude
                 }
 
                 if (!is_match) {
-                        if (STR_IN_SET(attr,
-                                       "ACTION", "DEVLINKS", "DEVNAME", "DEVPATH", "DEVTYPE", "DRIVER",
-                                       "IFINDEX", "MAJOR", "MINOR", "SEQNUM", "SUBSYSTEM", "TAGS"))
+                        if (!device_property_can_set(attr))
                                 return log_line_error_errno(rule_line, SYNTHETIC_ERRNO(EINVAL),
                                                             "Invalid ENV attribute. '%s' cannot be set.", attr);
 
index 998b1759c4955b46f2d6081035cadfae24b4dab9..8f7c9fec42c9eed1c0388e20c6510d47d6b3d0fc 100644 (file)
@@ -23,6 +23,7 @@ typedef struct Spawn {
         usec_t timeout_usec;
         int timeout_signal;
         usec_t event_birth_usec;
+        usec_t cmd_birth_usec;
         bool accept_failure;
         int fd_stdout;
         int fd_stderr;
@@ -163,31 +164,20 @@ static int spawn_wait(Spawn *spawn) {
         if (r < 0)
                 return log_device_debug_errno(spawn->device, r, "Failed to allocate sd-event object: %m");
 
-        if (spawn->timeout_usec > 0) {
-                usec_t usec, age_usec;
-
-                usec = now(CLOCK_MONOTONIC);
-                age_usec = usec - spawn->event_birth_usec;
-                if (age_usec < spawn->timeout_usec) {
-                        if (spawn->timeout_warn_usec > 0 &&
-                            spawn->timeout_warn_usec < spawn->timeout_usec &&
-                            spawn->timeout_warn_usec > age_usec) {
-                                spawn->timeout_warn_usec -= age_usec;
-
-                                r = sd_event_add_time(e, NULL, CLOCK_MONOTONIC,
-                                                      usec + spawn->timeout_warn_usec, USEC_PER_SEC,
-                                                      on_spawn_timeout_warning, spawn);
-                                if (r < 0)
-                                        return log_device_debug_errno(spawn->device, r, "Failed to create timeout warning event source: %m");
-                        }
-
-                        spawn->timeout_usec -= age_usec;
-
+        if (spawn->timeout_usec != USEC_INFINITY) {
+                if (spawn->timeout_warn_usec < spawn->timeout_usec) {
                         r = sd_event_add_time(e, NULL, CLOCK_MONOTONIC,
-                                              usec + spawn->timeout_usec, USEC_PER_SEC, on_spawn_timeout, spawn);
+                                              usec_add(spawn->cmd_birth_usec, spawn->timeout_warn_usec), USEC_PER_SEC,
+                                              on_spawn_timeout_warning, spawn);
                         if (r < 0)
-                                return log_device_debug_errno(spawn->device, r, "Failed to create timeout event source: %m");
+                                return log_device_debug_errno(spawn->device, r, "Failed to create timeout warning event source: %m");
                 }
+
+                r = sd_event_add_time(e, NULL, CLOCK_MONOTONIC,
+                                      usec_add(spawn->cmd_birth_usec, spawn->timeout_usec), USEC_PER_SEC,
+                                      on_spawn_timeout, spawn);
+                if (r < 0)
+                        return log_device_debug_errno(spawn->device, r, "Failed to create timeout event source: %m");
         }
 
         if (spawn->fd_stdout >= 0) {
@@ -240,6 +230,13 @@ int udev_event_spawn(
 
         int timeout_signal = event->worker ? event->worker->timeout_signal : SIGKILL;
         usec_t timeout_usec = event->worker ? event->worker->timeout_usec : DEFAULT_WORKER_TIMEOUT_USEC;
+        usec_t now_usec = now(CLOCK_MONOTONIC);
+        usec_t age_usec = usec_sub_unsigned(now_usec, event->birth_usec);
+        usec_t cmd_timeout_usec = usec_sub_unsigned(timeout_usec, age_usec);
+        if (cmd_timeout_usec <= 0)
+                return log_device_warning_errno(event->dev, SYNTHETIC_ERRNO(ETIME),
+                                                "The event already takes longer (%s) than the timeout (%s), skipping execution of '%s'.",
+                                                FORMAT_TIMESPAN(age_usec, 1), FORMAT_TIMESPAN(timeout_usec, 1), cmd);
 
         /* pipes from child to parent */
         if (result || log_get_max_level() >= LOG_INFO)
@@ -300,10 +297,11 @@ int udev_event_spawn(
                 .cmd = cmd,
                 .pid = pid,
                 .accept_failure = accept_failure,
-                .timeout_warn_usec = udev_warn_timeout(timeout_usec),
-                .timeout_usec = timeout_usec,
+                .timeout_warn_usec = udev_warn_timeout(cmd_timeout_usec),
+                .timeout_usec = cmd_timeout_usec,
                 .timeout_signal = timeout_signal,
                 .event_birth_usec = event->birth_usec,
+                .cmd_birth_usec = now_usec,
                 .fd_stdout = outpipe[READ_END],
                 .fd_stderr = errpipe[READ_END],
                 .result = result,
@@ -340,6 +338,17 @@ void udev_event_execute_run(UdevEvent *event) {
                                 log_device_debug_errno(event->dev, r, "Failed to run built-in command \"%s\", ignoring: %m", command);
                 } else {
                         if (event->worker && event->worker->exec_delay_usec > 0) {
+                                usec_t timeout_usec = event->worker ? event->worker->timeout_usec : DEFAULT_WORKER_TIMEOUT_USEC;
+                                usec_t now_usec = now(CLOCK_MONOTONIC);
+                                usec_t age_usec = usec_sub_unsigned(now_usec, event->birth_usec);
+
+                                if (event->worker->exec_delay_usec >= usec_sub_unsigned(timeout_usec, age_usec)) {
+                                        log_device_warning(event->dev,
+                                                           "Cannot delaying execution of \"%s\" for %s, skipping.",
+                                                           command, FORMAT_TIMESPAN(event->worker->exec_delay_usec, USEC_PER_SEC));
+                                        continue;
+                                }
+
                                 log_device_debug(event->dev, "Delaying execution of \"%s\" for %s.",
                                                  command, FORMAT_TIMESPAN(event->worker->exec_delay_usec, USEC_PER_SEC));
                                 (void) usleep_safe(event->worker->exec_delay_usec);
index b6632968070e006d3eca81381a80f30b97d1469a..fdbdb6f59a7060af99fd7da73f331c3c5ca6340b 100644 (file)
@@ -6,6 +6,8 @@
 #include <stdio.h>
 #include <stdlib.h>
 
+#include "device-private.h"
+#include "device-util.h"
 #include "log.h"
 #include "udev-builtin.h"
 #include "udevadm.h"
@@ -78,6 +80,7 @@ int builtin_main(int argc, char *argv[], void *userdata) {
         int r;
 
         log_set_max_level(LOG_DEBUG);
+        log_parse_environment();
 
         r = parse_argv(argc, argv);
         if (r <= 0)
@@ -103,6 +106,15 @@ int builtin_main(int argc, char *argv[], void *userdata) {
                 goto finish;
         }
 
+        if (arg_action != SD_DEVICE_REMOVE) {
+                /* For net_setup_link */
+                r = device_clone_with_db(dev, &event->dev_db_clone);
+                if (r < 0) {
+                        log_device_error_errno(dev, r, "Failed to clone device: %m");
+                        goto finish;
+                }
+        }
+
         r = udev_builtin_run(event, cmd, arg_command, true);
         if (r < 0) {
                 log_debug_errno(r, "Builtin command '%s' fails: %m", arg_command);
index 813638fd90cfad4b8a0a216da0172c3bdcae0397..6c4a01a30e30b6800582aa772260bf194503cb32 100644 (file)
@@ -95,6 +95,7 @@ int test_main(int argc, char *argv[], void *userdata) {
         int r;
 
         log_set_max_level(LOG_DEBUG);
+        log_parse_environment();
 
         r = parse_argv(argc, argv);
         if (r <= 0)
index 9ab5d42c4b86f87b9e1eb3f3aab3a1961686e349..ecbdcb0f41fe19ed49f63ae2675e2a4c94abd7e2 100644 (file)
@@ -169,7 +169,7 @@ static const struct {
         },
 };
 
-static int table_add_uid_boundaries(Table *table, const UidRange *p) {
+static int table_add_uid_boundaries(Table *table, const UIDRange *p) {
         int r;
 
         assert(table);
@@ -301,7 +301,7 @@ static int add_unavailable_uid(Table *table, uid_t start, uid_t end) {
 
 static int table_add_uid_map(
                 Table *table,
-                const UidRange *p,
+                const UIDRange *p,
                 int (*add_unavailable)(Table *t, uid_t start, uid_t end)) {
 
         uid_t focus = 0;
@@ -311,7 +311,7 @@ static int table_add_uid_map(
         assert(add_unavailable);
 
         for (size_t i = 0; p && i < p->n_entries; i++) {
-                UidRangeEntry *x = p->entries + i;
+                UIDRangeEntry *x = p->entries + i;
 
                 if (focus < x->start) {
                         r = add_unavailable(table, focus, x->start-1);
@@ -425,7 +425,7 @@ static int display_user(int argc, char *argv[], void *userdata) {
         }
 
         if (table) {
-                _cleanup_(uid_range_freep) UidRange *uid_range = NULL;
+                _cleanup_(uid_range_freep) UIDRange *uid_range = NULL;
                 int boundary_lines, uid_map_lines;
 
                 r = uid_range_load_userns(&uid_range, "/proc/self/uid_map");
@@ -526,7 +526,7 @@ static int show_group(GroupRecord *gr, Table *table) {
         return 0;
 }
 
-static int table_add_gid_boundaries(Table *table, const UidRange *p) {
+static int table_add_gid_boundaries(Table *table, const UIDRange *p) {
         int r;
 
         assert(table);
@@ -728,7 +728,7 @@ static int display_group(int argc, char *argv[], void *userdata) {
         }
 
         if (table) {
-                _cleanup_(uid_range_freep) UidRange *gid_range = NULL;
+                _cleanup_(uid_range_freep) UIDRange *gid_range = NULL;
                 int boundary_lines, gid_map_lines;
 
                 r = uid_range_load_userns(&gid_range, "/proc/self/gid_map");
index bdaaab2c0557d01eb60c31e35e2f452def66e096..73b6d720262ecc2d6fd27d5f897b8f2a780d476c 100644 (file)
@@ -5,6 +5,7 @@
 #include "sd-daemon.h"
 
 #include "common-signal.h"
+#include "env-util.h"
 #include "fd-util.h"
 #include "fs-util.h"
 #include "mkdir.h"
@@ -157,7 +158,6 @@ static int start_one_worker(Manager *m) {
         if (r < 0)
                 return log_error_errno(r, "Failed to fork new worker child: %m");
         if (r == 0) {
-                char pids[DECIMAL_STR_MAX(pid_t)];
                 /* Child */
 
                 if (m->listen_fd == 3) {
@@ -175,9 +175,9 @@ static int start_one_worker(Manager *m) {
                         safe_close(m->listen_fd);
                 }
 
-                xsprintf(pids, PID_FMT, pid);
-                if (setenv("LISTEN_PID", pids, 1) < 0) {
-                        log_error_errno(errno, "Failed to set $LISTEN_PID: %m");
+                r = setenvf("LISTEN_PID", /* overwrite= */ true, PID_FMT, pid);
+                if (r < 0) {
+                        log_error_errno(r, "Failed to set $LISTEN_PID: %m");
                         _exit(EXIT_FAILURE);
                 }
 
index d55d4aab0b5ff4bcebccd7cbbc194a37d2da8419..95ce82b7c603da13721b5852d4eae345b066b66c 100644 (file)
@@ -252,7 +252,7 @@ static int determine_device(
         if (*data_what && *hash_what)
                 return 0;
 
-        r = unhexmem(hash, strlen(hash), &m, &l);
+        r = unhexmem(hash, &m, &l);
         if (r < 0)
                 return log_error_errno(r, "Failed to parse hash: %s", hash);
         if (l < sizeof(sd_id128_t)) {
index d73c2d39fbec8cd9bebd9cc66fc0e3c45241c2b1..d133572464410187e04420861bd1d46dddc005aa 100644 (file)
@@ -205,7 +205,7 @@ static int parse_options(const char *options) {
                                 size_t l;
                                 void *m;
 
-                                r = unhexmem(val, strlen(val), &m, &l);
+                                r = unhexmem(val, &m, &l);
                                 if (r < 0)
                                         return log_error_errno(r, "Failed to parse salt '%s': %m", word);
 
@@ -312,7 +312,7 @@ static int run(int argc, char *argv[]) {
                 if (!filename_is_valid(volume))
                         return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Volume name '%s' is not valid.", volume);
 
-                r = unhexmem(root_hash, SIZE_MAX, &m, &l);
+                r = unhexmem(root_hash, &m, &l);
                 if (r < 0)
                         return log_error_errno(r, "Failed to parse root hash: %m");
 
@@ -378,7 +378,7 @@ static int run(int argc, char *argv[]) {
                         char *value;
 
                         if ((value = startswith(arg_root_hash_signature, "base64:"))) {
-                                r = unbase64mem(value, strlen(value), (void *)&hash_sig, &hash_sig_size);
+                                r = unbase64mem(value, (void*) &hash_sig, &hash_sig_size);
                                 if (r < 0)
                                         return log_error_errno(r, "Failed to parse root hash signature '%s': %m", arg_root_hash_signature);
                         } else {
diff --git a/src/vpick/meson.build b/src/vpick/meson.build
new file mode 100644 (file)
index 0000000..a8c14cb
--- /dev/null
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+executables += [
+        executable_template + {
+                'name' : 'systemd-vpick',
+                'public' : true,
+                'sources' : files('vpick-tool.c'),
+        },
+]
diff --git a/src/vpick/vpick-tool.c b/src/vpick/vpick-tool.c
new file mode 100644 (file)
index 0000000..f3afc33
--- /dev/null
@@ -0,0 +1,350 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <getopt.h>
+
+#include "architecture.h"
+#include "build.h"
+#include "format-table.h"
+#include "fs-util.h"
+#include "main-func.h"
+#include "parse-util.h"
+#include "path-util.h"
+#include "pretty-print.h"
+#include "stat-util.h"
+#include "string-util.h"
+#include "strv.h"
+#include "terminal-util.h"
+#include "vpick.h"
+
+static char *arg_filter_basename = NULL;
+static char *arg_filter_version = NULL;
+static Architecture arg_filter_architecture = _ARCHITECTURE_INVALID;
+static char *arg_filter_suffix = NULL;
+static uint32_t arg_filter_type_mask = 0;
+static enum {
+        PRINT_PATH,
+        PRINT_FILENAME,
+        PRINT_VERSION,
+        PRINT_TYPE,
+        PRINT_ARCHITECTURE,
+        PRINT_TRIES,
+        PRINT_ALL,
+        _PRINT_INVALID = -EINVAL,
+} arg_print = _PRINT_INVALID;
+static PickFlags arg_flags = PICK_ARCHITECTURE|PICK_TRIES;
+
+STATIC_DESTRUCTOR_REGISTER(arg_filter_basename, freep);
+STATIC_DESTRUCTOR_REGISTER(arg_filter_version, freep);
+STATIC_DESTRUCTOR_REGISTER(arg_filter_suffix, freep);
+
+static int help(void) {
+        _cleanup_free_ char *link = NULL;
+        int r;
+
+        r = terminal_urlify_man("systemd-vpick", "1", &link);
+        if (r < 0)
+                return log_oom();
+
+        printf("%1$s [OPTIONS...] PATH...\n"
+               "\n%5$sPick entry from versioned directory.%6$s\n\n"
+               "  -h --help            Show this help\n"
+               "     --version         Show package version\n"
+               "\n%3$sLookup Keys:%4$s\n"
+               "  -B --basename=BASENAME\n"
+               "                       Look for specified basename\n"
+               "  -V VERSION           Look for specified version\n"
+               "  -A ARCH              Look for specified architecture\n"
+               "  -S --suffix=SUFFIX   Look for specified suffix\n"
+               "  -t --type=TYPE       Look for specified inode type\n"
+               "\n%3$sOutput:%4$s\n"
+               "  -p --print=filename  Print selected filename rather than path\n"
+               "  -p --print=version   Print selected version rather than path\n"
+               "  -p --print=type      Print selected inode type rather than path\n"
+               "  -p --print=arch      Print selected architecture rather than path\n"
+               "  -p --print=tries     Print selected tries left/tries done rather than path\n"
+               "  -p --print=all       Print all of the above\n"
+               "     --resolve=yes     Canonicalize the result path\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 parse_argv(int argc, char *argv[]) {
+
+        enum {
+                ARG_VERSION = 0x100,
+                ARG_RESOLVE,
+        };
+
+        static const struct option options[] = {
+                { "help",         no_argument,       NULL, 'h'          },
+                { "version",      no_argument,       NULL, ARG_VERSION  },
+                { "basename",     required_argument, NULL, 'B'          },
+                { "suffix",       required_argument, NULL, 'S'          },
+                { "type",         required_argument, NULL, 't'          },
+                { "print",        required_argument, NULL, 'p'          },
+                { "resolve",      required_argument, NULL, ARG_RESOLVE  },
+                {}
+        };
+
+        int c, r;
+
+        assert(argc >= 0);
+        assert(argv);
+
+        while ((c = getopt_long(argc, argv, "hB:V:A:S:t:p:", options, NULL)) >= 0) {
+
+                switch (c) {
+
+                case 'h':
+                        return help();
+
+                case ARG_VERSION:
+                        return version();
+
+                case 'B':
+                        if (!filename_part_is_valid(optarg))
+                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid basename string: %s", optarg);
+
+                        r = free_and_strdup_warn(&arg_filter_basename, optarg);
+                        if (r < 0)
+                                return r;
+
+                        break;
+
+                case 'V':
+                        if (!version_is_valid(optarg))
+                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid version string: %s", optarg);
+
+                        r = free_and_strdup_warn(&arg_filter_version, optarg);
+                        if (r < 0)
+                                return r;
+
+                        break;
+
+                case 'A':
+                        if (streq(optarg, "native"))
+                                arg_filter_architecture = native_architecture();
+                        else if (streq(optarg, "secondary")) {
+#ifdef ARCHITECTURE_SECONDARY
+                                arg_filter_architecture = ARCHITECTURE_SECONDARY;
+#else
+                                return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Local architecture has no secondary architecture.");
+#endif
+                        } else if (streq(optarg, "uname"))
+                                arg_filter_architecture = uname_architecture();
+                        else if (streq(optarg, "auto"))
+                                arg_filter_architecture = _ARCHITECTURE_INVALID;
+                        else {
+                                arg_filter_architecture = architecture_from_string(optarg);
+                                if (arg_filter_architecture < 0)
+                                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unknown architecture: %s", optarg);
+                        }
+                        break;
+
+                case 'S':
+                        if (!filename_part_is_valid(optarg))
+                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid suffix string: %s", optarg);
+
+                        r = free_and_strdup_warn(&arg_filter_suffix, optarg);
+                        if (r < 0)
+                                return r;
+
+                        break;
+
+                case 't':
+                        if (isempty(optarg))
+                                arg_filter_type_mask = 0;
+                        else {
+                                mode_t m;
+
+                                m = inode_type_from_string(optarg);
+                                if (m == MODE_INVALID)
+                                        return log_error_errno(m, "Unknown inode type: %s", optarg);
+
+                                arg_filter_type_mask |= UINT32_C(1) << IFTODT(m);
+                        }
+
+                        break;
+
+                case 'p':
+                        if (streq(optarg, "path"))
+                                arg_print = PRINT_PATH;
+                        else if (streq(optarg, "filename"))
+                                arg_print = PRINT_FILENAME;
+                        else if (streq(optarg, "version"))
+                                arg_print = PRINT_VERSION;
+                        else if (streq(optarg, "type"))
+                                arg_print = PRINT_TYPE;
+                        else if (STR_IN_SET(optarg, "arch", "architecture"))
+                                arg_print = PRINT_ARCHITECTURE;
+                        else if (streq(optarg, "tries"))
+                                arg_print = PRINT_TRIES;
+                        else if (streq(optarg, "all"))
+                                arg_print = PRINT_ALL;
+                        else
+                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unknown --print= argument: %s", optarg);
+
+                        break;
+
+                case ARG_RESOLVE:
+                        r = parse_boolean(optarg);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to parse --resolve= value: %m");
+
+                        SET_FLAG(arg_flags, PICK_RESOLVE, r);
+                        break;
+
+                case '?':
+                        return -EINVAL;
+
+                default:
+                        assert_not_reached();
+                }
+        }
+
+        if (arg_print < 0)
+                arg_print = PRINT_PATH;
+
+        return 1;
+}
+
+static int run(int argc, char *argv[]) {
+        int r;
+
+        log_show_color(true);
+        log_parse_environment();
+        log_open();
+
+        r = parse_argv(argc, argv);
+        if (r <= 0)
+                return r;
+
+        if (optind >= argc)
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Path to resolve must be specified.");
+
+        for (int i = optind; i < argc; i++) {
+                _cleanup_free_ char *p = NULL;
+                r = path_make_absolute_cwd(argv[i], &p);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to make path '%s' absolute: %m", argv[i]);
+
+                _cleanup_(pick_result_done) PickResult result = PICK_RESULT_NULL;
+                r = path_pick(/* toplevel_path= */ NULL,
+                              /* toplevel_fd= */ AT_FDCWD,
+                              p,
+                              &(PickFilter) {
+                                      .basename = arg_filter_basename,
+                                      .version = arg_filter_version,
+                                      .architecture = arg_filter_architecture,
+                                      .suffix = arg_filter_suffix,
+                                      .type_mask = arg_filter_type_mask,
+                              },
+                              arg_flags,
+                              &result);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to pick version for '%s': %m", p);
+                if (r == 0)
+                        return log_error_errno(SYNTHETIC_ERRNO(ENOENT), "No matching version for '%s' found.", p);
+
+                switch (arg_print) {
+
+                case PRINT_PATH:
+                        fputs(result.path, stdout);
+                        if (result.st.st_mode != MODE_INVALID && S_ISDIR(result.st.st_mode) && !endswith(result.path, "/"))
+                                fputc('/', stdout);
+                        fputc('\n', stdout);
+                        break;
+
+                case PRINT_FILENAME: {
+                        _cleanup_free_ char *fname = NULL;
+
+                        r = path_extract_filename(result.path, &fname);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to extract filename from path '%s': %m", result.path);
+
+                        puts(fname);
+                        break;
+                }
+
+                case PRINT_VERSION:
+                        if (!result.version)
+                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "No version information discovered.");
+
+                        puts(result.version);
+                        break;
+
+                case PRINT_TYPE:
+                        if (result.st.st_mode == MODE_INVALID)
+                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "No inode type information discovered.");
+
+                        puts(inode_type_to_string(result.st.st_mode));
+                        break;
+
+                case PRINT_ARCHITECTURE:
+                        if (result.architecture < 0)
+                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "No architecture information discovered.");
+
+                        puts(architecture_to_string(result.architecture));
+                        break;
+
+                case PRINT_TRIES:
+                        if (result.tries_left == UINT_MAX)
+                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "No tries left/tries done information discovered.");
+
+                        printf("+%u-%u", result.tries_left, result.tries_done);
+                        break;
+
+                case PRINT_ALL: {
+                        _cleanup_(table_unrefp) Table *t = NULL;
+
+                        t = table_new_vertical();
+                        if (!t)
+                                return log_oom();
+
+                        table_set_ersatz_string(t, TABLE_ERSATZ_NA);
+
+                        r = table_add_many(
+                                        t,
+                                        TABLE_FIELD, "Path",
+                                        TABLE_PATH, result.path,
+                                        TABLE_FIELD, "Version",
+                                        TABLE_STRING, result.version,
+                                        TABLE_FIELD, "Type",
+                                        TABLE_STRING, result.st.st_mode == MODE_INVALID ? NULL : inode_type_to_string(result.st.st_mode),
+                                        TABLE_FIELD, "Architecture",
+                                        TABLE_STRING, result.architecture < 0 ? NULL : architecture_to_string(result.architecture));
+                        if (r < 0)
+                                return table_log_add_error(r);
+
+                        if (result.tries_left != UINT_MAX) {
+                                r = table_add_many(
+                                                t,
+                                                TABLE_FIELD, "Tries left",
+                                                TABLE_UINT, result.tries_left,
+                                                TABLE_FIELD, "Tries done",
+                                                TABLE_UINT, result.tries_done);
+                                if (r < 0)
+                                        return table_log_add_error(r);
+                        }
+
+                        r = table_print(t, stdout);
+                        if (r < 0)
+                                return table_log_print_error(r);
+
+                        break;
+                }
+
+                default:
+                        assert_not_reached();
+                }
+        }
+
+        return 0;
+}
+
+DEFINE_MAIN_FUNCTION(run);
index a6739eee1cd104824df627c83f2e13bbd9bdecdb..a9c3e05d006246a38b65cf5d00efba808e2549e7 100755 (executable)
@@ -56,6 +56,10 @@ can_test_pkcs11() {
         ddebug "Support for p11-kit is disabled, skipping the PKCS#11 test"
         return 1
     fi
+    if ! "${SYSTEMCTL:?}" --version | grep -q "+OPENSSL"; then
+        ddebug "Support for openssl 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
index 7940600612d3694b0341f70dbc9213eed04b961a..2d17630d29ee10ad7e86a05f3e428e86e32c7bc6 100755 (executable)
@@ -8,6 +8,9 @@ NSPAWN_ARGUMENTS="--private-network"
 # shellcheck source=test/test-functions
 . "${TEST_BASE_DIR:?}/test-functions"
 
+# (Hopefully) a temporary workaround for https://github.com/systemd/systemd/issues/30573
+KERNEL_APPEND="${KERNEL_APPEND:-} SYSTEMD_DEFAULT_MOUNT_RATE_LIMIT_BURST=100"
+
 test_append_files() {
     local workspace="${1:?}"
 
@@ -22,6 +25,8 @@ test_append_files() {
         install_mdadm
         generate_module_dependencies
     fi
+
+    image_install socat
 }
 
 do_test "$@"
index 6ea0cca3db14c851bace57d03e59136ed00e5281..69c2082e48cd42471a820728f76bb82e37349384 100644 (file)
@@ -19,19 +19,27 @@ acl:
       address: fd00:dead:beef:cafe::/64
       action: update
 
+    - id: transfer_acl
+      address: 10.0.0.0/24
+      address: fd00:dead:beef:cafe::/64
+      action: transfer
+
 remote:
     - id: parent_zone_server
       address: 10.0.0.1@53
       address: fd00:dead:beef:cafe::1@53
 
+    - id: forwarded
+      address: 10.99.0.1@53
+
 submission:
     - id: parent_zone_sbm
       check-interval: 2s
       parent: [parent_zone_server]
 
-# Auto ZSK/KSK rollover for DNSSEC-enabled zones + pushing the respective DS
-# records to the parent zone
 policy:
+    # Auto ZSK/KSK rollover for DNSSEC-enabled zones + pushing the respective DS
+    # records to the parent zone
     - id: auto_rollover
       algorithm: ECDSAP256SHA256
       cds-cdnskey-publish: always
@@ -43,8 +51,7 @@ policy:
       zone-max-ttl: 1s
       zsk-lifetime: 60d
 
-# Same as auto_rollover, but with NSEC3 turned on
-policy:
+    # Same as auto_rollover, but with NSEC3 turned on
     - id: auto_rollover_nsec3
       algorithm: ECDSAP256SHA256
       cds-cdnskey-publish: always
@@ -58,17 +65,20 @@ policy:
       zone-max-ttl: 1s
       zsk-lifetime: 60d
 
-policy:
     - id: untrusted
       cds-cdnskey-publish: none
 
-# Manual ZSK/KSK management
-policy:
+    # Manual ZSK/KSK management
     - id: manual
       manual: on
 
-# Sign everything by default and propagate the respective DS records to the parent
+mod-dnsproxy:
+  - id: forwarded
+    remote: forwarded
+    fallback: off
+
 template:
+    # Sign everything by default and propagate the respective DS records to the parent
     - id: default
       acl: update_acl
       dnssec-policy: auto_rollover
@@ -77,14 +87,18 @@ template:
       semantic-checks: on
       storage: "/var/lib/knot/zones"
 
-# A template for unsigned zones (i.e. without DNSSEC)
-template:
+    # A template for unsigned zones (i.e. without DNSSEC)
     - id: unsigned
       dnssec-signing: off
       file: "%s.zone"
       semantic-checks: on
       storage: "/var/lib/knot/zones"
 
+    - id: forwarded
+      dnssec-signing: off
+      module: mod-dnsproxy/forwarded
+      zonefile-load: none
+
 zone:
     # Create our own DNSSEC-aware root zone, so we can test the whole chain of
     # trust. This needs a ZSK/KSK keypair to be generated before running knot +
@@ -98,8 +112,9 @@ zone:
     - domain: test
       dnssec-policy: auto_rollover_nsec3
 
-    # A fully (pre-)signed zone
+    # A fully (pre-)signed zone with allowed zone transfers (AXFR/IXFR)
     - domain: signed.test
+      acl: [update_acl, transfer_acl]
 
     # A fully (online)-signed zone
     # See: https://www.knot-dns.cz/docs/3.1/singlehtml/index.html#mod-onlinesign
@@ -117,3 +132,7 @@ zone:
     # An unsigned zone
     - domain: unsigned.test
       template: unsigned
+
+    # Forward all queries for this zone to our dummy test server
+    - domain: forwarded.test
+      template: forwarded
index 560628ec9a3210ef0a78a4f877c4f019f364b103..f91164295cfe17e98994ad8bde01b2893eac7e3e 100644 (file)
@@ -3,7 +3,7 @@
 Description=Test for AmbientCapabilities (dynamic user)
 
 [Service]
-ExecStart=/bin/sh -x -c 'c=$$(grep "CapAmb:" /proc/self/status); test "$$c" = "CapAmb: 0000000000002081"'
+ExecStart=sh -x -c 'c=$$(grep "CapAmb:" /proc/self/status); test "$$c" = "CapAmb:      0000000000002081"'
 Type=oneshot
 AmbientCapabilities=CAP_CHOWN CAP_SETUID CAP_NET_RAW
 DynamicUser=yes
index 4960da56bea09e8f18e040d23a5e44a705114da7..a170b3d7b687306754044b801b0a92e8921bbc9d 100644 (file)
@@ -3,7 +3,7 @@
 Description=Test for AmbientCapabilities
 
 [Service]
-ExecStart=/bin/sh -x -c 'c=$$(grep "CapAmb:" /proc/self/status); test "$$c" = "CapAmb: 0000000000002001"'
+ExecStart=sh -x -c 'c=$$(grep "CapAmb:" /proc/self/status); test "$$c" = "CapAmb:      0000000000002001"'
 Type=oneshot
 User=nfsnobody
 AmbientCapabilities=CAP_CHOWN
index 4c72b2eee5e934cc7717ef1b39a16d58ff5ea543..2e21bbc58a9f97427373af99f5e248c9aa311b9f 100644 (file)
@@ -3,7 +3,7 @@
 Description=Test for AmbientCapabilities
 
 [Service]
-ExecStart=/bin/sh -x -c 'c=$$(grep "CapAmb:" /proc/self/status); test "$$c" = "CapAmb: 0000000000002001"'
+ExecStart=sh -x -c 'c=$$(grep "CapAmb:" /proc/self/status); test "$$c" = "CapAmb:      0000000000002001"'
 Type=oneshot
 User=nobody
 AmbientCapabilities=CAP_CHOWN
index 13a5d4577e061fd3c69fea60de047b7d9187f7be..c4bb21b2ace879a50051a9c23173fc91b416e24e 100644 (file)
@@ -3,7 +3,7 @@
 Description=Test for AmbientCapabilities (daemon)
 
 [Service]
-ExecStart=/bin/sh -x -c 'c=$$(grep "CapAmb:" /proc/self/status); test "$$c" = "CapAmb: 0000000000002001"'
+ExecStart=sh -x -c 'c=$$(grep "CapAmb:" /proc/self/status); test "$$c" = "CapAmb:      0000000000002001"'
 Type=oneshot
 User=daemon
 AmbientCapabilities=CAP_CHOWN
index 10cb44012ba288e2ed99c6874f558ac6bbc10c90..0bf91cc98f6dda1852f6cef90e8ef713288feb1d 100644 (file)
@@ -3,7 +3,7 @@
 Description=Test for AmbientCapabilities
 
 [Service]
-ExecStart=/bin/sh -x -c 'c=$$(grep "CapAmb:" /proc/self/status); test "$$c" = "CapAmb: 0000000000002001"'
+ExecStart=sh -x -c 'c=$$(grep "CapAmb:" /proc/self/status); test "$$c" = "CapAmb:      0000000000002001"'
 Type=oneshot
 User=nfsnobody
 AmbientCapabilities=CAP_CHOWN CAP_NET_RAW
index 5400cac9516de38129ced1a7820430ebee93343e..8bd7ac4f90f8f8275d5472f4a92c90926e5c65d2 100644 (file)
@@ -3,7 +3,7 @@
 Description=Test for AmbientCapabilities
 
 [Service]
-ExecStart=/bin/sh -x -c 'c=$$(grep "CapAmb:" /proc/self/status); test "$$c" = "CapAmb: 0000000000002001"'
+ExecStart=sh -x -c 'c=$$(grep "CapAmb:" /proc/self/status); test "$$c" = "CapAmb:      0000000000002001"'
 Type=oneshot
 User=nobody
 AmbientCapabilities=CAP_CHOWN CAP_NET_RAW
index 5336bec3d80c4086876653786d94a37fa917e90e..1bbc7270310dd494e93d4b0b6a37f461cd4a90d7 100644 (file)
@@ -3,7 +3,7 @@
 Description=Test for AmbientCapabilities (daemon)
 
 [Service]
-ExecStart=/bin/sh -x -c 'c=$$(grep "CapAmb:" /proc/self/status); test "$$c" = "CapAmb: 0000000000002001"'
+ExecStart=sh -x -c 'c=$$(grep "CapAmb:" /proc/self/status); test "$$c" = "CapAmb:      0000000000002001"'
 Type=oneshot
 User=daemon
 AmbientCapabilities=CAP_CHOWN CAP_NET_RAW
index bf6968f3c172587ab0370d6f115830030c39c658..12e92e2ac56cb966e79ccf16810588875cfc8933 100644 (file)
@@ -11,7 +11,7 @@ ExecStart=test -f /tmp/thisisasimpletest
 # Also, through /tmp/test-exec-bindreadonlypaths
 ExecStart=test -f /tmp/test-exec-bindreadonlypaths/thisisasimpletest
 # The file cannot modify through /tmp/test-exec-bindreadonlypaths
-ExecStart=/bin/sh -x -c '! touch /tmp/test-exec-bindreadonlypaths/thisisasimpletest'
+ExecStart=sh -x -c '! touch /tmp/test-exec-bindreadonlypaths/thisisasimpletest'
 # Cleanup
 ExecStart=rm /tmp/thisisasimpletest
 BindPaths=/tmp:/tmp/test-exec-bindpaths
index 1b1217e094554eafbb5afe61dd08a9ba10792e30..14f16c6b565f7ff5feaea9b5b97f371d8d422712 100644 (file)
@@ -4,6 +4,6 @@ Description=Test for CapabilityBoundingSet
 
 [Service]
 # sed: remove dropped (cap_xxx-[epi]) and IAB capabilities from the output
-ExecStart=/bin/sh -x -c '! capsh --print | sed -re "s/[^ ]+?\-[epi]+//g" -e '/IAB/d' | grep "^Bounding set .*cap_chown"'
+ExecStart=sh -x -c '! capsh --print | sed -re "s/[^ ]+?\-[epi]+//g" -e '/IAB/d' | grep "^Bounding set .*cap_chown"'
 Type=oneshot
 CapabilityBoundingSet=~CAP_CHOWN
index 1ed3ccbb25fd78f109f17610b77d9bea42f355ea..d3a23706ee93021b0c6eb061b0bc375513544ec8 100644 (file)
@@ -3,7 +3,7 @@
 Description=Test for CapabilityBoundingSet
 
 [Service]
-ExecStart=/bin/sh -x -c 'c=$$(capsh --print | grep "Bounding set "); test "$$c" = "Bounding set =cap_chown,cap_fowner,cap_kill"'
+ExecStart=sh -x -c 'c=$$(capsh --print | grep "Bounding set "); test "$$c" = "Bounding set =cap_chown,cap_fowner,cap_kill"'
 Type=oneshot
 CapabilityBoundingSet=CAP_FOWNER
 CapabilityBoundingSet=CAP_KILL CAP_CHOWN
index 8eb142c64a480b00ec544141228ca04389eb6782..244395165181ca7f81b4603f636b05f3ff3d109d 100644 (file)
@@ -3,7 +3,7 @@
 Description=Test for CapabilityBoundingSet
 
 [Service]
-ExecStart=/bin/sh -x -c 'c=$$(capsh --print | grep "Bounding set "); test "$$c" = "Bounding set ="'
+ExecStart=sh -x -c 'c=$$(capsh --print | grep "Bounding set "); test "$$c" = "Bounding set ="'
 Type=oneshot
 CapabilityBoundingSet=CAP_FOWNER CAP_KILL
 CapabilityBoundingSet=
index be5a5e5b87f7b36fd6c73ee91c1dc38bf8524096..3df3e6d9aeb84de297ab31c9e085fb558530009a 100644 (file)
@@ -3,6 +3,6 @@
 Description=Test for CapabilityBoundingSet
 
 [Service]
-ExecStart=/bin/sh -x -c 'c=$$(capsh --print | grep "Bounding set "); test "$$c" = "Bounding set =cap_fowner,cap_kill"'
+ExecStart=sh -x -c 'c=$$(capsh --print | grep "Bounding set "); test "$$c" = "Bounding set =cap_fowner,cap_kill"'
 Type=oneshot
 CapabilityBoundingSet=CAP_FOWNER CAP_KILL
index 342219cbeac24432d12eda36f3029773cf0a2b71..eb136fff5d4b05a1447b229aa2687dce2d297c08 100644 (file)
@@ -9,4 +9,4 @@ Type=oneshot
 ExecCondition=/bin/sh -c 'exit 255'
 
 # This should not get run
-ExecStart=/bin/sh -c 'true'
+ExecStart=sh -c 'true'
index b69e16134754b5c2db430451d34a25517628279e..4ee58b9c11f9f896ef72d60843b965afcecca940 100644 (file)
@@ -13,4 +13,4 @@ ExecCondition=/bin/sh -c 'exit 254'
 ExecCondition=/bin/sh -c 'exit 255'
 
 # This should not get run
-ExecStart=/bin/sh -c 'true'
+ExecStart=sh -c 'true'
index 2a8544acb1f403cc9353b404965facdbf44805e8..c0941a55793faf874d3ce6df98b41a99556aad90 100644 (file)
@@ -3,5 +3,5 @@
 Description=Test for CPUAffinity (simple)
 
 [Service]
-ExecStart=/bin/sh -c 'test $$(cat /proc/self/status | grep Cpus_allowed: | rev | cut -c 1) = 1'
+ExecStart=sh -c 'test $$(cat /proc/self/status | grep Cpus_allowed: | rev | cut -c 1) = 1'
 CPUAffinity=0
index bed48c822fcaf491700c1a4ceac219f082860c03..d699ecc031d840a9cf7a37a0205927a67acb9e4e 100644 (file)
@@ -3,7 +3,7 @@
 Description=Test for CPUAffinity (reset)
 
 [Service]
-ExecStart=/bin/sh -c 'test $$(cat /proc/self/status | grep Cpus_allowed: | rev | cut -c 1) = 1'
+ExecStart=sh -c 'test $$(cat /proc/self/status | grep Cpus_allowed: | rev | cut -c 1) = 1'
 CPUAffinity=0-1 3
 CPUAffinity=
 CPUAffinity=0
index 774cd642cd8dfe3b9eb904b92fb3a3e11debf476..8e8f782ac35f7ed1846596f54b39a2bc25c4e37c 100644 (file)
@@ -3,6 +3,6 @@
 Description=Test for CPUAffinity (merge)
 
 [Service]
-ExecStart=/bin/sh -c 'test $$(cat /proc/self/status | grep Cpus_allowed: | rev | cut -c 1) = 7'
+ExecStart=sh -c 'test $$(cat /proc/self/status | grep Cpus_allowed: | rev | cut -c 1) = 7'
 CPUAffinity=0,1
 CPUAffinity=1-2
index daaed6c64c2b30f5e8044ea24f408b30883a5a0c..1b7f232cd1394355a7ebb48081365d933aab2140 100644 (file)
@@ -5,8 +5,8 @@ Description=Test DynamicUser with static User= whose uid and gid are different
 
 [Service]
 Type=oneshot
-ExecStart=/bin/sh -x -c 'test "$$(id -nG)" = "adm" && test "$$(id -ng)" = "adm" && test "$$(id -nu)" = "adm"'
+ExecStart=sh -x -c 'test "$$(id -nG)" = "adm" && test "$$(id -ng)" = "adm" && test "$$(id -nu)" = "adm"'
 # Multiple ExecStart= lines causes the issue #9702.
-ExecStart=/bin/sh -x -c 'test "$$(id -nG)" = "adm" && test "$$(id -ng)" = "adm" && test "$$(id -nu)" = "adm"'
+ExecStart=sh -x -c 'test "$$(id -nG)" = "adm" && test "$$(id -ng)" = "adm" && test "$$(id -nu)" = "adm"'
 DynamicUser=yes
 User=adm
index db8b88efdb910269a398c83763d88f78d1efb89e..b13c23a74d9f6728a83c2a8179235b9aae3dd422 100644 (file)
@@ -5,8 +5,8 @@ Description=Test DynamicUser with static User= whose uid and gid are different
 
 [Service]
 Type=oneshot
-ExecStart=/bin/sh -x -c 'test "$$(id -nG)" = "games" && test "$$(id -ng)" = "games" && test "$$(id -nu)" = "games"'
+ExecStart=sh -x -c 'test "$$(id -nG)" = "games" && test "$$(id -ng)" = "games" && test "$$(id -nu)" = "games"'
 # Multiple ExecStart= lines causes the issue #9702.
-ExecStart=/bin/sh -x -c 'test "$$(id -nG)" = "games" && test "$$(id -ng)" = "games" && test "$$(id -nu)" = "games"'
+ExecStart=sh -x -c 'test "$$(id -nG)" = "games" && test "$$(id -ng)" = "games" && test "$$(id -nu)" = "games"'
 DynamicUser=yes
 User=games
index bbb1af5fb384d3cbc4124c421625eef3f9dc7224..e494c335510439a2a04957777678f1a5989d5104 100644 (file)
@@ -3,8 +3,8 @@
 Description=Test DynamicUser with User= and SupplementaryGroups=
 
 [Service]
-ExecStart=/bin/sh -x -c 'for g in $$(id -G); do test "$$g" = "1" && exit 0; done; exit 1'
-ExecStart=/bin/sh -x -c 'test "$$(id -g)" = "1" && test "$$(id -u)" = "1"'
+ExecStart=sh -x -c 'for g in $$(id -G); do test "$$g" = "1" && exit 0; done; exit 1'
+ExecStart=sh -x -c 'test "$$(id -g)" = "1" && test "$$(id -u)" = "1"'
 Type=oneshot
 User=1
 DynamicUser=yes
index c5828c2a933fb1ce7388afd99212335ba3e07018..4ebfc20cde9cc09192cf0a748549417ab6755efa 100644 (file)
@@ -3,8 +3,8 @@
 Description=Test DynamicUser with User=
 
 [Service]
-ExecStart=/bin/sh -x -c 'for g in $$(id -G); do test "$$g" = "1" && exit 0; done; exit 1'
-ExecStart=/bin/sh -x -c 'test "$$(id -g)" = "1" && test "$$(id -u)" = "1"'
+ExecStart=sh -x -c 'for g in $$(id -G); do test "$$g" = "1" && exit 0; done; exit 1'
+ExecStart=sh -x -c 'test "$$(id -g)" = "1" && test "$$(id -u)" = "1"'
 Type=oneshot
 User=1
 DynamicUser=yes
index 790279ab17ebcbf8319ecaa0c7eea4ab87b7a665..d84a96b4912cbc2e13e75dd75a45766739cdbd86 100644 (file)
@@ -3,9 +3,9 @@
 Description=Test for RuntimeDirectory with RuntimeDirectoryPreserve=yes and DynamicUser=yes
 
 [Service]
-ExecStart=/bin/sh -x -c 'test -d %t/test-exec_runtimedirectorypreserve'
-ExecStart=/bin/sh -x -c 'test "$$RUNTIME_DIRECTORY" = "%t/test-exec_runtimedirectorypreserve"'
-ExecStart=/bin/sh -x -c 'touch $$RUNTIME_DIRECTORY/test'
+ExecStart=sh -x -c 'test -d %t/test-exec_runtimedirectorypreserve'
+ExecStart=sh -x -c 'test "$$RUNTIME_DIRECTORY" = "%t/test-exec_runtimedirectorypreserve"'
+ExecStart=sh -x -c 'touch $$RUNTIME_DIRECTORY/test'
 Type=oneshot
 RuntimeDirectory=test-exec_runtimedirectorypreserve
 RuntimeDirectoryPreserve=yes
index 18df74e4471adca4ccfba0d1fb2efb2791b24d4c..d6084b0f5b5caff0665886fcdcc2458beedd11d6 100644 (file)
@@ -3,10 +3,10 @@
 Description=Test for RuntimeDirectory with RuntimeDirectoryPreserve=yes and DynamicUser=yes 2nd trial
 
 [Service]
-ExecStart=/bin/sh -x -c 'test -d %t/test-exec_runtimedirectorypreserve'
-ExecStart=/bin/sh -x -c 'test "$$RUNTIME_DIRECTORY" = "%t/test-exec_runtimedirectorypreserve"'
-ExecStart=/bin/sh -x -c 'test -f $$RUNTIME_DIRECTORY/test'
-ExecStart=/bin/sh -x -c 'touch $$RUNTIME_DIRECTORY/test'
+ExecStart=sh -x -c 'test -d %t/test-exec_runtimedirectorypreserve'
+ExecStart=sh -x -c 'test "$$RUNTIME_DIRECTORY" = "%t/test-exec_runtimedirectorypreserve"'
+ExecStart=sh -x -c 'test -f $$RUNTIME_DIRECTORY/test'
+ExecStart=sh -x -c 'touch $$RUNTIME_DIRECTORY/test'
 Type=oneshot
 RuntimeDirectory=test-exec_runtimedirectorypreserve
 RuntimeDirectoryPreserve=yes
index 831a808f901871631f3f347c936af4bdfcf63afa..fad194183bbe9c41cc8e02fb89dc0b0b78ce5dd3 100644 (file)
@@ -3,10 +3,10 @@
 Description=Test for RuntimeDirectory with DynamicUser=yes migrated from RuntimeDirectoryPreserve=yes
 
 [Service]
-ExecStart=/bin/sh -x -c 'test -d %t/test-exec_runtimedirectorypreserve'
-ExecStart=/bin/sh -x -c 'test "$$RUNTIME_DIRECTORY" = "%t/test-exec_runtimedirectorypreserve"'
-ExecStart=/bin/sh -x -c 'test -f $$RUNTIME_DIRECTORY/test'
-ExecStart=/bin/sh -x -c 'touch $$RUNTIME_DIRECTORY/test'
+ExecStart=sh -x -c 'test -d %t/test-exec_runtimedirectorypreserve'
+ExecStart=sh -x -c 'test "$$RUNTIME_DIRECTORY" = "%t/test-exec_runtimedirectorypreserve"'
+ExecStart=sh -x -c 'test -f $$RUNTIME_DIRECTORY/test'
+ExecStart=sh -x -c 'touch $$RUNTIME_DIRECTORY/test'
 Type=oneshot
 RuntimeDirectory=test-exec_runtimedirectorypreserve
 DynamicUser=yes
index 2a5a1e1ff3caa99cbae6d3e75af1e79de714c9e6..12375afbb5e5a5f073f1a29aa7b1e8939504a4d8 100644 (file)
@@ -11,7 +11,7 @@ ExecStart=test -d %S/test-dynamicuser-migrate
 ExecStart=test -d %S/test-dynamicuser-migrate2/hoge
 ExecStart=touch %S/test-dynamicuser-migrate/yay
 ExecStart=touch %S/test-dynamicuser-migrate2/hoge/yayyay
-ExecStart=/bin/sh -x -c 'test "$$STATE_DIRECTORY" = "%S/test-dynamicuser-migrate:%S/test-dynamicuser-migrate2/hoge"'
+ExecStart=sh -x -c 'test "$$STATE_DIRECTORY" = "%S/test-dynamicuser-migrate:%S/test-dynamicuser-migrate2/hoge"'
 
 Type=oneshot
 DynamicUser=no
index e89f0c5aae1bc676df874233f8d3a9d0afd9e243..6fddd21cad480a1ca4e6cd3d8f3b63cbf88fd10a 100644 (file)
@@ -19,7 +19,7 @@ ExecStart=touch %S/test-dynamicuser-migrate/yay
 ExecStart=touch %S/test-dynamicuser-migrate2/hoge/yayyay
 ExecStart=touch %S/private/test-dynamicuser-migrate/yay
 ExecStart=touch %S/private/test-dynamicuser-migrate2/hoge/yayyay
-ExecStart=/bin/sh -x -c 'test "$$STATE_DIRECTORY" = "%S/test-dynamicuser-migrate:%S/test-dynamicuser-migrate2/hoge"'
+ExecStart=sh -x -c 'test "$$STATE_DIRECTORY" = "%S/test-dynamicuser-migrate:%S/test-dynamicuser-migrate2/hoge"'
 
 Type=oneshot
 DynamicUser=yes
index d601af272e4872597eb2e3ddd6d927defc15bb7a..ac6a0fbc201842ae196a74e4c075aa87571da413 100644 (file)
@@ -3,8 +3,8 @@
 Description=Test DynamicUser with SupplementaryGroups=
 
 [Service]
-ExecStart=/bin/sh -x -c 'for g in $$(id -G); do test "$$g" = "1" && exit 0; done; exit 1'
-ExecStart=/bin/sh -x -c 'for g in $$(id -G); do test "$$g" = "2" && exit 0; done; exit 1'
+ExecStart=sh -x -c 'for g in $$(id -G); do test "$$g" = "1" && exit 0; done; exit 1'
+ExecStart=sh -x -c 'for g in $$(id -G); do test "$$g" = "2" && exit 0; done; exit 1'
 Type=oneshot
 DynamicUser=yes
 SupplementaryGroups=1 2
index 6c3118643f257d6c5e3c42ac1ad25b132ae5e7b1..e5af6ffe6830e452b8c28a426ae14c955f206180 100644 (file)
@@ -3,7 +3,7 @@
 Description=Test for Environment
 
 [Service]
-ExecStart=/bin/sh -x -c 'test "$${VAR1-unset}" = "unset" && test "$${VAR2-unset}" = "unset" && test "$${VAR3-unset}" = "unset"'
+ExecStart=sh -x -c 'test "$${VAR1-unset}" = "unset" && test "$${VAR2-unset}" = "unset" && test "$${VAR3-unset}" = "unset"'
 Type=oneshot
 Environment="VAR1=word1 word2" VAR2=word3 "VAR3=$word 5 6"
 Environment=
index d9b8d22667f89c89c702aef0df4b5095893aa6ea..4199a46d844a8eccf1ae2f692aa38e8107132b38 100644 (file)
@@ -3,7 +3,7 @@
 Description=Test for Environment
 
 [Service]
-ExecStart=/bin/sh -x -c 'test "$$VAR1" = "word1 word2" && test "$$VAR2" = word3 && test "$$VAR3" = foobar'
+ExecStart=sh -x -c 'test "$$VAR1" = "word1 word2" && test "$$VAR2" = word3 && test "$$VAR3" = foobar'
 Type=oneshot
 Environment="VAR1=word1 word2" VAR2=word3 "VAR3=$word 5 6"
 Environment="VAR3=foobar"
index b5cb2a4445ab525f3539c3f748398572d5beeece..7396576d7d8d9fa26331c0372027457b8ec1f658 100644 (file)
@@ -3,7 +3,7 @@
 Description=Test for No Environment Variable Substitution
 
 [Service]
-ExecStart=/bin/sh -x -c 'test "$${VAR1-unset}" = "unset" && test "$${VAR2}" = "word3" && test "$${VAR3-unset}" = \'$word 5 6\''
+ExecStart=sh -x -c 'test "$${VAR1-unset}" = "unset" && test "$${VAR2}" = "word3" && test "$${VAR3-unset}" = \'$word 5 6\''
 ExecStart=:/bin/sh -x -c 'test "$${VAR1-unset}" != "unset" && test "$${VAR2}" != "word3" && test "$${VAR3-unset}" != \'$word 5 6\''
 Type=oneshot
 Environment="VAR2=word3" "VAR3=$word 5 6"
index 5655be0a22a7594dd26b98a2bbf3795c5be2de8d..7e3cb0ef42a21c6b40b6f6c92df6fe1dc49e2af9 100644 (file)
@@ -3,6 +3,6 @@
 Description=Test for Environment
 
 [Service]
-ExecStart=/bin/sh -x -c 'test "$$VAR1" = "word1 word2" && test "$$VAR2" = word3 && test "$$VAR3" = "\\$$word 5 6"'
+ExecStart=sh -x -c 'test "$$VAR1" = "word1 word2" && test "$$VAR2" = word3 && test "$$VAR3" = "\\$$word 5 6"'
 Type=oneshot
 Environment="VAR1=word1 word2" VAR2=word3 "VAR3=$word 5 6"
index 4ad5a9bb3b8cf886750e4d7d8de9355c1ccd54ae..3f739fa1e0470a4b5e0488608bdeff5ac4405cfc 100644 (file)
@@ -3,6 +3,6 @@
 Description=Test for EnvironmentFile
 
 [Service]
-ExecStart=/bin/sh -x -c 'test "$$VAR1" = "word1 word2" && test "$$VAR2" = word3 && test "$$VAR3" = "\\$$word 5 6" && test "$$VAR4" = "new\nline" && test "$$VAR5" = passwordwithbackslashes'
+ExecStart=sh -x -c 'test "$$VAR1" = "word1 word2" && test "$$VAR2" = word3 && test "$$VAR3" = "\\$$word 5 6" && test "$$VAR4" = "new\nline" && test "$$VAR5" = passwordwithbackslashes'
 Type=oneshot
 EnvironmentFile=/tmp/test-exec_environmentfile.conf
index 5969cc6764df0703e6fc6bc399737c6517ea12a7..424c4ac7b6a07e102ed6b9dfd616bbb45e4c40a5 100644 (file)
@@ -1,6 +1,6 @@
 # SPDX-License-Identifier: LGPL-2.1-or-later
 [Service]
-ExecStart=/bin/sh -x -c 'test "$$PATH" = "/usr" && test "$$VAR1" = word3 && test "$$VAR2" = "\\$$word 5 6"'
+ExecStart=sh -x -c 'test "$$PATH" = "/usr" && test "$$VAR1" = word3 && test "$$VAR2" = "\\$$word 5 6"'
 Type=oneshot
 ExecSearchPath=/tmp:/bin
 Environment="PATH=/usr" VAR1=word3 "VAR2=$word 5 6"
index b0fa6a36e49604e2dbd27a39692487750b64e3af..5c39d9ca63b28fc3c0fb341c0a7d5fbbd1f6c8fd 100644 (file)
@@ -1,6 +1,6 @@
 # SPDX-License-Identifier: LGPL-2.1-or-later
 [Service]
-ExecStart=/bin/sh -x -c 'test "$$VAR1" = "word1 word2" && test "$$VAR2" = word3 && test "$$VAR3" = "\\$$word 5 6" && test "$$PATH" = "/tmp:/bin"'
+ExecStart=sh -x -c 'test "$$VAR1" = "word1 word2" && test "$$VAR2" = word3 && test "$$VAR3" = "\\$$word 5 6" && test "$$PATH" = "/tmp:/bin"'
 Type=oneshot
 ExecSearchPath=/tmp:/bin
 Environment="VAR1=word1 word2" VAR2=word3 "VAR3=$word 5 6"
index 5f55a4b9346944bcbddd1dcc470e6c08b3e7b0ee..8741582231c4b255bb3cdb4334ee4f29da378848 100644 (file)
@@ -3,7 +3,7 @@
 Description=Test for ExecSearchPath with EnvironmentFile where EnvironmentFile sets PATH
 
 [Service]
-ExecStart=/bin/sh -x -c 'test "$$VAR1" = "word1 word2" && test "$$VAR2" = word3 && test "$$VAR3" = "\\$$word 5 6" && test "$$VAR4" = "new\nline" && test "$$VAR5" = passwordwithbackslashes && test "$$PATH" = /usr'
+ExecStart=sh -x -c 'test "$$VAR1" = "word1 word2" && test "$$VAR2" = word3 && test "$$VAR3" = "\\$$word 5 6" && test "$$VAR4" = "new\nline" && test "$$VAR5" = passwordwithbackslashes && test "$$PATH" = /usr'
 Type=oneshot
 EnvironmentFile=/tmp/test-exec_execsearchpath_environmentfile-set.conf
 ExecSearchPath=/tmp:/bin
index b8335bcf97c276c0be62f9de14c9530d4d595d02..53cede8f1beebe6db3ed8ff7c898c8c58379cd58 100644 (file)
@@ -3,7 +3,7 @@
 Description=Test for ExecSearchPath with EnvironmentFile where EnvironmentFile does not set PATH
 
 [Service]
-ExecStart=/bin/sh -x -c 'test "$$VAR1" = "word1 word2" && test "$$VAR2" = word3 && test "$$VAR3" = "\\$$word 5 6" && test "$$VAR4" = "new\nline" && test "$$VAR5" = passwordwithbackslashes && test "$$PATH" = "/tmp:/bin"'
+ExecStart=sh -x -c 'test "$$VAR1" = "word1 word2" && test "$$VAR2" = word3 && test "$$VAR3" = "\\$$word 5 6" && test "$$VAR4" = "new\nline" && test "$$VAR5" = passwordwithbackslashes && test "$$PATH" = "/tmp:/bin"'
 Type=oneshot
 ExecSearchPath=/tmp:/bin
 EnvironmentFile=/tmp/test-exec_execsearchpath_environmentfile.conf
index a1511616e2c86fb3ac8c517adcf5e00afe2e71a2..2d4e75a0c8b4f8ae269ad432ebff1ba9b0ef163d 100644 (file)
@@ -3,7 +3,7 @@
 Description=Test for PassEnvironment with ExecSearchPath with PATH set by user
 
 [Service]
-ExecStart=/bin/sh -x -c 'test "$$VAR1" = "word1 word2" && test "$$VAR2" = word3 && test "$$VAR3" = "\\$$word 5 6" && test "$$VAR4" = "new\nline" && test "$$VAR5" = passwordwithbackslashes && test "$$PATH" = "/usr"'
+ExecStart=sh -x -c 'test "$$VAR1" = "word1 word2" && test "$$VAR2" = word3 && test "$$VAR3" = "\\$$word 5 6" && test "$$VAR4" = "new\nline" && test "$$VAR5" = passwordwithbackslashes && test "$$PATH" = "/usr"'
 Type=oneshot
 PassEnvironment=VAR1 VAR2 VAR3 VAR4 VAR5 PATH
 ExecSearchPath=/tmp:/bin
index d8a41c1ed790d971211865ccf984c376e8f21643..5bdab471939d5c181b70844313f266d62cd101b6 100644 (file)
@@ -3,7 +3,7 @@
 Description=Test for PassEnvironment with ExecSearchPath with PATH not set by user
 
 [Service]
-ExecStart=/bin/sh -x -c 'test "$$VAR1" = "word1 word2" && test "$$VAR2" = word3 && test "$$VAR3" = "\\$$word 5 6" && test "$$VAR4" = "new\nline" && test "$$VAR5" = passwordwithbackslashes && test "$$PATH" = "/tmp:/bin"'
+ExecStart=sh -x -c 'test "$$VAR1" = "word1 word2" && test "$$VAR2" = word3 && test "$$VAR3" = "\\$$word 5 6" && test "$$VAR4" = "new\nline" && test "$$VAR5" = passwordwithbackslashes && test "$$PATH" = "/tmp:/bin"'
 Type=oneshot
 PassEnvironment=VAR1 VAR2 VAR3 VAR4 VAR5
 ExecSearchPath=/tmp:/bin
index 30d6b3280d8e601de9ae3c725d4d22259dfb7727..a2037e930df4b999a0fe945d0337dd977373f4df 100644 (file)
@@ -5,4 +5,4 @@ Description=Test for specifiers with exec search path
 [Service]
 Type=oneshot
 ExecSearchPath=/tmp:/bin:/usr/bin:%V
-ExecStart=/bin/sh -x -c 'test %V = /var/tmp && test "$$PATH" = "/tmp:/bin:/usr/bin:/var/tmp"'
+ExecStart=sh -x -c 'test %V = /var/tmp && test "$$PATH" = "/tmp:/bin:/usr/bin:/var/tmp"'
index a1e59c5d1157edf3b7042478365af7f171dd10d2..aebb1984a190a23146333e95d070b167d4b4b375 100644 (file)
@@ -3,6 +3,6 @@
 Description=Test for Group
 
 [Service]
-ExecStart=/bin/sh -x -c 'test "$$(id -n -g)" = "nfsnobody"'
+ExecStart=sh -x -c 'test "$$(id -n -g)" = "nfsnobody"'
 Type=oneshot
 Group=nfsnobody
index 58dce1e3d640b9061e81b4a40e3df21834699d0d..cf283cb65782e35ccfa88d8af3f0e3762457d548 100644 (file)
@@ -3,6 +3,6 @@
 Description=Test for Group
 
 [Service]
-ExecStart=/bin/sh -x -c 'test "$$(id -n -g)" = "nobody"'
+ExecStart=sh -x -c 'test "$$(id -n -g)" = "nobody"'
 Type=oneshot
 Group=nobody
index 7f167298903a226dda57856eabf060bbbc05dfc5..46c3dd3acf6890dc579b8675230107d4f1086e38 100644 (file)
@@ -3,6 +3,6 @@
 Description=Test for Group
 
 [Service]
-ExecStart=/bin/sh -x -c 'test "$$(id -n -g)" = "nogroup"'
+ExecStart=sh -x -c 'test "$$(id -n -g)" = "nogroup"'
 Type=oneshot
 Group=nogroup
index 9f21557d8260312ff71cf4f3edbbdf27b6e823ee..bd5ac2db3471a59a25ff864572574a8d958b5b52 100644 (file)
@@ -3,6 +3,6 @@
 Description=Test for Group (daemon)
 
 [Service]
-ExecStart=/bin/sh -x -c 'test "$$(id -n -g)" = "daemon"'
+ExecStart=sh -x -c 'test "$$(id -n -g)" = "daemon"'
 Type=oneshot
 Group=daemon
index e97248109d49124490c683ccd8ffb2af7eed792f..ce8b25871270a708e759165b69cb30bc783b3ae3 100644 (file)
@@ -3,6 +3,6 @@
 Description=Test for IgnoreSIGPIPE=no
 
 [Service]
-ExecStart=/bin/sh -x -c 'kill -PIPE 0'
+ExecStart=sh -x -c 'kill -PIPE 0'
 Type=oneshot
 IgnoreSIGPIPE=no
index ee3aa9a2b3eeddf2de842ebb3634f9ab252af967..a26f53c413e61db57d0a8be66b4af17c30f99e5f 100644 (file)
@@ -3,6 +3,6 @@
 Description=Test for IgnoreSIGPIPE=yes
 
 [Service]
-ExecStart=/bin/sh -x -c 'kill -PIPE 0'
+ExecStart=sh -x -c 'kill -PIPE 0'
 Type=oneshot
 IgnoreSIGPIPE=yes
index 520bc539b0a45f73cfc10ae8524922b225edfd5e..8580f5208e8c520068214a4d71801808f20c2a17 100644 (file)
@@ -4,5 +4,5 @@ Description=Test to make sure that InaccessiblePaths= disconnect mount propagati
 
 [Service]
 InaccessiblePaths=-/i-dont-exist
-ExecStart=/bin/sh -x -c 'd=$$(mktemp -d -p /tmp); trap "umount \'$$d\' && rmdir \'$$d\'" EXIT; mount -t tmpfs tmpfs "$$d"; grep "$$d" /proc/self/mountinfo && ! grep "$$d" /proc/$${PPID}/mountinfo && ! grep "$$d" /proc/1/mountinfo'
+ExecStart=sh -x -c 'd=$$(mktemp -d -p /tmp); trap "umount \'$$d\' && rmdir \'$$d\'" EXIT; mount -t tmpfs tmpfs "$$d"; grep "$$d" /proc/self/mountinfo && ! grep "$$d" /proc/$${PPID}/mountinfo && ! grep "$$d" /proc/1/mountinfo'
 Type=oneshot
index 0d64aa19c833763428cbbd110870aafc29b8e92c..64a570ce4df283e03abea2cef6b0b015bcc9d75e 100644 (file)
@@ -4,5 +4,5 @@ Description=Test to make sure that mount namespace setup works properly with the
 
 [Service]
 InaccessiblePaths=/sys
-ExecStart=/bin/sh -x -c 'test "$$(stat -c %%a /sys)" = "0"'
+ExecStart=sh -x -c 'test "$$(stat -c %%a /sys)" = "0"'
 Type=oneshot
index 3b946b785569d18cb1aae53f036ec5dc727ce928..569183f1737da2b0b72be6e825b21ec897ae7bfd 100644 (file)
@@ -3,6 +3,6 @@
 Description=Test for IOSchedulingClass=best-effort
 
 [Service]
-ExecStart=/bin/sh -x -c 'c=$$(LC_ALL=C ionice); test "$${c%%:*}" = "best-effort"'
+ExecStart=sh -x -c 'c=$$(LC_ALL=C ionice); test "$${c%%:*}" = "best-effort"'
 Type=oneshot
 IOSchedulingClass=best-effort
index b1e64bbbc167db48b6b651aff0868cf256c67172..93377ea0c07f1394bdeb97ba189181e5df9ed1e9 100644 (file)
@@ -3,6 +3,6 @@
 Description=Test for IOSchedulingClass=idle
 
 [Service]
-ExecStart=/bin/sh -x -c 'c=$$(LC_ALL=C ionice); test "$${c%%:*}" = "idle"'
+ExecStart=sh -x -c 'c=$$(LC_ALL=C ionice); test "$${c%%:*}" = "idle"'
 Type=oneshot
 IOSchedulingClass=idle
index 0494d458970c2363c5f240ff0e2233e32011df7e..b8198d16f2b7fe28a59efea9926908336644db9d 100644 (file)
@@ -4,6 +4,6 @@ Description=Test for IOSchedulingClass=none
 
 [Service]
 # Old kernels might report "none" here, new kernels "best-effort".
-ExecStart=/bin/sh -x -c 'c=$$(LC_ALL=C ionice); test "$${c%%:*}" = "none" -o "$${c%%:*}" = "best-effort"'
+ExecStart=sh -x -c 'c=$$(LC_ALL=C ionice); test "$${c%%:*}" = "none" -o "$${c%%:*}" = "best-effort"'
 Type=oneshot
 IOSchedulingClass=none
index ef8e2eb7f1652c91df59e8080fe18bd177c8b014..a7edb6d012e7529a6c825574babc15ff5570535a 100644 (file)
@@ -3,6 +3,6 @@
 Description=Test for IOSchedulingClass=realtime
 
 [Service]
-ExecStart=/bin/sh -x -c 'c=$$(LC_ALL=C ionice); test "$${c%%:*}" = "realtime"'
+ExecStart=sh -x -c 'c=$$(LC_ALL=C ionice); test "$${c%%:*}" = "realtime"'
 Type=oneshot
 IOSchedulingClass=realtime
index 3a29b6d13dcc5e53e0818abd95af03e6ad50cd29..9da19e6369c640f5c970af5a6b3b2c6374f7ad1f 100644 (file)
@@ -3,9 +3,9 @@
 Description=Test for LoadCredential=
 
 [Service]
-ExecStart=/bin/sh -x -c 'test "$$(cat %d/test-execute.load-credential)" = "foo"'
-ExecStartPost=/bin/sh -x -c 'test "$$(cat %d/test-execute.load-credential)" = "foo"'
-ExecStop=/bin/sh -x -c 'test "$$(cat %d/test-execute.load-credential)" = "foo"'
-ExecStopPost=/bin/sh -x -c 'test "$$(cat %d/test-execute.load-credential)" = "foo"'
+ExecStart=sh -x -c 'test "$$(cat %d/test-execute.load-credential)" = "foo"'
+ExecStartPost=sh -x -c 'test "$$(cat %d/test-execute.load-credential)" = "foo"'
+ExecStop=sh -x -c 'test "$$(cat %d/test-execute.load-credential)" = "foo"'
+ExecStopPost=sh -x -c 'test "$$(cat %d/test-execute.load-credential)" = "foo"'
 Type=oneshot
 LoadCredential=test-execute.load-credential
index 49277e3d517d455131a21c1e1d8d0499e95e80db..07c0525dcc611cb45a1fb1e1c27a5d88c6021899 100644 (file)
@@ -3,14 +3,14 @@
 Description=Test for NetworkNamespacePath= without mount namespacing
 
 [Service]
-ExecStart=/bin/sh -x -c '! ip link show dummy-test-exec'
-ExecStart=/bin/sh -x -c 'test ! -e /proc/sys/net/ipv4/conf/dummy-test-exec'
+ExecStart=sh -x -c '! ip link show dummy-test-exec'
+ExecStart=sh -x -c 'test ! -e /proc/sys/net/ipv4/conf/dummy-test-exec'
 # Without mount namespacing, we can access the dummy-test-exec interface through sysfs.
-ExecStart=/bin/sh -x -c 'test -e /sys/class/net/dummy-test-exec'
-ExecStart=/bin/sh -x -c 'ip link show dummy-test-ns'
-ExecStart=/bin/sh -x -c 'test -e /proc/sys/net/ipv4/conf/dummy-test-ns'
+ExecStart=sh -x -c 'test -e /sys/class/net/dummy-test-exec'
+ExecStart=sh -x -c 'ip link show dummy-test-ns'
+ExecStart=sh -x -c 'test -e /proc/sys/net/ipv4/conf/dummy-test-ns'
 # Without mount namespacing, we cannot access the dummy-test-ns interface through sysfs.
-ExecStart=/bin/sh -x -c 'test ! -e /sys/class/net/dummy-test-ns'
+ExecStart=sh -x -c 'test ! -e /sys/class/net/dummy-test-ns'
 Type=oneshot
 NetworkNamespacePath=/run/netns/test-execute-netns
 PrivateMounts=no
index 078fba8fa2f2915edf71d1f60fbe806b8ee715e8..10bc1923b794be80ec39445fb5fc6baffd9a5323 100644 (file)
@@ -3,14 +3,14 @@
 Description=Test for NetworkNamespacePath= with mount namespacing
 
 [Service]
-ExecStart=/bin/sh -x -c '! ip link show dummy-test-exec'
-ExecStart=/bin/sh -x -c 'test ! -e /proc/sys/net/ipv4/conf/dummy-test-exec'
+ExecStart=sh -x -c '! ip link show dummy-test-exec'
+ExecStart=sh -x -c 'test ! -e /proc/sys/net/ipv4/conf/dummy-test-exec'
 # With mount namespacing, we cannot access the dummy-test-exec interface through sysfs.
-ExecStart=/bin/sh -x -c 'test ! -e /sys/class/net/dummy-test-exec'
-ExecStart=/bin/sh -x -c 'ip link show dummy-test-ns'
-ExecStart=/bin/sh -x -c 'test -e /proc/sys/net/ipv4/conf/dummy-test-ns'
+ExecStart=sh -x -c 'test ! -e /sys/class/net/dummy-test-exec'
+ExecStart=sh -x -c 'ip link show dummy-test-ns'
+ExecStart=sh -x -c 'test -e /proc/sys/net/ipv4/conf/dummy-test-ns'
 # With mount namespacing, we can access the dummy-test-ns interface through sysfs.
-ExecStart=/bin/sh -x -c 'test -e /sys/class/net/dummy-test-ns'
+ExecStart=sh -x -c 'test -e /sys/class/net/dummy-test-ns'
 Type=oneshot
 NetworkNamespacePath=/run/netns/test-execute-netns
 # NetworkNamespacePath= implies PrivateMounts=yes
index 5d954da6ace9cb9ad68ef9be14ca44a7002281b6..503be5a770930a85f34583ed57df92a172ed800b 100644 (file)
@@ -7,5 +7,5 @@ Type=oneshot
 # This should work, as we explicitly disable the effect of NoExecPaths=
 ExecStart=+/bin/sh -c '/bin/cat /dev/null'
 # This should also work, as we do not disable the effect of NoExecPaths= but invert the exit code
-ExecStart=/bin/sh -x -c '! /bin/cat /dev/null'
+ExecStart=sh -x -c '! /bin/cat /dev/null'
 NoExecPaths=/bin/cat
index 25b5f1ffcc8221703d83f18b7d5fbf2f6e31d217..56560307968020ff09225c6f0830eac6b303edb4 100644 (file)
@@ -3,6 +3,6 @@
 Description=Test for OOMScoreAdjust
 
 [Service]
-ExecStart=/bin/sh -x -c 'c=$$(cat /proc/self/oom_score_adj); test "$$c" -eq -100'
+ExecStart=sh -x -c 'c=$$(cat /proc/self/oom_score_adj); test "$$c" -eq -100'
 Type=oneshot
 OOMScoreAdjust=-100
index ea6c23f78e808dfab0c153b93f81a7b485f681e5..a2079b841ecd318b02e256d38d41bbcaecca58a2 100644 (file)
@@ -3,6 +3,6 @@
 Description=Test for OOMScoreAdjust
 
 [Service]
-ExecStart=/bin/sh -x -c 'c=$$(cat /proc/self/oom_score_adj); test "$$c" -eq 100'
+ExecStart=sh -x -c 'c=$$(cat /proc/self/oom_score_adj); test "$$c" -eq 100'
 Type=oneshot
 OOMScoreAdjust=100
index 6b19a12bb7e24617fc3e063d0c8fdb117420d649..b2e5c200162f5991c859d64c264a3460699389d4 100644 (file)
@@ -3,6 +3,6 @@
 Description=Test for PassEnvironment with variables absent from the execution environment
 
 [Service]
-ExecStart=/bin/sh -x -c 'test "$${VAR1-unset}" = "unset" && test "$${VAR2-unset}" = "unset" && test "$${VAR3-unset}" = "unset" && test "$${VAR4-unset}" = "unset" && test "$${VAR5-unset}" = "unset"'
+ExecStart=sh -x -c 'test "$${VAR1-unset}" = "unset" && test "$${VAR2-unset}" = "unset" && test "$${VAR3-unset}" = "unset" && test "$${VAR4-unset}" = "unset" && test "$${VAR5-unset}" = "unset"'
 Type=oneshot
 PassEnvironment=VAR1 VAR2 VAR3 VAR4 VAR5
index 6ffc5e7de680ed045f2798fe4b66055150d59bf4..a5fd092783eba99750c293103dd2f88eb88eb548 100644 (file)
@@ -3,7 +3,7 @@
 Description=Test for PassEnvironment and erasing the variable list
 
 [Service]
-ExecStart=/bin/sh -x -c 'test "$${VAR1-unset}" = "unset" && test "$${VAR2-unset}" = "unset" && test "$${VAR3-unset}" = "unset" && test "$${VAR4-unset}" = "unset" && test "$${VAR5-unset}" = "unset"'
+ExecStart=sh -x -c 'test "$${VAR1-unset}" = "unset" && test "$${VAR2-unset}" = "unset" && test "$${VAR3-unset}" = "unset" && test "$${VAR4-unset}" = "unset" && test "$${VAR5-unset}" = "unset"'
 Type=oneshot
 PassEnvironment=VAR1 VAR2 VAR3 VAR4 VAR5
 PassEnvironment=
index b8e904fb6d28d722cdc4790282fa89a67a5da0f0..f3b886c5c6920add4ac7aee2934037b16afd3da3 100644 (file)
@@ -3,7 +3,7 @@
 Description=Test for PassEnvironment with a variable name repeated
 
 [Service]
-ExecStart=/bin/sh -x -c 'test "$$VAR1" = "word1 word2" && test "$$VAR2" = word3 && test "$$VAR3" = "\\$$word 5 6" && test "$$VAR4" = "new\nline" && test "$$VAR5" = passwordwithbackslashes'
+ExecStart=sh -x -c 'test "$$VAR1" = "word1 word2" && test "$$VAR2" = word3 && test "$$VAR3" = "\\$$word 5 6" && test "$$VAR4" = "new\nline" && test "$$VAR5" = passwordwithbackslashes'
 Type=oneshot
 PassEnvironment=VAR1 VAR2
 PassEnvironment=VAR1 VAR3
index b69592ad6a9db12fee88c68642294f4c3c861150..1dcbcf97c88da6898cc75774c1a83f64cc30d2c0 100644 (file)
@@ -3,6 +3,6 @@
 Description=Test for PassEnvironment
 
 [Service]
-ExecStart=/bin/sh -x -c 'test "$$VAR1" = "word1 word2" && test "$$VAR2" = word3 && test "$$VAR3" = "\\$$word 5 6" && test "$$VAR4" = "new\nline" && test "$$VAR5" = passwordwithbackslashes'
+ExecStart=sh -x -c 'test "$$VAR1" = "word1 word2" && test "$$VAR2" = word3 && test "$$VAR3" = "\\$$word 5 6" && test "$$VAR4" = "new\nline" && test "$$VAR5" = passwordwithbackslashes'
 Type=oneshot
 PassEnvironment=VAR1 VAR2 VAR3 VAR4 VAR5
index 0783a873640408166721138fc2822a67d12aaf54..e4ea294dcc24bd4aba58a0c822769c34d650deb7 100644 (file)
@@ -3,6 +3,6 @@
 Description=Test for Personality=aarch64
 
 [Service]
-ExecStart=/bin/sh -c 'echo $(uname -m); exit $(test $(uname -m) = "aarch64")'
+ExecStart=sh -c 'echo $(uname -m); exit $(test $(uname -m) = "aarch64")'
 Type=oneshot
 Personality=aarch64
index 0531ad1164dcf58cdf8652b380e97254e870984b..31c6b258c71e3998c865222e7d1a4d34464140b0 100644 (file)
@@ -2,6 +2,6 @@
 Description=Test for Personality=loongarch64
 
 [Service]
-ExecStart=/bin/sh -c 'echo $(uname -m); exit $(test $(uname -m) = "loongarch64")'
+ExecStart=sh -c 'echo $(uname -m); exit $(test $(uname -m) = "loongarch64")'
 Type=oneshot
 Personality=loongarch64
index 72f063a59c1556ac00ecceeab32be6527c5e53ff..dd83bf66eec7b822cd9b81e0639fb1f3c15c67ca 100644 (file)
@@ -3,6 +3,6 @@
 Description=Test for Personality=ppc64
 
 [Service]
-ExecStart=/bin/sh -c 'echo $(uname -m); exit $(test $(uname -m) = "ppc64")'
+ExecStart=sh -c 'echo $(uname -m); exit $(test $(uname -m) = "ppc64")'
 Type=oneshot
 Personality=ppc64
index 5e38029c05433a5ca29dbfebb2ec9060e9e57b58..3f19d825db6dc74ad21422fc718e7fd9a0e24ebd 100644 (file)
@@ -3,6 +3,6 @@
 Description=Test for Personality=ppc64le
 
 [Service]
-ExecStart=/bin/sh -c 'echo $(uname -m); exit $(test $(uname -m) = "ppc64le")'
+ExecStart=sh -c 'echo $(uname -m); exit $(test $(uname -m) = "ppc64le")'
 Type=oneshot
 Personality=ppc64le
index 439dc5fea87ac6a2e078dfe1207188384c13ff0f..7d120cde2d2e83683a9fb4d5802c21059e40e9f9 100644 (file)
@@ -3,6 +3,6 @@
 Description=Test for Personality=s390
 
 [Service]
-ExecStart=/bin/sh -x -c 'c=$$(uname -m); test "$$c" = "s390"'
+ExecStart=sh -x -c 'c=$$(uname -m); test "$$c" = "s390"'
 Type=oneshot
 Personality=s390
index c6a0a4061c7a4a9ae198f76d6ad6a57480e863c6..e7b945ca1491c3b7d9f68635bb52137e20efff89 100644 (file)
@@ -3,6 +3,6 @@
 Description=Test for Personality=x86-64
 
 [Service]
-ExecStart=/bin/sh -x -c 'c=$$(uname -m); test "$$c" = "x86_64"'
+ExecStart=sh -x -c 'c=$$(uname -m); test "$$c" = "x86_64"'
 Type=oneshot
 Personality=x86-64
index 8b820b31c6eee67e484a20fcdffccd1ae6441d02..95ec353cb1e8c45825353d052a38131acbd2db07 100644 (file)
@@ -3,6 +3,6 @@
 Description=Test for Personality=x86
 
 [Service]
-ExecStart=/bin/sh -x -c 'c=$$(uname -m); test "$$c" = "i686" -o "$$c" = "x86_64"'
+ExecStart=sh -x -c 'c=$$(uname -m); test "$$c" = "i686" -o "$$c" = "x86_64"'
 Type=oneshot
 Personality=x86
index dbbbb4ee33d0eca21fbd160054f7ebb64ba316b0..c2229a4a7af4238c9113b070a66ebab771a12223 100644 (file)
@@ -3,8 +3,8 @@
 Description=Test for PrivateDevices=yes with a bind mounted device
 
 [Service]
-ExecStart=/bin/sh -c 'test -c /dev/kmsg'
-ExecStart=/bin/sh -c 'test ! -w /dev/'
+ExecStart=sh -c 'test -c /dev/kmsg'
+ExecStart=sh -c 'test ! -w /dev/'
 Type=oneshot
 PrivateDevices=yes
 BindPaths=/dev/kmsg
index 021cadff529291d37f51ab0a87876770802ab4e3..8f09c4ab95c42e5c967553a3377ad8099bdb04a5 100644 (file)
@@ -3,7 +3,7 @@
 Description=Test for PrivateDevices=yes with prefix
 
 [Service]
-ExecStart=/bin/sh -x -c '! test -c /dev/kmsg'
+ExecStart=sh -x -c '! test -c /dev/kmsg'
 ExecStart=+/bin/sh -x -c 'test -c /dev/kmsg'
 Type=oneshot
 PrivateDevices=yes
index a07e82284158a38b0ecf98ae03280eac48975526..811f4ad6853b629831d26242b957965c36ea3ec1 100644 (file)
@@ -5,5 +5,5 @@ Description=Test CAP_MKNOD capability for PrivateDevices=no
 [Service]
 PrivateDevices=no
 # sed: remove dropped (cap_xxx-[epi]) and IAB capabilities from the output
-ExecStart=/bin/sh -x -c 'capsh --print | sed -re "s/[^ ]+?\-[epi]+//g" -e '/IAB/d' | grep cap_mknod'
+ExecStart=sh -x -c 'capsh --print | sed -re "s/[^ ]+?\-[epi]+//g" -e '/IAB/d' | grep cap_mknod'
 Type=oneshot
index b0ce2d409c55bff2bd90b2f7f69ef3a404944352..47be622b5fb7720021b4a21f77c0c6a620a5bfd2 100644 (file)
@@ -5,5 +5,5 @@ Description=Test CAP_SYS_RAWIO capability for PrivateDevices=no
 [Service]
 PrivateDevices=no
 # sed: remove dropped (cap_xxx-[epi]) and IAB capabilities from the output
-ExecStart=/bin/sh -x -c 'capsh --print | sed -re "s/[^ ]+?\-[epi]+//g" -e '/IAB/d' | grep cap_sys_rawio'
+ExecStart=sh -x -c 'capsh --print | sed -re "s/[^ ]+?\-[epi]+//g" -e '/IAB/d' | grep cap_sys_rawio'
 Type=oneshot
index 31a5e3c72b23470f17ef62253c5ccb9c0e3262ce..5b8a05133bfed1fba69553ff6763ebf1646f661e 100644 (file)
@@ -3,6 +3,6 @@
 Description=Test for PrivateDevices=no
 
 [Service]
-ExecStart=/bin/sh -x -c 'test -c /dev/kmsg'
+ExecStart=sh -x -c 'test -c /dev/kmsg'
 Type=oneshot
 PrivateDevices=no
index f798f3167082573b3b7c54fc55268fd5d27e6ab1..3d29a9cb05073c87484ce505219afa4267050378 100644 (file)
@@ -5,5 +5,5 @@ Description=Test CAP_MKNOD capability for PrivateDevices=yes
 [Service]
 PrivateDevices=yes
 # sed: remove dropped (cap_xxx-[epi]) and IAB capabilities from the output
-ExecStart=/bin/sh -x -c '! capsh --print | sed -re "s/[^ ]+?\-[epi]+//g" -e '/IAB/d' | grep cap_mknod'
+ExecStart=sh -x -c '! capsh --print | sed -re "s/[^ ]+?\-[epi]+//g" -e '/IAB/d' | grep cap_mknod'
 Type=oneshot
index d902c234e204032da651c99b11e11099de2d1fd3..b1c0617837c62dd5e4d5fecf6c6464588863c020 100644 (file)
@@ -5,5 +5,5 @@ Description=Test CAP_SYS_RAWIO capability for PrivateDevices=yes
 [Service]
 PrivateDevices=yes
 # sed: remove dropped (cap_xxx-[epi]) and IAB capabilities from the output
-ExecStart=/bin/sh -x -c '! capsh --print | sed -re "s/[^ ]+?\-[epi]+//g" -e '/IAB/d' | grep cap_sys_rawio'
+ExecStart=sh -x -c '! capsh --print | sed -re "s/[^ ]+?\-[epi]+//g" -e '/IAB/d' | grep cap_sys_rawio'
 Type=oneshot
index a39ae0f846899be85ca3d25bdcab1817922dda0c..094ac227247a533139c31a2ab7697c6d31870c7a 100644 (file)
@@ -8,10 +8,10 @@ Group=daemon
 Type=oneshot
 
 # Check the group applied
-ExecStart=/bin/sh -x -c 'test "$$(id -n -g)" = "daemon"'
+ExecStart=sh -x -c 'test "$$(id -n -g)" = "daemon"'
 
 # Check that the namespace applied
-ExecStart=/bin/sh -c 'test ! -c /dev/kmsg'
+ExecStart=sh -c 'test ! -c /dev/kmsg'
 
 # Check that the owning group of a node is not daemon (should be the host root)
-ExecStart=/bin/sh -x -c 'test ! "$$(stat -c %%G /dev/stderr)" = "daemon"'
+ExecStart=sh -x -c 'test ! "$$(stat -c %%G /dev/stderr)" = "daemon"'
index 564e95892532c4304097f326a6eb22fae1cc23a6..2d32753053aefb7799daa14f8fb3e0bdf6220c19 100644 (file)
@@ -3,6 +3,6 @@
 Description=Test for PrivateDevices=yes
 
 [Service]
-ExecStart=/bin/sh -c 'test ! -c /dev/kmsg'
+ExecStart=sh -c 'test ! -c /dev/kmsg'
 Type=oneshot
 PrivateDevices=yes
index 83708df830db697d79155ac96dbe97af73a6da10..c16102d07f43839e50ce09385793d50c5bb9c0aa 100644 (file)
@@ -3,10 +3,10 @@
 Description=Test for PrivateNetwork= without mount namespacing
 
 [Service]
-ExecStart=/bin/sh -x -c '! ip link show dummy-test-exec'
-ExecStart=/bin/sh -x -c 'test ! -e /proc/sys/net/ipv4/conf/dummy-test-exec'
+ExecStart=sh -x -c '! ip link show dummy-test-exec'
+ExecStart=sh -x -c 'test ! -e /proc/sys/net/ipv4/conf/dummy-test-exec'
 # Without mount namespacing, we can access the dummy-test-exec interface through sysfs
-ExecStart=/bin/sh -x -c 'test -d /sys/class/net/dummy-test-exec'
+ExecStart=sh -x -c 'test -d /sys/class/net/dummy-test-exec'
 Type=oneshot
 PrivateNetwork=yes
 PrivateMounts=no
index 874f10084ef1b31d0dbb7d774b17349082a44682..eb48d6e0c56a61934a115401cf4c7fb7f6bcce0e 100644 (file)
@@ -3,10 +3,10 @@
 Description=Test for PrivateNetwork= with mount namespacing
 
 [Service]
-ExecStart=/bin/sh -x -c '! ip link show dummy-test-exec'
-ExecStart=/bin/sh -x -c 'test ! -e /proc/sys/net/ipv4/conf/dummy-test-exec'
+ExecStart=sh -x -c '! ip link show dummy-test-exec'
+ExecStart=sh -x -c 'test ! -e /proc/sys/net/ipv4/conf/dummy-test-exec'
 # With mount namespacing, we cannot access the dummy-test-exec interface through sysfs.
-ExecStart=/bin/sh -x -c 'test ! -e /sys/class/net/dummy-test-exec'
+ExecStart=sh -x -c 'test ! -e /sys/class/net/dummy-test-exec'
 Type=oneshot
 PrivateNetwork=yes
 # PrivateNetwork=yes implies PrivateMounts=yes
index f67afee101cc89d39103cd3f0fa4dc0a63c6b025..9dfcecc02c139e545901fa43e18f0563153504de 100644 (file)
@@ -3,7 +3,7 @@
 Description=Test for PrivateTmp=yes with prefix
 
 [Service]
-ExecStart=/bin/sh -x -c 'test ! -f /tmp/test-exec_privatetmp'
+ExecStart=sh -x -c 'test ! -f /tmp/test-exec_privatetmp'
 ExecStart=+/bin/sh -x -c 'test -f /tmp/test-exec_privatetmp'
 Type=oneshot
 PrivateTmp=yes
index 6a8a3fc319387d8eb70cbca08553808bfad029c6..599203abb80f92ce4f5768067ef5c8358553485b 100644 (file)
@@ -3,6 +3,6 @@
 Description=Test for PrivateTmp=no
 
 [Service]
-ExecStart=/bin/sh -x -c 'test -f /tmp/test-exec_privatetmp'
+ExecStart=sh -x -c 'test -f /tmp/test-exec_privatetmp'
 Type=oneshot
 PrivateTmp=no
index 6395be0842d6bf6b651382e01939b9e0666ccd47..5ea52639f0f90ba63da7fedbf85212ab135004bb 100644 (file)
@@ -3,6 +3,6 @@
 Description=Test for PrivateTmp=yes
 
 [Service]
-ExecStart=/bin/sh -x -c 'test ! -f /tmp/test-exec_privatetmp'
+ExecStart=sh -x -c 'test ! -f /tmp/test-exec_privatetmp'
 Type=oneshot
 PrivateTmp=yes
index f84e6b6f037ced650971ea57f51663773663feef..c51cacf40d4c263677acbbed6869999e530cce18 100644 (file)
@@ -7,4 +7,4 @@ Description=Test ProtectHome=tmpfs vs ProtectSystem=strict
 ProtectHome=tmpfs
 ProtectSystem=strict
 Type=oneshot
-ExecStart=/bin/sh -x -c 'test "$$(stat -fc %%T /home)" = "tmpfs"'
+ExecStart=sh -x -c 'test "$$(stat -fc %%T /home)" = "tmpfs"'
index 54789627555fd562142f94d203e28c9a13e99b81..be64c58089f53c85e22238824a2c86f31edc7050 100644 (file)
@@ -5,5 +5,5 @@ Description=Test CAP_SYSLOG for ProtectKernelLogs=no
 [Service]
 ProtectKernelLogs=no
 # sed: remove dropped (cap_xxx-[epi]) and IAB capabilities from the output
-ExecStart=/bin/sh -x -c 'capsh --print | sed -re "s/[^ ]+?\-[epi]+//g" -e '/IAB/d' | grep cap_syslog'
+ExecStart=sh -x -c 'capsh --print | sed -re "s/[^ ]+?\-[epi]+//g" -e '/IAB/d' | grep cap_syslog'
 Type=oneshot
index 6fe12410d96b06eae7617a31e14f9147644df393..646ff751a7b5622982d0375e2e09a267e1df70be 100644 (file)
@@ -5,5 +5,5 @@ Description=Test CAP_SYSLOG for ProtectKernelLogs=yes
 [Service]
 ProtectKernelLogs=yes
 # sed: remove dropped (cap_xxx-[epi]) and IAB capabilities from the output
-ExecStart=/bin/sh -x -c '! capsh --print | sed -re "s/[^ ]+?\-[epi]+//g" -e '/IAB/d' | grep cap_syslog'
+ExecStart=sh -x -c '! capsh --print | sed -re "s/[^ ]+?\-[epi]+//g" -e '/IAB/d' | grep cap_syslog'
 Type=oneshot
index 7236af2b245e226467d91243fe6f5a7da743c941..cefdb60991ffd5348b4fceb17d2a33a32d6832fd 100644 (file)
@@ -5,5 +5,5 @@ Description=Test CAP_SYS_MODULE ProtectKernelModules=no
 [Service]
 ProtectKernelModules=no
 # sed: remove dropped (cap_xxx-[epi]) and IAB capabilities from the output
-ExecStart=/bin/sh -x -c 'capsh --print | sed -re "s/[^ ]+?\-[epi]+//g" -e '/IAB/d' | grep cap_sys_module'
+ExecStart=sh -x -c 'capsh --print | sed -re "s/[^ ]+?\-[epi]+//g" -e '/IAB/d' | grep cap_sys_module'
 Type=oneshot
index e40160daf50d182d77035bb16667db13cf2fc178..1f327a28aba757ebef0513d31e5004a34409e72a 100644 (file)
@@ -5,5 +5,5 @@ Description=Test CAP_SYS_MODULE for ProtectKernelModules=yes
 [Service]
 ProtectKernelModules=yes
 # sed: remove dropped (cap_xxx-[epi]) and IAB capabilities from the output
-ExecStart=/bin/sh -x -c '! capsh --print | sed -re "s/[^ ]+?\-[epi]+//g" -e '/IAB/d' | grep cap_sys_module'
+ExecStart=sh -x -c '! capsh --print | sed -re "s/[^ ]+?\-[epi]+//g" -e '/IAB/d' | grep cap_sys_module'
 Type=oneshot
index 0ecf1a2b6ce2000652415077b21aab2d2c5d9ed9..16399bd2061800597338780db911c8c173f6c8d4 100644 (file)
@@ -4,5 +4,5 @@ Description=Test to make sure that passing ProtectKernelModules=yes disconnect m
 
 [Service]
 ProtectKernelModules=yes
-ExecStart=/bin/sh -x -c 'd=$$(mktemp -d -p /tmp); trap "umount \'$$d\' && rmdir \'$$d\'" EXIT; mount -t tmpfs tmpfs "$$d"; grep "$$d" /proc/self/mountinfo && ! grep "$$d" /proc/$${PPID}/mountinfo && ! grep "$$d" /proc/1/mountinfo'
+ExecStart=sh -x -c 'd=$$(mktemp -d -p /tmp); trap "umount \'$$d\' && rmdir \'$$d\'" EXIT; mount -t tmpfs tmpfs "$$d"; grep "$$d" /proc/self/mountinfo && ! grep "$$d" /proc/$${PPID}/mountinfo && ! grep "$$d" /proc/1/mountinfo'
 Type=oneshot
index abc180b3b38930ac5d4a6e225e48420e66057207..e896bac5fdad40f356a2785f5ee91ee0c349c089 100644 (file)
@@ -4,5 +4,5 @@ Description=Test to make sure that passing ReadOnlyPaths= disconnect mount propa
 
 [Service]
 ReadOnlyPaths=-/i-dont-exist
-ExecStart=/bin/sh -x -c 'd=$$(mktemp -d -p /tmp); trap "umount \'$$d\' && rmdir \'$$d\'" EXIT; mount -t tmpfs tmpfs "$$d"; grep "$$d" /proc/self/mountinfo && ! grep "$$d" /proc/$${PPID}/mountinfo && ! grep "$$d" /proc/1/mountinfo'
+ExecStart=sh -x -c 'd=$$(mktemp -d -p /tmp); trap "umount \'$$d\' && rmdir \'$$d\'" EXIT; mount -t tmpfs tmpfs "$$d"; grep "$$d" /proc/self/mountinfo && ! grep "$$d" /proc/$${PPID}/mountinfo && ! grep "$$d" /proc/1/mountinfo'
 Type=oneshot
index 5587e8dce71eea767d94fea7cbbecee8bdc35e84..80e6c83bf74b2f7d5cb82a001fce3b9251455908 100644 (file)
@@ -7,6 +7,6 @@ Type=oneshot
 # This should work, as we explicitly disable the effect of ReadOnlyPaths=
 ExecStart=+/bin/sh -c 'touch /tmp/thisisasimpletest'
 # This should also work, as we do not disable the effect of ReadOnlyPaths= but invert the exit code
-ExecStart=/bin/sh -x -c '! touch /tmp/thisisasimpletest'
+ExecStart=sh -x -c '! touch /tmp/thisisasimpletest'
 ExecStart=+/bin/sh -c 'rm /tmp/thisisasimpletest'
 ReadOnlyPaths=/tmp
index 71c7e7b92683eca29ce20040a91e996ad3286f96..7a183672c73b7b92499f55974028e921b45b5f09 100644 (file)
@@ -5,5 +5,5 @@ Description=Test for ReadOnlyPaths=
 [Service]
 ReadOnlyPaths=/etc -/i-dont-exist /usr
 BindPaths=/etc:/tmp/etc2
-ExecStart=/bin/sh -x -c 'test ! -w /etc && test ! -w /usr && test ! -e /i-dont-exist && test -w /var'
+ExecStart=sh -x -c 'test ! -w /etc && test ! -w /usr && test ! -e /i-dont-exist && test -w /var'
 Type=oneshot
index 21814c2f26289fb0b89021bd6e11be66000754c8..a0eff8bcecc897ff24a6a0f021540c820ff7df48 100644 (file)
@@ -5,6 +5,6 @@ Description=Test for ReadOnlyPaths=
 [Service]
 ReadOnlyPaths=/usr /etc /sys /dev -/i-dont-exist
 PrivateDevices=yes
-ExecStart=/bin/sh -x -c 'test ! -w /usr && test ! -w /etc && test ! -w /sys && test ! -w /sys/fs/cgroup'
-ExecStart=/bin/sh -x -c 'test ! -w /dev && test ! -w /dev/shm && test ! -e /i-dont-exist && test -w /var'
+ExecStart=sh -x -c 'test ! -w /usr && test ! -w /etc && test ! -w /sys && test ! -w /sys/fs/cgroup'
+ExecStart=sh -x -c 'test ! -w /dev && test ! -w /dev/shm && test ! -e /i-dont-exist && test -w /var'
 Type=oneshot
index 35e736f74261cd261c1201cca790ebf3a4be04c2..9b844cf4338db7b176def66842177dbcc08af778 100644 (file)
@@ -4,5 +4,5 @@ Description=Test to make sure that passing ReadWritePaths= disconnect mount prop
 
 [Service]
 ReadWritePaths=-/i-dont-exist
-ExecStart=/bin/sh -x -c 'd=$$(mktemp -d -p /tmp); trap "umount \'$$d\' && rmdir \'$$d\'" EXIT; mount -t tmpfs tmpfs "$$d"; grep "$$d" /proc/self/mountinfo && ! grep "$$d" /proc/$${PPID}/mountinfo && ! grep "$$d" /proc/1/mountinfo'
+ExecStart=sh -x -c 'd=$$(mktemp -d -p /tmp); trap "umount \'$$d\' && rmdir \'$$d\'" EXIT; mount -t tmpfs tmpfs "$$d"; grep "$$d" /proc/self/mountinfo && ! grep "$$d" /proc/$${PPID}/mountinfo && ! grep "$$d" /proc/1/mountinfo'
 Type=oneshot
index 580bac94eacadb239b9cdf84fc704e58b7ad8172..e75e0d23800ab28f6586566c4561ca766c1112b9 100644 (file)
@@ -3,8 +3,8 @@
 Description=Test for RuntimeDirectoryMode
 
 [Service]
-ExecStart=/bin/sh -x -c 'mode=$$(stat -c %%a %t/test-exec_runtimedirectory-mode); test "$$mode" = "750"'
-ExecStart=/bin/sh -x -c 'test "$$RUNTIME_DIRECTORY" = "%t/test-exec_runtimedirectory-mode"'
+ExecStart=sh -x -c 'mode=$$(stat -c %%a %t/test-exec_runtimedirectory-mode); test "$$mode" = "750"'
+ExecStart=sh -x -c 'test "$$RUNTIME_DIRECTORY" = "%t/test-exec_runtimedirectory-mode"'
 Type=oneshot
 RuntimeDirectory=test-exec_runtimedirectory-mode
 RuntimeDirectoryMode=0750
index 79bebc4616a81ad3385ff8e44a357b03057921b2..4bc336167c96c154831f00454a3d1e6e7930e329 100644 (file)
@@ -3,7 +3,7 @@
 Description=Test for RuntimeDirectory owner (must not be the default group of the user if Group is set)
 
 [Service]
-ExecStart=/bin/sh -x -c 'group=$$(stat -c %%G %t/test-exec_runtimedirectory-owner); test "$$group" = "nfsnobody"'
+ExecStart=sh -x -c 'group=$$(stat -c %%G %t/test-exec_runtimedirectory-owner); test "$$group" = "nfsnobody"'
 Type=oneshot
 Group=nfsnobody
 User=root
index 3b42a9fc419169120f01fb6d1849f2f7356019c6..5f94bf9cd0be39fd0693943e8bc3e0de89675729 100644 (file)
@@ -3,7 +3,7 @@
 Description=Test for RuntimeDirectory owner (must not be the default group of the user if Group is set)
 
 [Service]
-ExecStart=/bin/sh -x -c 'group=$$(stat -c %%G %t/test-exec_runtimedirectory-owner); test "$$group" = "nobody"'
+ExecStart=sh -x -c 'group=$$(stat -c %%G %t/test-exec_runtimedirectory-owner); test "$$group" = "nobody"'
 Type=oneshot
 Group=nobody
 User=root
index 804048ea0596234c7d9c8ef026c07df6a29f38fc..6d508959f8033a4bff55ddcea71ef735345973cf 100644 (file)
@@ -3,7 +3,7 @@
 Description=Test for RuntimeDirectory owner (must not be the default group of the user if Group is set)
 
 [Service]
-ExecStart=/bin/sh -x -c 'group=$$(stat -c %%G %t/test-exec_runtimedirectory-owner); test "$$group" = "nogroup"'
+ExecStart=sh -x -c 'group=$$(stat -c %%G %t/test-exec_runtimedirectory-owner); test "$$group" = "nogroup"'
 Type=oneshot
 Group=nogroup
 User=root
index e2c0890b006530aa2f51f4876f30a60d1d8837f2..64d66b302ee53710d32a82abb32d60ffec2d0ca1 100644 (file)
@@ -3,7 +3,7 @@
 Description=Test for RuntimeDirectory owner (must not be the default group of the user if Group is set)
 
 [Service]
-ExecStart=/bin/sh -x -c 'group=$$(stat -c %%G %t/test-exec_runtimedirectory-owner-daemon); test "$$group" = "daemon"'
+ExecStart=sh -x -c 'group=$$(stat -c %%G %t/test-exec_runtimedirectory-owner-daemon); test "$$group" = "daemon"'
 Type=oneshot
 Group=daemon
 User=root
index 1928c57acefd0f9925e3bf08d063cc22657a8828..f60110a48587994dcf7d51b6501ead771516a8b9 100644 (file)
@@ -3,9 +3,9 @@
 Description=Test for RuntimeDirectory
 
 [Service]
-ExecStart=/bin/sh -x -c 'test -d %t/test-exec_runtimedirectory'
-ExecStart=/bin/sh -x -c 'test -d %t/test-exec_runtimedirectory2/hogehoge'
-ExecStart=/bin/sh -x -c 'test "$$RUNTIME_DIRECTORY" = "%t/test-exec_runtimedirectory:%t/test-exec_runtimedirectory2/hogehoge"'
+ExecStart=sh -x -c 'test -d %t/test-exec_runtimedirectory'
+ExecStart=sh -x -c 'test -d %t/test-exec_runtimedirectory2/hogehoge'
+ExecStart=sh -x -c 'test "$$RUNTIME_DIRECTORY" = "%t/test-exec_runtimedirectory:%t/test-exec_runtimedirectory2/hogehoge"'
 Type=oneshot
 RuntimeDirectory=test-exec_runtimedirectory
 RuntimeDirectory=./test-exec_runtimedirectory2///./hogehoge/.
index 9db6c5f3d4f81bcd07014b14db998711dbd175f1..22634361cb3fe501e12172f0d22830f5f2915a0f 100644 (file)
@@ -3,9 +3,9 @@
 Description=Test for SetCredential=
 
 [Service]
-ExecStart=/bin/sh -x -c 'test "$$(cat %d/test-execute.set-credential)" = "hoge"'
-ExecStartPost=/bin/sh -x -c 'test "$$(cat %d/test-execute.set-credential)" = "hoge"'
-ExecStop=/bin/sh -x -c 'test "$$(cat %d/test-execute.set-credential)" = "hoge"'
-ExecStopPost=/bin/sh -x -c 'test "$$(cat %d/test-execute.set-credential)" = "hoge"'
+ExecStart=sh -x -c 'test "$$(cat %d/test-execute.set-credential)" = "hoge"'
+ExecStartPost=sh -x -c 'test "$$(cat %d/test-execute.set-credential)" = "hoge"'
+ExecStop=sh -x -c 'test "$$(cat %d/test-execute.set-credential)" = "hoge"'
+ExecStopPost=sh -x -c 'test "$$(cat %d/test-execute.set-credential)" = "hoge"'
 Type=oneshot
 SetCredential=test-execute.set-credential:hoge
index 2e8882c5ab18ec639aef1a2585470dacdf9a9d66..aa0ecdfe2013cf45327df2a1156d01951d33395c 100644 (file)
@@ -4,4 +4,4 @@ Description=https://github.com/systemd/systemd/issues/2637
 
 [Service]
 Type=oneshot
-ExecStart=/bin/bash -x -c "[[ %%U == ?U ]]"
+ExecStart=bash -x -c "[[ %%U == ?U ]]"
index 838fea7d047e0e9107b82a08f14db67cbcd942e7..fd56f7e37b3f7538fa5449ab3aabc5357ac1aa4a 100644 (file)
@@ -3,7 +3,7 @@
 Description=Test for StandardInputText= and StandardInputData=
 
 [Service]
-ExecStart=/bin/sh -x -c 'd=$$(mktemp -d -p /tmp); echo -e "this is a test\nand this is more\nsomething encoded!\nsomething   in multiple lines\nand some more\nand a more bas64 data\nsomething with strange\nembedded\tcharacters\nand something with a exec-stdin-data.service specifier" >$d/text ; cmp $d/text ; rm -rf $d'
+ExecStart=sh -x -c 'd=$$(mktemp -d -p /tmp); echo -e "this is a test\nand this is more\nsomething encoded!\nsomething   in multiple lines\nand some more\nand a more bas64 data\nsomething with strange\nembedded\tcharacters\nand something with a exec-stdin-data.service specifier" >$d/text ; cmp $d/text ; rm -rf $d'
 Type=oneshot
 StandardInput=data
 StandardInputText=this is a test
index 0ecc34441c423d4b97c6febb59a6f5395115d7c0..3c90124818db3bbbb153700ebe36208baa2140be 100644 (file)
@@ -3,9 +3,9 @@
 Description=Test for Supplementary Group with multiple groups without Group and User
 
 [Service]
-ExecStart=/bin/sh -x -c 'for g in $$(id -G); do test "$$g" = "%G" && exit 0; done; exit 1'
-ExecStart=/bin/sh -x -c 'for g in $$(id -G); do test "$$g" = "1" && exit 0; done; exit 1'
-ExecStart=/bin/sh -x -c 'for g in $$(id -G); do test "$$g" = "2" && exit 0; done; exit 1'
-ExecStart=/bin/sh -x -c 'test "$$(id -g)" = "%G" && test "$$(id -u)" = "%U"'
+ExecStart=sh -x -c 'for g in $$(id -G); do test "$$g" = "%G" && exit 0; done; exit 1'
+ExecStart=sh -x -c 'for g in $$(id -G); do test "$$g" = "1" && exit 0; done; exit 1'
+ExecStart=sh -x -c 'for g in $$(id -G); do test "$$g" = "2" && exit 0; done; exit 1'
+ExecStart=sh -x -c 'test "$$(id -g)" = "%G" && test "$$(id -u)" = "%U"'
 Type=oneshot
 SupplementaryGroups=1 2
index cd1021bbdfd679a1455b693ef96a34d7e15c1b8e..0fd1c6205286e4bbc93966c234e3e9c5c5f79738 100644 (file)
@@ -3,9 +3,9 @@
 Description=Test for Supplementary Group with multiple groups and Group=1
 
 [Service]
-ExecStart=/bin/sh -x -c 'for g in $$(id -G); do test "$$g" = "1" && exit 0; done; exit 1'
-ExecStart=/bin/sh -x -c 'for g in $$(id -G); do test "$$g" = "2" && exit 0; done; exit 1'
-ExecStart=/bin/sh -x -c 'test "$$(id -g)" = "1" && test "$$(id -u)" = "%U"'
+ExecStart=sh -x -c 'for g in $$(id -G); do test "$$g" = "1" && exit 0; done; exit 1'
+ExecStart=sh -x -c 'for g in $$(id -G); do test "$$g" = "2" && exit 0; done; exit 1'
+ExecStart=sh -x -c 'test "$$(id -g)" = "1" && test "$$(id -u)" = "%U"'
 Type=oneshot
 Group=1
 SupplementaryGroups=1 2
index 7913a2c2ededfcd24af3bca25527f40a71c99065..c430e54685a1b6701e5460599f9e1a166cf95904 100644 (file)
@@ -3,8 +3,8 @@
 Description=Test for Supplementary Group with multiple groups and Uid=1
 
 [Service]
-ExecStart=/bin/sh -x -c 'for g in $$(id -G); do test "$$g" = "1" && exit 0; done; exit 1'
-ExecStart=/bin/sh -x -c 'for g in $$(id -G); do test "$$g" = "2" && exit 0; done; exit 1'
+ExecStart=sh -x -c 'for g in $$(id -G); do test "$$g" = "1" && exit 0; done; exit 1'
+ExecStart=sh -x -c 'for g in $$(id -G); do test "$$g" = "2" && exit 0; done; exit 1'
 Type=oneshot
 User=1
 SupplementaryGroups=1 2
index ee4017e74e79ae6ff84974eb528b36cd4bac3dc7..20a3561d08d36a30cebc3276e20eaf32bc20e1cf 100644 (file)
@@ -3,8 +3,8 @@
 Description=Test for Supplementary Group with only one group and uid 1
 
 [Service]
-ExecStart=/bin/sh -x -c 'for g in $$(id -G); do test "$$g" = "1" && exit 0; done; exit 1'
-ExecStart=/bin/sh -x -c 'test "$$(id -g)" = "1" && test "$$(id -u)" = "1"'
+ExecStart=sh -x -c 'for g in $$(id -G); do test "$$g" = "1" && exit 0; done; exit 1'
+ExecStart=sh -x -c 'test "$$(id -g)" = "1" && test "$$(id -u)" = "1"'
 Type=oneshot
 User=1
 Group=1
index 62275201cc271c21648884b8a593bc7f26f59daa..8c812573e4d289cb400fdf5fd426d92bb66c7a3d 100644 (file)
@@ -3,8 +3,8 @@
 Description=Test for Supplementary Group with only one group
 
 [Service]
-ExecStart=/bin/sh -x -c 'for g in $$(id -G); do test "$$g" = "1" && exit 0; done; exit 1'
-ExecStart=/bin/sh -x -c 'test "$$(id -g)" = "1" && test "$$(id -u)" = "0"'
+ExecStart=sh -x -c 'for g in $$(id -G); do test "$$g" = "1" && exit 0; done; exit 1'
+ExecStart=sh -x -c 'test "$$(id -g)" = "1" && test "$$(id -u)" = "0"'
 Type=oneshot
 Group=1
 SupplementaryGroups=1
index 03406c3ee8819a3bf594d9b5065acebb94ab43c6..0a3d370595c0096de041736b0b01db625586fcdc 100644 (file)
@@ -3,7 +3,7 @@
 Description=Test for Supplementary Group
 
 [Service]
-ExecStart=/bin/sh -x -c 'for g in $$(id -G); do test "$$g" = "%G" && exit 0; done; exit 1'
-ExecStart=/bin/sh -x -c 'for g in $$(id -G); do test "$$g" = "1" && exit 0; done; exit 1'
+ExecStart=sh -x -c 'for g in $$(id -G); do test "$$g" = "%G" && exit 0; done; exit 1'
+ExecStart=sh -x -c 'for g in $$(id -G); do test "$$g" = "1" && exit 0; done; exit 1'
 Type=oneshot
 SupplementaryGroups=1
index f2be6003c8fbe9a944ea4f26e4b012afb2082593..00a450810b41f3e4b9fc227f9f259ebf68ea8f11 100644 (file)
@@ -3,7 +3,7 @@
 Description=Test for SystemCallErrorNumber
 
 [Service]
-ExecStart=/usr/bin/python3 -c 'import os\ntry: os.uname()\nexcept Exception as e: exit(e.errno)'
+ExecStart=python3 -c 'import os\ntry: os.uname()\nexcept Exception as e: exit(e.errno)'
 Type=oneshot
 SystemCallFilter=~uname
 SystemCallErrorNumber=EACCES
index 5d99a97476b7a5686d9b61bfa7c0b033e766395d..3b5fb6eab2a21e5df5d644e55a132f3b0bf3020f 100644 (file)
@@ -3,7 +3,7 @@
 Description=Test for SystemCallErrorNumber
 
 [Service]
-ExecStart=/usr/bin/python3 -c 'import os\ntry: os.uname()\nexcept Exception as e: exit(e.errno)'
+ExecStart=python3 -c 'import os\ntry: os.uname()\nexcept Exception as e: exit(e.errno)'
 Type=oneshot
 SystemCallFilter=~uname
 SystemCallErrorNumber=255
index 3aad372900b3db5e07390c0623d42b5fbae74f9b..7437d301081de34e0dbc18bf96a0a189123a0381 100644 (file)
@@ -3,7 +3,7 @@
 Description=Test for SystemCallFilter
 
 [Service]
-ExecStart=/bin/sh -c '/bin/echo "This should not be seen"'
+ExecStart=sh -c '/bin/echo "This should not be seen"'
 Type=oneshot
 LimitCORE=0
 SystemCallFilter=ioperm
index 8cdb8de45b8f91f13f6e5eafb4a075ad039351ed..92672d1eb132324ea6f85d7327dd2b975775c0ab 100644 (file)
@@ -3,7 +3,7 @@
 Description=Test for SystemCallFilter
 
 [Service]
-ExecStart=/bin/sh -c '/bin/echo "This should not be seen"'
+ExecStart=sh -c '/bin/echo "This should not be seen"'
 Type=oneshot
 LimitCORE=0
 SystemCallFilter=~write open execve fexecve execveat exit_group close mmap munmap fstat DONOTEXIST
index 98c88fd0b15ee70e9ac856a968dde061a8501e56..4e7b81214b264ccfb438a10aafbc73dfc453e528 100644 (file)
@@ -3,7 +3,7 @@
 Description=Test for SystemCallFilter
 
 [Service]
-ExecStart=/bin/sh -c '/bin/echo "This should not be seen"'
+ExecStart=sh -c '/bin/echo "This should not be seen"'
 Type=oneshot
 LimitCORE=0
 SystemCallArchitectures=native
index 8f8192cc9433baa7cec1b08c4edca1f2a551ae1a..eaa75dfb61671115ac9d3b69412e68c5f4e98db0 100644 (file)
@@ -3,7 +3,7 @@
 Description=Test bounding set is right with SystemCallFilter and non-root user
 
 [Service]
-ExecStart=/bin/sh -x -c 'c=$$(capsh --print | grep "Bounding set "); test "$$c" = "Bounding set =cap_net_bind_service"'
+ExecStart=sh -x -c 'c=$$(capsh --print | grep "Bounding set "); test "$$c" = "Bounding set =cap_net_bind_service"'
 Type=oneshot
 User=1
 SystemCallFilter=@system-service
index d78c3232bb4beb7af720656250471bfac024730c..fd0e3a259db5d9db6614bf76f324dffe15272eed 100644 (file)
@@ -3,7 +3,7 @@
 Description=Test bounding set is right with SystemCallFilter and non-root user
 
 [Service]
-ExecStart=/bin/sh -x -c 'c=$$(capsh --print | grep "Bounding set "); test "$$c" = "Bounding set =cap_setpcap,cap_net_bind_service,cap_sys_admin"'
+ExecStart=sh -x -c 'c=$$(capsh --print | grep "Bounding set "); test "$$c" = "Bounding set =cap_setpcap,cap_net_bind_service,cap_sys_admin"'
 Type=oneshot
 User=1
 SystemCallFilter=@system-service
index f33a2a05c4b40e917089cf6a9b1e9a8b992cfa29..76b028c82c9314b87641497c61e668813192bb4a 100644 (file)
@@ -3,7 +3,7 @@
 Description=Test no_new_privs is unset for ProtectClock and non-root user
 
 [Service]
-ExecStart=/bin/sh -x -c 'c=$$(cat /proc/self/status | grep "NoNewPrivs:        "); test "$$c" = "NoNewPrivs:   0"'
+ExecStart=sh -x -c 'c=$$(cat /proc/self/status | grep "NoNewPrivs:     "); test "$$c" = "NoNewPrivs:   0"'
 Type=oneshot
 User=1
 ProtectClock=yes
index 8bfd0a79d9a3ebda58fd019945d9b682a431195c..2091b71db009d2a4679a6c619a724423d5311cc4 100644 (file)
@@ -3,7 +3,7 @@
 Description=Test no_new_privs is unset for SystemCallFilter and non-root user
 
 [Service]
-ExecStart=/bin/sh -x -c 'c=$$(cat /proc/self/status | grep "NoNewPrivs:        "); test "$$c" = "NoNewPrivs:   0"'
+ExecStart=sh -x -c 'c=$$(cat /proc/self/status | grep "NoNewPrivs:     "); test "$$c" = "NoNewPrivs:   0"'
 Type=oneshot
 User=1
 SystemCallFilter=@system-service
index c7eddea6655cf1690999f914cc3fe5beb0bbc182..bb2ea5516695a4ed08f46994b056e6519f5be136 100644 (file)
@@ -3,7 +3,7 @@
 Description=Test for SystemCallFilter
 
 [Service]
-ExecStart=/bin/sh -c 'echo "Foo bar"'
+ExecStart=sh -c 'echo "Foo bar"'
 Type=oneshot
 SystemCallFilter=~read write open execve ioperm
 SystemCallFilter=ioctl
index 96eaf16a45bc257c2c6f8c28e033ff0c9aa57941..d9f0a3717cf0db9d03fad5c9d4e20768458b6cc2 100644 (file)
@@ -3,6 +3,6 @@
 Description=Test for SystemCallFilter
 
 [Service]
-ExecStart=/bin/sh -c 'echo "Foo bar"'
+ExecStart=sh -c 'echo "Foo bar"'
 Type=oneshot
 SystemCallFilter=
index f8f409284457014619c580996901dbbbaf81c850..df4e662d40dc15237a8a379ba853f924f54f5dea 100644 (file)
@@ -3,7 +3,7 @@
 Description=Test for SystemCallFilter
 
 [Service]
-ExecStart=/bin/sh -c 'echo "Foo bar"'
+ExecStart=sh -c 'echo "Foo bar"'
 Type=oneshot
 SystemCallArchitectures=native
 SystemCallFilter=
index de2c6ad2d6a4d415b0b4457eaf2d5739570ec4d5..6107d114bec1c65a174aea21878b1161d60d38a4 100644 (file)
@@ -3,7 +3,7 @@
 Description=Test for SystemCallFilter with specific kill action overriding default errno action
 
 [Service]
-ExecStart=/usr/bin/python3 -c 'import os\ntry: os.uname()\nexcept Exception as e: exit(e.errno)'
+ExecStart=python3 -c 'import os\ntry: os.uname()\nexcept Exception as e: exit(e.errno)'
 Type=oneshot
 SystemCallFilter=~uname:kill
 SystemCallErrorNumber=EILSEQ
index ffa35e64dff2a847f2c18f6910541161d9f3d7e6..e049275604daf5cd94f589ec11e818d48f6f3649 100644 (file)
@@ -3,7 +3,7 @@
 Description=Test for SystemCallFilter with specific errno action overriding default kill action
 
 [Service]
-ExecStart=/usr/bin/python3 -c 'import os\ntry: os.uname()\nexcept Exception as e: exit(e.errno)'
+ExecStart=python3 -c 'import os\ntry: os.uname()\nexcept Exception as e: exit(e.errno)'
 Type=oneshot
 SystemCallFilter=~uname:EILSEQ
 SystemCallErrorNumber=kill
index deba1543b44883394a9f81de3f06a75c4809408a..19122869b1ab6ffde616eabea6c51a124d0a078d 100644 (file)
@@ -3,7 +3,7 @@
 Description=Test for SystemCallFilter in system mode with User set
 
 [Service]
-ExecStart=/bin/sh -c 'echo "Foo bar"'
+ExecStart=sh -c 'echo "Foo bar"'
 Type=oneshot
 User=nfsnobody
 SystemCallFilter=~read write open execve ioperm
index 43fb9c3395dfd97e9a90160c9e24daaa5fe95bc0..0c2ebdd83e613c7342c376435ba10ea06ba94e78 100644 (file)
@@ -3,7 +3,7 @@
 Description=Test for SystemCallFilter in system mode with User set
 
 [Service]
-ExecStart=/bin/sh -c 'echo "Foo bar"'
+ExecStart=sh -c 'echo "Foo bar"'
 Type=oneshot
 User=nobody
 SystemCallFilter=~read write open execve ioperm
index 005c4ac1c82927abd39fdffbe78e52d448319503..6de39642e900a52eefb7837fed74da45b104c47e 100644 (file)
@@ -3,7 +3,7 @@
 Description=Test for SystemCallFilter in system mode with User set (daemon)
 
 [Service]
-ExecStart=/bin/sh -c 'echo "Foo bar"'
+ExecStart=sh -c 'echo "Foo bar"'
 Type=oneshot
 User=daemon
 SystemCallFilter=~read write open execve ioperm
index c7a4c4a61435336997310d737b270ba28bd5c4fc..a8dc10f3c18a1df241aa2e3e2172a6b75e30b7b5 100644 (file)
@@ -3,7 +3,7 @@
 Description=Test for SystemCallFilter with errno name (for issue #18916)
 
 [Service]
-ExecStart=/usr/bin/python3 -c 'import os\ntry: os.uname()\nexcept Exception as e: exit(e.errno)'
+ExecStart=python3 -c 'import os\ntry: os.uname()\nexcept Exception as e: exit(e.errno)'
 Type=oneshot
 SystemCallFilter=@system-service
 SystemCallFilter=~uname:EILSEQ
index 267832366fd32895864b54afedc01924ea193c73..224df01a23a9d6ebc0dfd6fac3e52fb40d085b07 100644 (file)
@@ -4,7 +4,7 @@ Description=Test for SystemCallFilter updating errno
 # test for issue #9939 which is fixed by a5404992cc7724ebf7572a0aa89d9fdb26ce0b62 (#9942)
 
 [Service]
-ExecStart=/usr/bin/python3 -c 'import os\ntry: os.uname()\nexcept Exception as e: exit(e.errno)'
+ExecStart=python3 -c 'import os\ntry: os.uname()\nexcept Exception as e: exit(e.errno)'
 Type=oneshot
 SystemCallFilter=~uname:ENOENT uname:EILSEQ
 SystemCallErrorNumber=EACCES
index a9023314adbe262e85c55f5c3082e5f34c5e6b7a..bed79610bef5845a7dca826168b6464c7f94302f 100644 (file)
@@ -3,7 +3,7 @@
 Description=Test for SystemCallFilter with errno name
 
 [Service]
-ExecStart=/usr/bin/python3 -c 'import os\ntry: os.uname()\nexcept Exception as e: exit(e.errno)'
+ExecStart=python3 -c 'import os\ntry: os.uname()\nexcept Exception as e: exit(e.errno)'
 Type=oneshot
 SystemCallFilter=~uname:EILSEQ
 SystemCallErrorNumber=EACCES
index ffbc84a3138beceb67abf0a8d5f31521d98fd80a..8db2281f81da9a6238d848b671bde0655c7d7a29 100644 (file)
@@ -3,7 +3,7 @@
 Description=Test for SystemCallFilter with errno number
 
 [Service]
-ExecStart=/usr/bin/python3 -c 'import os\ntry: os.uname()\nexcept Exception as e: exit(e.errno)'
+ExecStart=python3 -c 'import os\ntry: os.uname()\nexcept Exception as e: exit(e.errno)'
 Type=oneshot
 SystemCallFilter=~uname:255
 SystemCallErrorNumber=EACCES
index 1610c63a4adb1697fd48f734f1ec7a9e64d9e734..b00030131ec6f3b0073560f6b17bcb0463063599 100644 (file)
@@ -10,8 +10,8 @@ Type=oneshot
 TemporaryFileSystem=/var:ro,mode=0700,nostrictatime
 
 # Check /proc/self/mountinfo
-ExecStart=/bin/sh -x -c 'test "$$(awk \'$$5 == "/var" && $$11 !~ /(^|,)mode=700(,|$$)/ { print $$6 }\' /proc/self/mountinfo)" = ""'
+ExecStart=sh -x -c 'test "$$(awk \'$$5 == "/var" && $$11 !~ /(^|,)mode=700(,|$$)/ { print $$6 }\' /proc/self/mountinfo)" = ""'
 
-ExecStart=/bin/sh -x -c 'test "$$(awk \'$$5 == "/var" && $$6 !~ /(^|,)ro(,|$$)/ { print $$6 }\' /proc/self/mountinfo)" = ""'
-ExecStart=/bin/sh -x -c 'test "$$(awk \'$$5 == "/var" && $$6 !~ /(^|,)nodev(,|$$)/ { print $$6 }\' /proc/self/mountinfo)" = ""'
-ExecStart=/bin/sh -x -c 'test "$$(awk \'$$5 == "/var" && $$6 ~ /(^|,)strictatime(,|$$)/ { print $$6 }\' /proc/self/mountinfo)" = ""'
+ExecStart=sh -x -c 'test "$$(awk \'$$5 == "/var" && $$6 !~ /(^|,)ro(,|$$)/ { print $$6 }\' /proc/self/mountinfo)" = ""'
+ExecStart=sh -x -c 'test "$$(awk \'$$5 == "/var" && $$6 !~ /(^|,)nodev(,|$$)/ { print $$6 }\' /proc/self/mountinfo)" = ""'
+ExecStart=sh -x -c 'test "$$(awk \'$$5 == "/var" && $$6 ~ /(^|,)strictatime(,|$$)/ { print $$6 }\' /proc/self/mountinfo)" = ""'
index 2ee5c269f9f8fc4cb61a54c38974a27b46618f86..0a4b0f25521c39e5f0cb5975f711f10d40a2b174 100644 (file)
@@ -6,31 +6,31 @@ Description=Test for TemporaryFileSystem with read-only mode
 Type=oneshot
 
 # Check directories exist
-ExecStart=/bin/sh -c 'test -d /var/test-exec-temporaryfilesystem/rw && test -d /var/test-exec-temporaryfilesystem/ro'
+ExecStart=sh -c 'test -d /var/test-exec-temporaryfilesystem/rw && test -d /var/test-exec-temporaryfilesystem/ro'
 
 # Check TemporaryFileSystem= are empty
-ExecStart=/bin/sh -c 'for i in $$(ls -A /var); do test $$i = test-exec-temporaryfilesystem || false; done'
+ExecStart=sh -c 'for i in $$(ls -A /var); do test $$i = test-exec-temporaryfilesystem || false; done'
 
 # Check default mode
 ExecStart=sh -x -c 'test "$$(stat -c %%a /var)" = "755"'
 
 # Cannot create a file in /var
-ExecStart=/bin/sh -c '! touch /var/hoge'
+ExecStart=sh -c '! touch /var/hoge'
 
 # Create a file in /var/test-exec-temporaryfilesystem/rw
-ExecStart=/bin/sh -c 'touch /var/test-exec-temporaryfilesystem/rw/thisisasimpletest-temporaryfilesystem'
+ExecStart=sh -c 'touch /var/test-exec-temporaryfilesystem/rw/thisisasimpletest-temporaryfilesystem'
 
 # Then, the file can be access through /tmp
-ExecStart=/bin/sh -c 'test -f /tmp/thisisasimpletest-temporaryfilesystem'
+ExecStart=sh -c 'test -f /tmp/thisisasimpletest-temporaryfilesystem'
 
 # Also, through /var/test-exec-temporaryfilesystem/ro
-ExecStart=/bin/sh -c 'test -f /var/test-exec-temporaryfilesystem/ro/thisisasimpletest-temporaryfilesystem'
+ExecStart=sh -c 'test -f /var/test-exec-temporaryfilesystem/ro/thisisasimpletest-temporaryfilesystem'
 
 # The file cannot modify through /var/test-exec-temporaryfilesystem/ro
-ExecStart=/bin/sh -c '! touch /var/test-exec-temporaryfilesystem/ro/thisisasimpletest-temporaryfilesystem'
+ExecStart=sh -c '! touch /var/test-exec-temporaryfilesystem/ro/thisisasimpletest-temporaryfilesystem'
 
 # Cleanup
-ExecStart=/bin/sh -c 'rm /tmp/thisisasimpletest-temporaryfilesystem'
+ExecStart=sh -c 'rm /tmp/thisisasimpletest-temporaryfilesystem'
 
 TemporaryFileSystem=/var:ro
 BindPaths=/tmp:/var/test-exec-temporaryfilesystem/rw
index f62ce1a85204b98b9beb98b921cb052f12ccbda2..455344e114c6952a63cc260f52fed63e9d1b5171 100644 (file)
@@ -6,11 +6,11 @@ Description=Test for TemporaryFileSystem on /usr
 Type=oneshot
 
 # Check TemporaryFileSystem= are empty
-ExecStart=/bin/sh -c 'for i in $$(ls -A /usr); do test $$i = lib -o $$i = lib64 -o $$i = bin -o $$i = sbin || false; done'
+ExecStart=sh -c 'for i in $$(ls -A /usr); do test $$i = lib -o $$i = lib64 -o $$i = bin -o $$i = sbin || false; done'
 
 # Cannot create files under /usr
-ExecStart=/bin/sh -c '! touch /usr/hoge'
-ExecStart=/bin/sh -c '! touch /usr/bin/hoge'
+ExecStart=sh -c '! touch /usr/hoge'
+ExecStart=sh -c '! touch /usr/bin/hoge'
 
 TemporaryFileSystem=/usr:ro
 BindReadOnlyPaths=-/usr/lib -/usr/lib64 /usr/bin /usr/sbin
index 380cb8234a1a77267b1ee955f9d70f64f4cd8d33..de9ac5a2333cb34ee65676db733831834e0247c7 100644 (file)
@@ -3,7 +3,7 @@
 Description=Test for UMask
 
 [Service]
-ExecStart=/bin/sh -x -c 'rm /tmp/test-exec-umask; touch /tmp/test-exec-umask; mode=$$(stat -c %%a /tmp/test-exec-umask); test "$$mode" = "600"'
+ExecStart=sh -x -c 'rm /tmp/test-exec-umask; touch /tmp/test-exec-umask; mode=$$(stat -c %%a /tmp/test-exec-umask); test "$$mode" = "600"'
 Type=oneshot
 UMask=0177
 PrivateTmp=yes
index b28023d8a9588e33a4b072de0fd0c000a0e8b075..6d13c0bfa2a7045cdf669ec0c747385171b26367 100644 (file)
@@ -3,6 +3,6 @@
 Description=Test for UMask default
 
 [Service]
-ExecStart=/bin/sh -x -c 'rm /tmp/test-exec-umask; touch /tmp/test-exec-umask; mode=$$(stat -c %%a /tmp/test-exec-umask); test "$$mode" = "644"'
+ExecStart=sh -x -c 'rm /tmp/test-exec-umask; touch /tmp/test-exec-umask; mode=$$(stat -c %%a /tmp/test-exec-umask); test "$$mode" = "644"'
 Type=oneshot
 PrivateTmp=yes
index 8419c86c9ab73f694f7e590e60c0823695e14492..aac1dadc5afc3717d004d1bc41c4dffc471f536e 100644 (file)
@@ -3,7 +3,7 @@
 Description=Test for UMask= + namespacing
 
 [Service]
-ExecStart=/bin/ls -lahd /tmp/subdir
+ExecStart=ls -lahd /tmp/subdir
 Type=oneshot
 User=65534
 Group=65534
index b79e3d42c7ec2d318a693ce2877f6e5b3d0c99c9..9c5e277d0578e83d2fbabade95cf39d5ec5a47e6 100644 (file)
@@ -3,7 +3,7 @@
 Description=Test for UnsetEnvironment
 
 [Service]
-ExecStart=/bin/sh -x -c 'test "$$FOO" = "bar" && test "$${QUUX-X}" = "X" && test "$$VAR3" = "value3" && test "$${VAR4-X}" = "X" && test "$$VAR5" = "value5" && test "$${X%b-X}" = "X"'
+ExecStart=sh -x -c 'test "$$FOO" = "bar" && test "$${QUUX-X}" = "X" && test "$$VAR3" = "value3" && test "$${VAR4-X}" = "X" && test "$$VAR5" = "value5" && test "$${X%b-X}" = "X"'
 Type=oneshot
 Environment=FOO=bar QUUX=waldo VAR3=value3 VAR4=value4 VAR5=value5 X%b=%U
 UnsetEnvironment=QUUX=waldo VAR3=somethingelse VAR4 X%b=%U
index 8f0943c282e49990c0a93a08908b5f10c8ed5755..1ce5f08370c08381450d20d1ef2e1170934c4478 100644 (file)
@@ -3,6 +3,6 @@
 Description=Test for User
 
 [Service]
-ExecStart=/bin/sh -x -c 'test "$$USER" = "nfsnobody"'
+ExecStart=sh -x -c 'test "$$USER" = "nfsnobody"'
 Type=oneshot
 User=nfsnobody
index 834d11ad319e92c14b616588c6bb8a5eeeaffbe8..003b873b39706467c20290b0574aada8fd2756ef 100644 (file)
@@ -3,6 +3,6 @@
 Description=Test for User
 
 [Service]
-ExecStart=/bin/sh -x -c 'test "$$USER" = "nobody"'
+ExecStart=sh -x -c 'test "$$USER" = "nobody"'
 Type=oneshot
 User=nobody
index b9863d2025ff248d198d902867b5c72763b2f5e6..696c7e5739ac9d56b1c47dfa082604098c8e3b8d 100644 (file)
@@ -3,6 +3,6 @@
 Description=Test for User (daemon)
 
 [Service]
-ExecStart=/bin/sh -x -c 'test "$$USER" = "daemon"'
+ExecStart=sh -x -c 'test "$$USER" = "daemon"'
 Type=oneshot
 User=daemon
index 130d9d5c507d31db8e0199e7243f715b98cb3bc3..3c4869d858078a8215b62e3583995d9f22894acb 100644 (file)
@@ -3,6 +3,6 @@
 Description=Test for WorkingDirectory with trailing dot
 
 [Service]
-ExecStart=/bin/sh -x -c 'test "$$PWD" = "/tmp/test-exec_workingdirectory"'
+ExecStart=sh -x -c 'test "$$PWD" = "/tmp/test-exec_workingdirectory"'
 Type=oneshot
 WorkingDirectory=/tmp///./test-exec_workingdirectory/.
index b53bf6081f729ae2c46c845715622c9558028bfa..4c40fafcf1df0209e3d19a4c1163e30b59f356c2 100644 (file)
@@ -3,6 +3,6 @@
 Description=Test for WorkingDirectory
 
 [Service]
-ExecStart=/bin/sh -x -c 'test "$$PWD" = "/tmp/test-exec_workingdirectory"'
+ExecStart=sh -x -c 'test "$$PWD" = "/tmp/test-exec_workingdirectory"'
 Type=oneshot
 WorkingDirectory=/tmp/test-exec_workingdirectory
index 77da9259d730dfa4236f6aade2d47b36361aa807..016cf156827efd69ffff16b9adabdd091a736df4 100644 (file)
@@ -995,7 +995,7 @@ create_asan_wrapper() {
 
     [[ -z "$ASAN_RT_PATH" ]] && dfatal "ASAN_RT_PATH is empty, but it shouldn't be"
 
-    default_asan_options="${ASAN_OPTIONS:-strict_string_checks=1:detect_stack_use_after_return=1:check_initialization_order=1:strict_init_order=1}"
+    default_asan_options="${ASAN_OPTIONS:-strict_string_checks=1:detect_stack_use_after_return=1:check_initialization_order=1:strict_init_order=1:disable_coredump=0:use_madv_dontdump=1}"
     default_ubsan_options="${UBSAN_OPTIONS:-print_stacktrace=1:print_summary=1:halt_on_error=1}"
 
     if [[ "$ASAN_COMPILER" == "clang" ]]; then
@@ -1151,7 +1151,9 @@ install_multipath() {
     image_install kpartx /lib/udev/kpartx_id lsmod mpathpersist multipath multipathd partx
     image_install "${ROOTLIBDIR:?}"/system/multipathd.{service,socket}
     if get_bool "$LOOKS_LIKE_DEBIAN"; then
-        inst_rules 56-dm-parts.rules 56-dm-mpath.rules 60-multipath.rules 68-del-part-nodes.rules 95-kpartx.rules
+        # Note: try both 60-kpartx.rules (as seen on Debian Sid with 0.9.4-7) and 95-kpartx.rules (as seen on
+        # Ubuntu Jammy with 0.8.8-1ubuntu1.22.04.4)
+        inst_rules 56-dm-parts.rules 56-dm-mpath.rules 60-kpartx.rules 60-multipath.rules 68-del-part-nodes.rules 95-kpartx.rules
     else
         inst_rules 11-dm-mpath.rules 11-dm-parts.rules 62-multipath.rules 66-kpartx.rules 68-del-part-nodes.rules
     fi
similarity index 90%
rename from test/test-network/conf/25-neighbor-section.network
rename to test/test-network/conf/25-neighbor-dummy.network.d/10-step1.conf
index 59e21ebf1ba7fc8335bd2a91da5ec2c39d45bb36..727089e82441cdd259b1d29de3378fdbbb5a92af 100644 (file)
@@ -1,10 +1,4 @@
 # SPDX-License-Identifier: LGPL-2.1-or-later
-[Match]
-Name=dummy98
-
-[Network]
-IPv6AcceptRA=no
-
 [Neighbor]
 Address=192.168.10.1
 LinkLayerAddress=00:00:5e:00:02:65
similarity index 99%
rename from test/test-network/conf/25-neighbor-section.network.d/override.conf
rename to test/test-network/conf/25-neighbor-dummy.network.d/10-step2.conf
index 01027e35c323aff4ced7f854896a31df8f7bbfe2..e28e3fcc53f96a59ccb9760f6e3520fbd5948605 100644 (file)
@@ -1,5 +1,4 @@
 # SPDX-License-Identifier: LGPL-2.1-or-later
-
 [Neighbor]
 Address=192.168.10.1
 LinkLayerAddress=00:00:5e:00:03:65
diff --git a/test/test-network/conf/25-neighbor-dummy.network.d/10-step3.conf b/test/test-network/conf/25-neighbor-dummy.network.d/10-step3.conf
new file mode 100644 (file)
index 0000000..9262b74
--- /dev/null
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Neighbor]
+Address=192.168.10.1
+LinkLayerAddress=00:00:5e:00:03:66
similarity index 97%
rename from test/test-network/conf/25-nexthop.network
rename to test/test-network/conf/25-nexthop-1.network
index f53a58b0597e10b11de002590b69c27c7e233139..a5a8d8180d1f75f00985b5b2113a364f2bce9ae4 100644 (file)
@@ -6,7 +6,6 @@ Name=veth99
 IPv6AcceptRA=no
 Address=2001:1234:5:8f63::1/120
 Address=192.168.5.10/24
-Gateway=192.168.5.1
 
 [NextHop]
 Id=1
diff --git a/test/test-network/conf/25-nexthop-2.network b/test/test-network/conf/25-nexthop-2.network
new file mode 100644 (file)
index 0000000..63062f3
--- /dev/null
@@ -0,0 +1,98 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Match]
+Name=veth99
+
+[Network]
+IPv6AcceptRA=no
+Address=2001:1234:5:8f63::1/120
+Address=192.168.5.10/24
+
+# Commented out lines are specified in 25-nexthop.network
+
+[NextHop]
+#Id=1
+Id=6
+Gateway=192.168.5.1
+
+[NextHop]
+#Id=2
+Id=7
+Gateway=2001:1234:5:8f63::2
+
+[NextHop]
+#Id=3
+Id=4
+Family=ipv6
+
+[NextHop]
+#Id=4
+Id=3
+Family=ipv4
+
+[NextHop]
+Id=5
+#Gateway=192.168.10.1
+#OnLink=yes
+Gateway=192.168.5.3
+OnLink=no
+
+[NextHop]
+#Id=6
+Id=1
+Family=ipv4
+Blackhole=yes
+
+[NextHop]
+#Id=7
+Id=2
+Family=ipv6
+Blackhole=yes
+
+[NextHop]
+Id=8
+Gateway=fe80::222:4dff:ff:ff:ff:ff
+
+[NextHop]
+Gateway=192.168.5.2
+
+[NextHop]
+Family=ipv4
+Blackhole=yes
+
+[NextHop]
+Family=ipv6
+Blackhole=yes
+
+[Route]
+#NextHop=1
+NextHop=6
+Destination=10.10.10.10
+
+[Route]
+#NextHop=2
+NextHop=7
+Destination=10.10.10.11
+
+[Route]
+#NextHop=2
+NextHop=7
+Destination=2001:1234:5:8f62::1
+
+[Route]
+NextHop=5
+Destination=10.10.10.12
+
+[Route]
+#NextHop=6
+NextHop=1
+Destination=10.10.10.13
+
+[Route]
+#NextHop=7
+NextHop=2
+Destination=2001:1234:5:8f62::2
+
+[Route]
+#NextHop=21
+NextHop=20
+Destination=10.10.10.14
diff --git a/test/test-network/conf/25-nexthop-dummy-2.network b/test/test-network/conf/25-nexthop-dummy-2.network
new file mode 100644 (file)
index 0000000..2556b1f
--- /dev/null
@@ -0,0 +1,20 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Match]
+Name=dummy98
+
+[Network]
+Address=192.168.20.20/24
+IPv6AcceptRA=no
+
+# Commented out lines are specified in 25-nexthop-dummy.network
+
+[NextHop]
+#Id=20
+Id=21
+Gateway=192.168.20.1
+
+[NextHop]
+#Id=21
+#Group=1:3 20:1
+Id=20
+Group=5:3 21:1
diff --git a/test/test-network/conf/25-wireguard-endpoint-peer0-cred.txt b/test/test-network/conf/25-wireguard-endpoint-peer0-cred.txt
new file mode 100644 (file)
index 0000000..b4251c3
--- /dev/null
@@ -0,0 +1 @@
+192.168.27.3:51820
diff --git a/test/test-network/conf/25-wireguard-no-peer-private-key-cred.txt b/test/test-network/conf/25-wireguard-no-peer-private-key-cred.txt
new file mode 100644 (file)
index 0000000..8011c64
--- /dev/null
@@ -0,0 +1 @@
+EEGlnEPYJV//kbvvIqxKkQwOiS+UENyPncC4bF46ong=
index ce3b31a5cecc134bb3254a25bff5863fd38193bf..8c90735bc7657bec6bbbe6b9a7c92f007c123122 100644 (file)
@@ -4,6 +4,6 @@ Name=wg97
 Kind=wireguard
 
 [WireGuard]
-PrivateKey=EEGlnEPYJV//kbvvIqxKkQwOiS+UENyPncC4bF46ong=
+#PrivateKey=EEGlnEPYJV//kbvvIqxKkQwOiS+UENyPncC4bF46ong=
 ListenPort=51821
 FwMark=1235
diff --git a/test/test-network/conf/25-wireguard-preshared-key-peer2-cred.txt b/test/test-network/conf/25-wireguard-preshared-key-peer2-cred.txt
new file mode 100644 (file)
index 0000000..5e79c19
--- /dev/null
@@ -0,0 +1 @@
+6Fsg8XN0DE6aPQgAX4r2oazEYJOGqyHUz3QRH/jCB+I=
index 4fed38e57a1ea2ef89a37ff2239793b04b0ec1d5..6a2bb88c2e88090a6df8b739e8cca6e956b894b9 100644 (file)
@@ -13,8 +13,8 @@ RouteMetric=456
 [WireGuardPeer]
 PublicKey=RDf+LSpeEre7YEIKaxg+wbpsNV7du+ktR99uBEtIiCA=
 AllowedIPs=fd31:bf08:57cb::/48,192.168.26.3/24
-#Endpoint=wireguard.example.com:51820
-Endpoint=192.168.27.3:51820
+#Endpoint=192.168.27.3:51820
+Endpoint=@network.wireguard.peer0.endpoint
 PresharedKey=IIWIV17wutHv7t4cR6pOT91z6NSz/T8Arh0yaywhw3M=
 PersistentKeepalive=20
 RouteTable=1234
index bf99a5ab0f8967f16c22a1a64446b636e878d044..f3440df28f163658abc5a29663b9635213b17f3a 100644 (file)
@@ -1,5 +1,5 @@
 [WireGuardPeer]
 PublicKey=9uioxkGzjvGjkse3V35I9AhorWfIjBcrf3UPMS0bw2c=
-PresharedKey=6Fsg8XN0DE6aPQgAX4r2oazEYJOGqyHUz3QRH/jCB+I=
+PresharedKey=@network.wireguard.peer2.psk
 
 AllowedIPs=192.168.124.3
diff --git a/test/test-network/conf/26-bridge-mac-master.network b/test/test-network/conf/26-bridge-mac-master.network
new file mode 100644 (file)
index 0000000..d08970b
--- /dev/null
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Match]
+Name=bridge99
+
+[Network]
+IPv6AcceptRA=false
similarity index 57%
rename from test/test-network/conf/25-neighbor-next.network
rename to test/test-network/conf/26-bridge-mac-slave.network
index 6911f4810d48b7e36446769fdf92087f81f4d4d1..81a0b468e6e79cc2c885b245255a7167d98af721 100644 (file)
@@ -4,7 +4,4 @@ Name=dummy98
 
 [Network]
 IPv6AcceptRA=no
-
-[Neighbor]
-Address=192.168.10.1
-LinkLayerAddress=00:00:5e:00:02:66
+Bridge=bridge99
diff --git a/test/test-network/conf/26-bridge-mac.link b/test/test-network/conf/26-bridge-mac.link
new file mode 100644 (file)
index 0000000..82ed937
--- /dev/null
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Match]
+OriginalName=bridge99
+
+[Link]
+MACAddressPolicy=none
diff --git a/test/test-network/conf/26-bridge-mac.netdev b/test/test-network/conf/26-bridge-mac.netdev
new file mode 100644 (file)
index 0000000..2d26a03
--- /dev/null
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[NetDev]
+Name=bridge99
+Kind=bridge
+MACAddress=none
index eefd7192a75198bc319fa1a2232e75187101b3cc..5e26587843207bbee6ae31d35d1555d93f144299 100755 (executable)
@@ -27,6 +27,7 @@ network_unit_dir = '/run/systemd/network'
 networkd_conf_dropin_dir = '/run/systemd/networkd.conf.d'
 networkd_ci_temp_dir = '/run/networkd-ci'
 udev_rules_dir = '/run/udev/rules.d'
+credstore_dir = '/run/credstore'
 
 dnsmasq_pid_file = '/run/networkd-ci/test-dnsmasq.pid'
 dnsmasq_log_file = '/run/networkd-ci/test-dnsmasq.log'
@@ -298,6 +299,11 @@ def copy_network_unit(*units, copy_dropins=True):
     if has_link:
         udev_reload()
 
+def copy_credential(src, target):
+        mkdir_p(credstore_dir)
+        cp(os.path.join(networkd_ci_temp_dir, src),
+           os.path.join(credstore_dir, target))
+
 def remove_network_unit(*units):
     """
     Remove previously copied unit files from the testbed.
@@ -707,12 +713,18 @@ def read_networkd_log(invocation_id=None, since=None):
         invocation_id = networkd_invocation_id()
     command = [
         'journalctl',
+        '--no-hostname',
+        '--output=short-monotonic',
         f'_SYSTEMD_INVOCATION_ID={invocation_id}',
     ]
     if since:
         command.append(f'--since={since}')
+    check_output('journalctl --sync')
     return check_output(*command)
 
+def networkd_is_failed():
+    return call_quiet('systemctl is-failed -q systemd-networkd.service') != 1
+
 def stop_networkd(show_logs=True):
     if show_logs:
         invocation_id = networkd_invocation_id()
@@ -721,7 +733,7 @@ def stop_networkd(show_logs=True):
     if show_logs:
         print(read_networkd_log(invocation_id))
     # Check if networkd exits cleanly.
-    assert call_quiet('systemctl is-failed -q systemd-networkd.service') == 1
+    assert not networkd_is_failed()
 
 def start_networkd():
     check_output('systemctl start systemd-networkd')
@@ -736,16 +748,34 @@ def restart_networkd(show_logs=True):
 def networkd_pid():
     return int(check_output('systemctl show --value -p MainPID systemd-networkd.service'))
 
+def networkctl(*args):
+    # Do not call networkctl if networkd is in failed state.
+    # Otherwise, networkd may be restarted and we may get wrong results.
+    assert not networkd_is_failed()
+    return check_output(*(networkctl_cmd + list(args)), env=env)
+
+def networkctl_status(*args):
+    return networkctl('-n', '0', 'status', *args)
+
+def networkctl_json(*args):
+    return networkctl('--json=short', 'status', *args)
+
 def networkctl_reconfigure(*links):
-    check_output(*networkctl_cmd, 'reconfigure', *links, env=env)
+    networkctl('reconfigure', *links)
 
 def networkctl_reload(sleep_time=1):
-    check_output(*networkctl_cmd, 'reload', env=env)
+    networkctl('reload')
     # 'networkctl reload' asynchronously reconfigure links.
     # Hence, we need to wait for a short time for link to be in configuring state.
     if sleep_time > 0:
         time.sleep(sleep_time)
 
+def resolvectl(*args):
+    return check_output(*(resolvectl_cmd + list(args)), env=env)
+
+def timedatectl(*args):
+    return check_output(*(timedatectl_cmd + list(args)), env=env)
+
 def setup_common():
     print()
 
@@ -895,7 +925,6 @@ class Utilities():
 
     def wait_activated(self, link, state='down', timeout=20, fail_assert=True):
         # wait for the interface is activated.
-        invocation_id = check_output('systemctl show systemd-networkd -p InvocationID --value')
         needle = f'{link}: Bringing link {state}'
         flag = state.upper()
         for iteration in range(timeout + 1):
@@ -903,7 +932,7 @@ class Utilities():
                 time.sleep(1)
             if not link_exists(link):
                 continue
-            output = check_output('journalctl _SYSTEMD_INVOCATION_ID=' + invocation_id)
+            output = read_networkd_log()
             if needle in output and flag in check_output(f'ip link show {link}'):
                 return True
         if fail_assert:
@@ -934,7 +963,7 @@ class Utilities():
                 time.sleep(1)
             if not link_exists(link):
                 continue
-            output = check_output(*networkctl_cmd, '-n', '0', 'status', link, env=env)
+            output = networkctl_status(link)
             if re.search(rf'(?m)^\s*State:\s+{operstate}\s+\({setup_state}\)\s*$', output):
                 return True
 
@@ -975,11 +1004,15 @@ class Utilities():
         try:
             check_output(*args, env=wait_online_env)
         except subprocess.CalledProcessError:
-            # show detailed status on failure
-            for link in links_with_operstate:
-                name = link.split(':')[0]
-                if link_exists(name):
-                    call(*networkctl_cmd, '-n', '0', 'status', name, env=env)
+            if networkd_is_failed():
+                print('!!!!! systemd-networkd.service is failed !!!!!')
+                call('systemctl status systemd-networkd.service')
+            else:
+                # show detailed status on failure
+                for link in links_with_operstate:
+                    name = link.split(':')[0]
+                    if link_exists(name):
+                        networkctl_status(name)
             raise
         if not bool_any and setup_state:
             for link in links_with_operstate:
@@ -1072,7 +1105,7 @@ class NetworkctlTests(unittest.TestCase, Utilities):
         start_networkd()
         self.wait_online(['dummy98:degraded'])
 
-        output = check_output(*networkctl_cmd, '-n', '0', 'status', 'dummy98', env=env)
+        output = networkctl_status('dummy98')
         self.assertRegex(output, 'hogehogehogehogehogehoge')
 
     @expectedFailureIfAlternativeNameIsNotAvailable()
@@ -1082,7 +1115,7 @@ class NetworkctlTests(unittest.TestCase, Utilities):
         start_networkd()
         self.wait_online(['dummyalt:degraded'])
 
-        output = check_output(*networkctl_cmd, '-n', '0', 'status', 'dummyalt', env=env)
+        output = networkctl_status('dummyalt')
         self.assertIn('hogehogehogehogehogehoge', output)
         self.assertNotIn('dummy98', output)
 
@@ -1134,7 +1167,7 @@ class NetworkctlTests(unittest.TestCase, Utilities):
     def test_renew(self):
         def check():
             self.wait_online(['veth99:routable', 'veth-peer:routable'])
-            output = check_output(*networkctl_cmd, '-n', '0', 'status', 'veth99', env=env)
+            output = networkctl_status('veth99')
             print(output)
             self.assertRegex(output, r'Address: 192.168.5.[0-9]* \(DHCP4 via 192.168.5.1\)')
             self.assertIn('Gateway: 192.168.5.3', output)
@@ -1144,13 +1177,12 @@ class NetworkctlTests(unittest.TestCase, Utilities):
         copy_network_unit('25-veth.netdev', '25-dhcp-client.network', '25-dhcp-server.network')
         start_networkd()
         check()
-        output = check_output(*networkctl_cmd, '--lines=0', '--stats', '--all', '--full', '--json=short', 'status')
-        check_json(output)
+        check_json(networkctl_json('--lines=0', '--stats', '--all', '--full'))
 
         for verb in ['renew', 'forcerenew']:
-            call_check(*networkctl_cmd, verb, 'veth99')
+            networkctl(verb, 'veth99')
             check()
-            call_check(*networkctl_cmd, verb, 'veth99', 'veth99', 'veth99')
+            networkctl(verb, 'veth99', 'veth99', 'veth99')
             check()
 
     def test_up_down(self):
@@ -1158,13 +1190,13 @@ class NetworkctlTests(unittest.TestCase, Utilities):
         start_networkd()
         self.wait_online(['dummy98:routable'])
 
-        call_check(*networkctl_cmd, 'down', 'dummy98')
+        networkctl('down', 'dummy98')
         self.wait_online(['dummy98:off'])
-        call_check(*networkctl_cmd, 'up', 'dummy98')
+        networkctl('up', 'dummy98')
         self.wait_online(['dummy98:routable'])
-        call_check(*networkctl_cmd, 'down', 'dummy98', 'dummy98', 'dummy98')
+        networkctl('down', 'dummy98', 'dummy98', 'dummy98')
         self.wait_online(['dummy98:off'])
-        call_check(*networkctl_cmd, 'up', 'dummy98', 'dummy98', 'dummy98')
+        networkctl('up', 'dummy98', 'dummy98', 'dummy98')
         self.wait_online(['dummy98:routable'])
 
     def test_reload(self):
@@ -1196,23 +1228,23 @@ class NetworkctlTests(unittest.TestCase, Utilities):
 
         self.wait_online(['test1:degraded'])
 
-        output = check_output(*networkctl_cmd, 'list', env=env)
+        output = networkctl('list')
         self.assertRegex(output, '1 lo ')
         self.assertRegex(output, 'test1')
 
-        output = check_output(*networkctl_cmd, 'list', 'test1', env=env)
+        output = networkctl('list', 'test1')
         self.assertNotRegex(output, '1 lo ')
         self.assertRegex(output, 'test1')
 
-        output = check_output(*networkctl_cmd, 'list', 'te*', env=env)
+        output = networkctl('list', 'te*')
         self.assertNotRegex(output, '1 lo ')
         self.assertRegex(output, 'test1')
 
-        output = check_output(*networkctl_cmd, '-n', '0', 'status', 'te*', env=env)
+        output = networkctl_status('te*')
         self.assertNotRegex(output, '1: lo ')
         self.assertRegex(output, 'test1')
 
-        output = check_output(*networkctl_cmd, '-n', '0', 'status', 'tes[a-z][0-9]', env=env)
+        output = networkctl_status('tes[a-z][0-9]')
         self.assertNotRegex(output, '1: lo ')
         self.assertRegex(output, 'test1')
 
@@ -1222,7 +1254,7 @@ class NetworkctlTests(unittest.TestCase, Utilities):
 
         self.wait_online(['test1:degraded'])
 
-        output = check_output(*networkctl_cmd, '-n', '0', 'status', 'test1', env=env)
+        output = networkctl_status('test1')
         self.assertRegex(output, 'MTU: 1600')
 
     def test_type(self):
@@ -1230,11 +1262,11 @@ class NetworkctlTests(unittest.TestCase, Utilities):
         start_networkd()
         self.wait_online(['test1:degraded'])
 
-        output = check_output(*networkctl_cmd, '-n', '0', 'status', 'test1', env=env)
+        output = networkctl_status('test1')
         print(output)
         self.assertRegex(output, 'Type: ether')
 
-        output = check_output(*networkctl_cmd, '-n', '0', 'status', 'lo', env=env)
+        output = networkctl_status('lo')
         print(output)
         self.assertRegex(output, 'Type: loopback')
 
@@ -1243,7 +1275,7 @@ class NetworkctlTests(unittest.TestCase, Utilities):
         start_networkd()
         self.wait_online(['test1:degraded'])
 
-        output = check_output(*networkctl_cmd, '-n', '0', 'status', 'test1', env=env)
+        output = networkctl_status('test1')
         print(output)
         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)
@@ -1257,7 +1289,7 @@ class NetworkctlTests(unittest.TestCase, Utilities):
         # In that case, the udev DB for the loopback network interface may already have ID_NET_LINK_FILE property.
         # Let's reprocess the interface and drop the property.
         check_output(*udevadm_cmd, 'trigger', '--settle', '--action=add', '/sys/class/net/lo')
-        output = check_output(*networkctl_cmd, '-n', '0', 'status', 'lo', env=env)
+        output = networkctl_status('lo')
         print(output)
         self.assertIn('Link File: n/a', output)
         self.assertIn('Network File: n/a', output)
@@ -1269,13 +1301,13 @@ class NetworkctlTests(unittest.TestCase, Utilities):
 
         self.wait_online(['test1:degraded', 'veth99:degraded', 'veth-peer:degraded'])
 
-        check_output(*networkctl_cmd, 'delete', 'test1', 'veth99', env=env)
+        networkctl('delete', 'test1', 'veth99')
         self.check_link_exists('test1', expected=False)
         self.check_link_exists('veth99', expected=False)
         self.check_link_exists('veth-peer', expected=False)
 
     def test_label(self):
-        call_check(*networkctl_cmd, 'label')
+        networkctl('label')
 
 class NetworkdMatchTests(unittest.TestCase, Utilities):
 
@@ -1296,7 +1328,7 @@ class NetworkdMatchTests(unittest.TestCase, Utilities):
         start_networkd()
 
         self.wait_online(['dummy98:routable'])
-        output = check_output(*networkctl_cmd, '-n', '0', 'status', 'dummy98', env=env)
+        output = networkctl_status('dummy98')
         self.assertIn('Network File: /run/systemd/network/12-dummy-match-mac-01.network', output)
         output = check_output('ip -4 address show dev dummy98')
         self.assertIn('10.0.0.1/16', output)
@@ -1306,7 +1338,7 @@ class NetworkdMatchTests(unittest.TestCase, Utilities):
 
         self.wait_address('dummy98', '10.0.0.2/16', ipv='-4', timeout_sec=10)
         self.wait_online(['dummy98:routable'])
-        output = check_output(*networkctl_cmd, '-n', '0', 'status', 'dummy98', env=env)
+        output = networkctl_status('dummy98')
         self.assertIn('Network File: /run/systemd/network/12-dummy-match-mac-02.network', output)
 
         check_output('ip link set dev dummy98 down')
@@ -1314,7 +1346,7 @@ class NetworkdMatchTests(unittest.TestCase, Utilities):
 
         self.wait_address('dummy98-1', '10.0.1.2/16', ipv='-4', timeout_sec=10)
         self.wait_online(['dummy98-1:routable'])
-        output = check_output(*networkctl_cmd, '-n', '0', 'status', 'dummy98-1', env=env)
+        output = networkctl_status('dummy98-1')
         self.assertIn('Network File: /run/systemd/network/12-dummy-match-renamed.network', output)
 
         check_output('ip link set dev dummy98-1 down')
@@ -1323,7 +1355,7 @@ class NetworkdMatchTests(unittest.TestCase, Utilities):
 
         self.wait_address('dummy98-2', '10.0.2.2/16', ipv='-4', timeout_sec=10)
         self.wait_online(['dummy98-2:routable'])
-        output = check_output(*networkctl_cmd, '-n', '0', 'status', 'dummy98-2', env=env)
+        output = networkctl_status('dummy98-2')
         self.assertIn('Network File: /run/systemd/network/12-dummy-match-altname.network', output)
 
     def test_match_udev_property(self):
@@ -1331,7 +1363,7 @@ class NetworkdMatchTests(unittest.TestCase, Utilities):
         start_networkd()
         self.wait_online(['dummy98:routable'])
 
-        output = check_output(*networkctl_cmd, '-n', '0', 'status', 'dummy98', env=env)
+        output = networkctl_status('dummy98')
         print(output)
         self.assertRegex(output, 'Network File: /run/systemd/network/14-match-udev-property')
 
@@ -1410,7 +1442,7 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities):
         self.assertEqual(1,         int(read_link_attr('bridge99', 'bridge', 'stp_state')))
         self.assertEqual(3,         int(read_link_attr('bridge99', 'bridge', 'multicast_igmp_version')))
 
-        output = check_output(*networkctl_cmd, '-n', '0', 'status', 'bridge99', env=env)
+        output = networkctl_status('bridge99')
         print(output)
         self.assertRegex(output, 'Priority: 9')
         self.assertRegex(output, 'STP: yes')
@@ -1443,14 +1475,14 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities):
         self.check_link_attr('bond98', 'bonding', 'mode',              'balance-tlb 5')
         self.check_link_attr('bond98', 'bonding', 'tlb_dynamic_lb',    '1')
 
-        output = check_output(*networkctl_cmd, '-n', '0', 'status', 'bond99', env=env)
+        output = networkctl_status('bond99')
         print(output)
         self.assertIn('Mode: 802.3ad', output)
         self.assertIn('Miimon: 1s', output)
         self.assertIn('Updelay: 2s', output)
         self.assertIn('Downdelay: 2s', output)
 
-        output = check_output(*networkctl_cmd, '-n', '0', 'status', 'bond98', env=env)
+        output = networkctl_status('bond98')
         print(output)
         self.assertIn('Mode: balance-tlb', output)
 
@@ -1758,6 +1790,10 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities):
 
     @expectedFailureIfModuleIsNotAvailable('wireguard')
     def test_wireguard(self):
+        copy_credential('25-wireguard-endpoint-peer0-cred.txt', 'network.wireguard.peer0.endpoint')
+        copy_credential('25-wireguard-preshared-key-peer2-cred.txt', 'network.wireguard.peer2.psk')
+        copy_credential('25-wireguard-no-peer-private-key-cred.txt', 'network.wireguard.private.25-wireguard-no-peer')
+
         copy_network_unit('25-wireguard.netdev', '25-wireguard.network',
                           '25-wireguard-23-peers.netdev', '25-wireguard-23-peers.network',
                           '25-wireguard-preshared-key.txt', '25-wireguard-private-key.txt',
@@ -2323,7 +2359,7 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities):
         self.assertIn('00:11:22:33:44:66 dst 10.0.0.6 self permanent', output)
         self.assertIn('00:11:22:33:44:77 dst 10.0.0.7 via test1 self permanent', output)
 
-        output = check_output(*networkctl_cmd, '-n', '0', 'status', 'vxlan99', env=env)
+        output = networkctl_status('vxlan99')
         print(output)
         self.assertIn('VNI: 999', output)
         self.assertIn('Destination Port: 5555', output)
@@ -2564,8 +2600,7 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
         # netlabel
         self.check_netlabel('dummy98', r'10\.10\.1\.0/24')
 
-        output = check_output(*networkctl_cmd, '--json=short', 'status', env=env)
-        check_json(output)
+        check_json(networkctl_json())
 
     def test_address_static(self):
         copy_network_unit('25-address-static.network', '12-dummy.netdev', copy_dropins=False)
@@ -2891,7 +2926,7 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
                     check_output(f'ip link set dev test1 carrier {carrier}')
                 self.wait_online([f'test1:{routable_map[carrier]}:{routable_map[carrier]}'])
 
-                output = check_output(*networkctl_cmd, '-n', '0', 'status', 'test1', env=env)
+                output = networkctl_status('test1')
                 print(output)
                 self.assertRegex(output, '192.168.0.15')
                 self.assertRegex(output, '192.168.0.1')
@@ -2915,7 +2950,7 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
                     check_output(f'ip link set dev test1 carrier {carrier}')
                 self.wait_online([f'test1:{routable_map[carrier]}:{routable_map[carrier]}'])
 
-                output = check_output(*networkctl_cmd, '-n', '0', 'status', 'test1', env=env)
+                output = networkctl_status('test1')
                 print(output)
                 if have_config:
                     self.assertRegex(output, '192.168.0.15')
@@ -2960,8 +2995,7 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
         self.assertRegex(output, 'iif test1')
         self.assertRegex(output, 'lookup 10')
 
-        output = check_output(*networkctl_cmd, '--json=short', 'status', env=env)
-        check_json(output)
+        check_json(networkctl_json())
 
     def test_routing_policy_rule_issue_11280(self):
         copy_network_unit('25-routing-policy-rule-test1.network', '11-dummy.netdev',
@@ -3089,7 +3123,7 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
         start_networkd()
         self.wait_online(['dummy98:routable'])
 
-        output = check_output(*networkctl_cmd, '-n', '0', 'status', 'dummy98', env=env)
+        output = networkctl_status('dummy98')
         print(output)
 
         print('### ip -6 route show dev dummy98')
@@ -3192,8 +3226,7 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
         self.assertIn('via 2001:1234:5:8fff:ff:ff:ff:ff dev dummy98', output)
         self.assertIn('via 2001:1234:5:9fff:ff:ff:ff:ff dev dummy98', output)
 
-        output = check_output(*networkctl_cmd, '--json=short', 'status', env=env)
-        check_json(output)
+        check_json(networkctl_json())
 
         copy_network_unit('25-address-static.network', copy_dropins=False)
         networkctl_reload()
@@ -3317,7 +3350,7 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
         start_networkd()
         self.wait_online(['dummy98:routable'])
 
-        output = check_output(*networkctl_cmd, '-n', '0', 'status', 'dummy98', env=env)
+        output = networkctl_status('dummy98')
         print(output)
 
         print('### ip -6 route show dev dummy98')
@@ -3448,10 +3481,25 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
         for i in range(1, 5):
             self.assertRegex(output, f'2607:5300:203:5215:{i}::1 *proxy')
 
-    def test_neighbor_section(self):
-        copy_network_unit('25-neighbor-section.network', '12-dummy.netdev', copy_dropins=False)
+    def test_neighbor(self):
+        copy_network_unit('12-dummy.netdev', '25-neighbor-dummy.network', '25-neighbor-dummy.network.d/10-step1.conf',
+                          '25-gre-tunnel-remote-any.netdev', '25-neighbor-ip.network',
+                          '25-ip6gre-tunnel-remote-any.netdev', '25-neighbor-ipv6.network',
+                          copy_dropins=False)
         start_networkd()
-        self.wait_online(['dummy98:degraded'])
+        self.wait_online(['dummy98:degraded', 'gretun97:routable', 'ip6gretun97:routable'])
+
+        print('### ip neigh list dev gretun97')
+        output = check_output('ip neigh list dev gretun97')
+        print(output)
+        self.assertIn('10.0.0.22 lladdr 10.65.223.239 PERMANENT', output)
+        self.assertNotIn('10.0.0.23', output)
+
+        print('### ip neigh list dev ip6gretun97')
+        output = check_output('ip neigh list dev ip6gretun97')
+        print(output)
+        self.assertRegex(output, '2001:db8:0:f102::17 lladdr 2a:?00:ff:?de:45:?67:ed:?de:[0:]*:49:?88 PERMANENT')
+        self.assertNotIn('2001:db8:0:f102::18', output)
 
         print('### ip neigh list dev dummy98')
         output = check_output('ip neigh list dev dummy98')
@@ -3462,63 +3510,40 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
         self.assertNotIn('192.168.10.2', output)
         self.assertNotIn('00:00:5e:00:02:67', output)
 
-        output = check_output(*networkctl_cmd, '--json=short', 'status', env=env)
-        check_json(output)
+        check_json(networkctl_json())
 
-        copy_network_unit('25-neighbor-section.network.d/override.conf')
+        # Here, 10-step1.conf is intendedly kept, to verify that 10-step2.conf overrides
+        # the valid configurations in 10-step1.conf.
+        copy_network_unit('25-neighbor-dummy.network.d/10-step2.conf')
         networkctl_reload()
         self.wait_online(['dummy98:degraded'])
 
-        print('### ip neigh list dev dummy98 (after reloading)')
+        print('### ip neigh list dev dummy98')
         output = check_output('ip neigh list dev dummy98')
         print(output)
         self.assertIn('192.168.10.1 lladdr 00:00:5e:00:03:65 PERMANENT', output)
         self.assertIn('2004:da8:1::1 lladdr 00:00:5e:00:03:66 PERMANENT', output)
         self.assertNotIn('2004:da8:1:0::2', output)
         self.assertNotIn('192.168.10.2', output)
-        self.assertNotIn('00:00:5e:00:02', output)
+        self.assertNotIn('00:00:5e:00:02:67', output)
 
-    def test_neighbor_reconfigure(self):
-        copy_network_unit('25-neighbor-section.network', '12-dummy.netdev', copy_dropins=False)
-        start_networkd()
-        self.wait_online(['dummy98:degraded'])
+        check_json(networkctl_json())
 
-        print('### ip neigh list dev dummy98')
-        output = check_output('ip neigh list dev dummy98')
-        print(output)
-        self.assertIn('192.168.10.1 lladdr 00:00:5e:00:02:65 PERMANENT', output)
-        self.assertIn('2004:da8:1::1 lladdr 00:00:5e:00:02:66 PERMANENT', output)
-
-        remove_network_unit('25-neighbor-section.network')
-        copy_network_unit('25-neighbor-next.network')
+        remove_network_unit('25-neighbor-dummy.network.d/10-step1.conf',
+                            '25-neighbor-dummy.network.d/10-step2.conf')
+        copy_network_unit('25-neighbor-dummy.network.d/10-step3.conf')
         networkctl_reload()
         self.wait_online(['dummy98:degraded'])
+
         print('### ip neigh list dev dummy98')
         output = check_output('ip neigh list dev dummy98')
         print(output)
+        self.assertIn('192.168.10.1 lladdr 00:00:5e:00:03:66 PERMANENT', output)
         self.assertNotIn('00:00:5e:00:02:65', output)
-        self.assertIn('192.168.10.1 lladdr 00:00:5e:00:02:66 PERMANENT', output)
+        self.assertNotIn('00:00:5e:00:02:66', output)
+        self.assertNotIn('00:00:5e:00:03:65', output)
         self.assertNotIn('2004:da8:1::1', output)
 
-    def test_neighbor_gre(self):
-        copy_network_unit('25-neighbor-ip.network', '25-neighbor-ipv6.network', '25-neighbor-ip-dummy.network',
-                          '12-dummy.netdev', '25-gre-tunnel-remote-any.netdev', '25-ip6gre-tunnel-remote-any.netdev')
-        start_networkd()
-        self.wait_online(['dummy98:degraded', 'gretun97:routable', 'ip6gretun97:routable'], timeout='40s')
-
-        output = check_output('ip neigh list dev gretun97')
-        print(output)
-        self.assertIn('10.0.0.22 lladdr 10.65.223.239 PERMANENT', output)
-        self.assertNotIn('10.0.0.23', output)
-
-        output = check_output('ip neigh list dev ip6gretun97')
-        print(output)
-        self.assertRegex(output, '2001:db8:0:f102::17 lladdr 2a:?00:ff:?de:45:?67:ed:?de:[0:]*:49:?88 PERMANENT')
-        self.assertNotIn('2001:db8:0:f102::18', output)
-
-        output = check_output(*networkctl_cmd, '--json=short', 'status', env=env)
-        check_json(output)
-
     def test_link_local_addressing(self):
         copy_network_unit('25-link-local-addressing-yes.network', '11-dummy.netdev',
                           '25-link-local-addressing-no.network', '12-dummy.netdev')
@@ -3808,7 +3833,7 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
             # default is true, if neither are specified
             expected = True
 
-        output = check_output(*networkctl_cmd, '-n', '0', 'status', 'test1', env=env)
+        output = networkctl_status('test1')
         print(output)
 
         yesno = 'yes' if expected else 'no'
@@ -3832,7 +3857,7 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
         start_networkd()
         self.wait_online(['dummy98:routable'])
 
-        output = check_output(*networkctl_cmd, '-n', '0', 'status', 'dummy98', env=env)
+        output = networkctl_status('dummy98')
         print(output)
         self.assertRegex(output, 'Address: 192.168.42.100')
         self.assertRegex(output, 'DNS: 192.168.42.1')
@@ -3858,23 +3883,34 @@ 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')
 
-    def check_nexthop(self, manage_foreign_nexthops):
+    def check_nexthop(self, manage_foreign_nexthops, first):
         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)
+        if first:
+            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)
+        else:
+            self.assertIn('id 6 via 192.168.5.1 dev veth99', output)
+            self.assertIn('id 7 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')
+        if first:
+            self.assertRegex(output, 'id 5 via 192.168.10.1 dev veth99 .*onlink')
+        else:
+            self.assertIn('id 5 via 192.168.5.3 dev veth99', output)
+            self.assertNotRegex(output, 'id 5 via 192.168.5.3 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)
+        if first:
+            self.assertIn('id 20 via 192.168.20.1 dev dummy98', output)
+        else:
+            self.assertIn('id 21 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:
@@ -3883,46 +3919,76 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
         # 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)
+        if first:
+            self.assertIn('id 6 blackhole', output)
+            self.assertIn('id 7 blackhole', output)
+        else:
+            self.assertIn('id 1 blackhole', output)
+            self.assertIn('id 2 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)')
+        if first:
+            output = check_output('ip -0 nexthop list id 21')
+            print(output)
+            self.assertRegex(output, r'id 21 group (1,3/20|20/1,3)')
+        else:
+            output = check_output('ip -0 nexthop list id 20')
+            print(output)
+            self.assertRegex(output, r'id 20 group (5,3/21|21/5,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)
+        if first:
+            self.assertEqual('10.10.10.10 nhid 1 via 192.168.5.1 proto static', output)
+        else:
+            self.assertEqual('10.10.10.10 nhid 6 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)
+        if first:
+            self.assertEqual('10.10.10.11 nhid 2 via inet6 2001:1234:5:8f63::2 proto static', output)
+        else:
+            self.assertEqual('10.10.10.11 nhid 7 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)
+        if first:
+            self.assertEqual('10.10.10.12 nhid 5 via 192.168.10.1 proto static onlink', output)
+        else:
+            self.assertEqual('10.10.10.12 nhid 5 via 192.168.5.3 proto static', 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)
+        if first:
+            self.assertEqual('2001:1234:5:8f62::1 nhid 2 via 2001:1234:5:8f63::2 proto static metric 1024 pref medium', output)
+        else:
+            self.assertEqual('2001:1234:5:8f62::1 nhid 7 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)
+        if first:
+            self.assertEqual('blackhole 10.10.10.13 nhid 6 dev lo proto static', output)
+        else:
+            self.assertEqual('blackhole 10.10.10.13 nhid 1 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)
+        if first:
+            self.assertEqual('blackhole 2001:1234:5:8f62::2 nhid 7 dev lo proto static metric 1024 pref medium', output)
+        else:
+            self.assertEqual('blackhole 2001:1234:5:8f62::2 nhid 2 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)
+        if first:
+            self.assertIn('10.10.10.14 nhid 21 proto static', output)
+            self.assertIn('nexthop via 192.168.5.1 dev veth99 weight 3', output)
+        else:
+            self.assertIn('10.10.10.14 nhid 20 proto static', output)
+            self.assertIn('nexthop via 192.168.5.3 dev veth99 weight 3', 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)
+        check_json(networkctl_json())
 
     def _test_nexthop(self, manage_foreign_nexthops):
         if not manage_foreign_nexthops:
@@ -3933,13 +3999,18 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
         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')
+        copy_network_unit('25-nexthop-1.network', '25-veth.netdev', '25-veth-peer.network',
+                          '12-dummy.netdev', '25-nexthop-dummy-1.network')
         start_networkd()
 
-        self.check_nexthop(manage_foreign_nexthops)
+        self.check_nexthop(manage_foreign_nexthops, first=True)
 
-        remove_network_unit('25-nexthop.network')
+        remove_network_unit('25-nexthop-1.network', '25-nexthop-dummy-1.network')
+        copy_network_unit('25-nexthop-2.network', '25-nexthop-dummy-2.network')
+        networkctl_reload()
+        self.check_nexthop(manage_foreign_nexthops, first=False)
+
+        remove_network_unit('25-nexthop-2.network')
         copy_network_unit('25-nexthop-nothing.network')
         networkctl_reload()
         self.wait_online(['veth99:routable', 'veth-peer:routable'])
@@ -3951,12 +4022,11 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
         print(output)
         self.assertEqual(output, '')
 
-        remove_network_unit('25-nexthop-nothing.network')
-        copy_network_unit('25-nexthop.network')
-        networkctl_reconfigure('dummy98')
+        remove_network_unit('25-nexthop-nothing.network', '25-nexthop-dummy-2.network')
+        copy_network_unit('25-nexthop-1.network', '25-nexthop-dummy-1.network')
         networkctl_reload()
 
-        self.check_nexthop(manage_foreign_nexthops)
+        self.check_nexthop(manage_foreign_nexthops, first=True)
 
         remove_link('veth99')
         time.sleep(2)
@@ -4261,10 +4331,9 @@ class NetworkdStateFileTests(unittest.TestCase, Utilities):
         self.wait_online(['dummy98:routable'])
 
         # make link state file updated
-        check_output(*resolvectl_cmd, 'revert', 'dummy98', env=env)
+        resolvectl('revert', 'dummy98')
 
-        output = check_output(*networkctl_cmd, '--json=short', 'status', env=env)
-        check_json(output)
+        check_json(networkctl_json())
 
         output = read_link_state_file('dummy98')
         print(output)
@@ -4285,15 +4354,14 @@ class NetworkdStateFileTests(unittest.TestCase, Utilities):
         self.assertIn('MDNS=yes', output)
         self.assertIn('DNSSEC=no', output)
 
-        check_output(*resolvectl_cmd, 'dns', 'dummy98', '10.10.10.12#ccc.com', '10.10.10.13', '1111:2222::3333', env=env)
-        check_output(*resolvectl_cmd, 'domain', 'dummy98', 'hogehogehoge', '~foofoofoo', env=env)
-        check_output(*resolvectl_cmd, 'llmnr', 'dummy98', 'yes', env=env)
-        check_output(*resolvectl_cmd, 'mdns', 'dummy98', 'no', env=env)
-        check_output(*resolvectl_cmd, 'dnssec', 'dummy98', 'yes', env=env)
-        check_output(*timedatectl_cmd, 'ntp-servers', 'dummy98', '2.fedora.pool.ntp.org', '3.fedora.pool.ntp.org', env=env)
+        resolvectl('dns', 'dummy98', '10.10.10.12#ccc.com', '10.10.10.13', '1111:2222::3333')
+        resolvectl('domain', 'dummy98', 'hogehogehoge', '~foofoofoo')
+        resolvectl('llmnr', 'dummy98', 'yes')
+        resolvectl('mdns', 'dummy98', 'no')
+        resolvectl('dnssec', 'dummy98', 'yes')
+        timedatectl('ntp-servers', 'dummy98', '2.fedora.pool.ntp.org', '3.fedora.pool.ntp.org')
 
-        output = check_output(*networkctl_cmd, '--json=short', 'status', env=env)
-        check_json(output)
+        check_json(networkctl_json())
 
         output = read_link_state_file('dummy98')
         print(output)
@@ -4305,10 +4373,9 @@ class NetworkdStateFileTests(unittest.TestCase, Utilities):
         self.assertIn('MDNS=no', output)
         self.assertIn('DNSSEC=yes', output)
 
-        check_output(*timedatectl_cmd, 'revert', 'dummy98', env=env)
+        timedatectl('revert', 'dummy98')
 
-        output = check_output(*networkctl_cmd, '--json=short', 'status', env=env)
-        check_json(output)
+        check_json(networkctl_json())
 
         output = read_link_state_file('dummy98')
         print(output)
@@ -4320,10 +4387,9 @@ class NetworkdStateFileTests(unittest.TestCase, Utilities):
         self.assertIn('MDNS=no', output)
         self.assertIn('DNSSEC=yes', output)
 
-        check_output(*resolvectl_cmd, 'revert', 'dummy98', env=env)
+        resolvectl('revert', 'dummy98')
 
-        output = check_output(*networkctl_cmd, '--json=short', 'status', env=env)
-        check_json(output)
+        check_json(networkctl_json())
 
         output = read_link_state_file('dummy98')
         print(output)
@@ -4474,6 +4540,20 @@ class NetworkdBridgeTests(unittest.TestCase, Utilities):
     def tearDown(self):
         tear_down_common()
 
+    def test_bridge_mac_none(self):
+        copy_network_unit('12-dummy-mac.netdev', '26-bridge-mac-slave.network',
+                          '26-bridge-mac.netdev', '26-bridge-mac-master.network', '26-bridge-mac.link')
+        start_networkd()
+        self.wait_online(['dummy98:enslaved', 'bridge99:degraded'])
+
+        output = check_output('ip link show dev dummy98')
+        print(output)
+        self.assertIn('link/ether 12:34:56:78:9a:01', output)
+
+        output = check_output('ip link show dev bridge99')
+        print(output)
+        self.assertIn('link/ether 12:34:56:78:9a:01', output)
+
     def test_bridge_vlan(self):
         copy_network_unit('11-dummy.netdev', '26-bridge-vlan-slave.network',
                           '26-bridge.netdev', '26-bridge-vlan-master.network',
@@ -4798,7 +4878,7 @@ class NetworkdBridgeTests(unittest.TestCase, Utilities):
                     self.wait_online(['bridge99:no-carrier:no-carrier'])
                     self.check_link_attr('bridge99', 'carrier', '0')
 
-                output = check_output(*networkctl_cmd, '-n', '0', 'status', 'bridge99', env=env)
+                output = networkctl_status('bridge99')
                 self.assertRegex(output, '10.1.2.3')
                 self.assertRegex(output, '10.1.2.1')
 
@@ -4978,7 +5058,7 @@ class NetworkdLLDPTests(unittest.TestCase, Utilities):
             if trial > 0:
                 time.sleep(1)
 
-            output = check_output(*networkctl_cmd, 'lldp', env=env)
+            output = networkctl('lldp')
             print(output)
             if re.search(r'veth99 .* veth-peer', output):
                 break
@@ -5001,16 +5081,16 @@ class NetworkdRATests(unittest.TestCase, Utilities):
         start_networkd()
         self.wait_online(['veth99:routable', 'veth-peer:degraded'])
 
-        output = check_output(*resolvectl_cmd, 'dns', 'veth99', env=env)
+        output = resolvectl('dns', 'veth99')
         print(output)
         self.assertRegex(output, 'fe80::')
         self.assertRegex(output, '2002:da8:1::1')
 
-        output = check_output(*resolvectl_cmd, 'domain', 'veth99', env=env)
+        output = resolvectl('domain', 'veth99')
         print(output)
         self.assertIn('hogehoge.test', output)
 
-        output = check_output(*networkctl_cmd, '-n', '0', 'status', 'veth99', env=env)
+        output = networkctl_status('veth99')
         print(output)
         self.assertRegex(output, '2002:da8:1:0')
 
@@ -5030,7 +5110,7 @@ class NetworkdRATests(unittest.TestCase, Utilities):
         start_networkd()
         self.wait_online(['veth99:routable', 'veth-peer:degraded'])
 
-        output = check_output(*networkctl_cmd, '-n', '0', 'status', 'veth99', env=env)
+        output = networkctl_status('veth99')
         print(output)
         self.assertRegex(output, '2002:da8:1:0:1a:2b:3c:4d')
         self.assertRegex(output, '2002:da8:1:0:fa:de:ca:fe')
@@ -5042,7 +5122,7 @@ class NetworkdRATests(unittest.TestCase, Utilities):
         start_networkd()
         self.wait_online(['veth99:routable', 'veth-peer:degraded'])
 
-        output = check_output(*networkctl_cmd, '-n', '0', 'status', 'veth99', env=env)
+        output = networkctl_status('veth99')
         print(output)
         self.assertIn('2002:da8:1:0:b47e:7975:fc7a:7d6e', output)
         self.assertIn('2002:da8:2:0:1034:56ff:fe78:9abc', output) # EUI64
@@ -5052,7 +5132,7 @@ class NetworkdRATests(unittest.TestCase, Utilities):
         start_networkd()
         self.wait_online(['veth99:routable', 'veth-peer:degraded'])
 
-        output = check_output(*networkctl_cmd, '-n', '0', 'status', 'veth99', env=env)
+        output = networkctl_status('veth99')
         print(output)
         self.assertIn('2002:da8:1:0:b47e:7975:fc7a:7d6e', output)
         self.assertIn('2002:da8:2:0:f689:561a:8eda:7443', output)
@@ -5124,7 +5204,7 @@ class NetworkdRATests(unittest.TestCase, Utilities):
         self.wait_online(['client:routable'])
 
         self.wait_address('client', '2002:da8:1:99:1034:56ff:fe78:9a00/64', ipv='-6', timeout_sec=10)
-        output = check_output(*networkctl_cmd, 'status', 'client', env=env)
+        output = networkctl_status('client')
         print(output)
         self.assertIn('Captive Portal: http://systemd.io', output)
 
@@ -5160,7 +5240,7 @@ class NetworkdRATests(unittest.TestCase, Utilities):
             self.wait_online(['client:routable'])
 
             self.wait_address('client', '2002:da8:1:99:1034:56ff:fe78:9a00/64', ipv='-6', timeout_sec=10)
-            output = check_output(*networkctl_cmd, 'status', 'client', env=env)
+            output = networkctl_status('client')
             print(output)
             self.assertNotIn('Captive Portal:', output)
 
@@ -5177,14 +5257,14 @@ class NetworkdDHCPServerTests(unittest.TestCase, Utilities):
         start_networkd()
         self.wait_online(['veth99:routable', 'veth-peer:routable'])
 
-        output = check_output(*networkctl_cmd, '-n', '0', 'status', 'veth99', env=env)
+        output = networkctl_status('veth99')
         print(output)
         self.assertRegex(output, r'Address: 192.168.5.[0-9]* \(DHCP4 via 192.168.5.1\)')
         self.assertIn('Gateway: 192.168.5.3', output)
         self.assertRegex(output, 'DNS: 192.168.5.1\n *192.168.5.10')
         self.assertRegex(output, 'NTP: 192.168.5.1\n *192.168.5.11')
 
-        output = check_output(*networkctl_cmd, '-n', '0', 'status', 'veth-peer', env=env)
+        output = networkctl_status('veth-peer')
         self.assertRegex(output, "Offered DHCP leases: 192.168.5.[0-9]*")
 
     def test_dhcp_server_null_server_address(self):
@@ -5200,14 +5280,14 @@ class NetworkdDHCPServerTests(unittest.TestCase, Utilities):
         client_address = json.loads(output)[0]['addr_info'][0]['local']
         print(client_address)
 
-        output = check_output(*networkctl_cmd, '-n', '0', 'status', 'veth99', env=env)
+        output = networkctl_status('veth99')
         print(output)
         self.assertRegex(output, rf'Address: {client_address} \(DHCP4 via {server_address}\)')
         self.assertIn(f'Gateway: {server_address}', output)
         self.assertIn(f'DNS: {server_address}', output)
         self.assertIn(f'NTP: {server_address}', output)
 
-        output = check_output(*networkctl_cmd, '-n', '0', 'status', 'veth-peer', env=env)
+        output = networkctl_status('veth-peer')
         self.assertIn(f'Offered DHCP leases: {client_address}', output)
 
     def test_dhcp_server_with_uplink(self):
@@ -5216,7 +5296,7 @@ class NetworkdDHCPServerTests(unittest.TestCase, Utilities):
         start_networkd()
         self.wait_online(['veth99:routable', 'veth-peer:routable'])
 
-        output = check_output(*networkctl_cmd, '-n', '0', 'status', 'veth99', env=env)
+        output = networkctl_status('veth99')
         print(output)
         self.assertRegex(output, r'Address: 192.168.5.[0-9]* \(DHCP4 via 192.168.5.1\)')
         self.assertIn('Gateway: 192.168.5.3', output)
@@ -5228,7 +5308,7 @@ class NetworkdDHCPServerTests(unittest.TestCase, Utilities):
         start_networkd()
         self.wait_online(['veth99:routable', 'veth-peer:routable'])
 
-        output = check_output(*networkctl_cmd, '-n', '0', 'status', 'veth99', env=env)
+        output = networkctl_status('veth99')
         print(output)
         self.assertRegex(output, r'Address: 192.168.5.[0-9]* \(DHCP4 via 192.168.5.1\)')
         self.assertIn('Gateway: 192.168.5.1', output)
@@ -5239,7 +5319,7 @@ class NetworkdDHCPServerTests(unittest.TestCase, Utilities):
         start_networkd()
         self.wait_online(['veth99:routable', 'veth-peer:routable'])
 
-        output = check_output(*networkctl_cmd, '-n', '0', 'status', 'veth99', env=env)
+        output = networkctl_status('veth99')
         print(output)
         self.assertIn('Address: 10.1.1.200 (DHCP4 via 10.1.1.1)', output)
         self.assertIn('DHCP4 Client ID: 12:34:56:78:9a:bc', output)
@@ -5249,7 +5329,7 @@ class NetworkdDHCPServerTests(unittest.TestCase, Utilities):
         start_networkd()
         self.wait_online(['veth99:routable', 'veth-peer:routable'])
 
-        output = check_output(*networkctl_cmd, '-n', '0', 'status', 'veth99', env=env)
+        output = networkctl_status('veth99')
         print(output)
         self.assertIn('Address: 10.1.1.200 (DHCP4 via 10.1.1.1)', output)
         self.assertRegex(output, 'DHCP4 Client ID: IAID:[0-9a-z]*/DUID')
@@ -5273,7 +5353,7 @@ class NetworkdDHCPServerRelayAgentTests(unittest.TestCase, Utilities):
 
         self.wait_online(['client:routable'])
 
-        output = check_output(*networkctl_cmd, '-n', '0', 'status', 'client', env=env)
+        output = networkctl_status('client')
         print(output)
         self.assertRegex(output, r'Address: 192.168.5.150 \(DHCP4 via 192.168.5.1\)')
 
@@ -5332,8 +5412,7 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
         self.assertNotIn('DHCPREPLY(veth-peer)', output)
 
         # Check json format
-        output = check_output(*networkctl_cmd, '--json=short', 'status', 'veth99', env=env)
-        check_json(output)
+        check_json(networkctl_json('veth99'))
 
         # solicit mode
         stop_dnsmasq()
@@ -5360,7 +5439,7 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
         self.assertRegex(output, 'token :: dev veth99')
 
         # Make manager and link state file updated
-        check_output(*resolvectl_cmd, 'revert', 'veth99', env=env)
+        resolvectl('revert', 'veth99')
 
         # Check link state file
         print('## link state file')
@@ -5387,8 +5466,7 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
         self.assertIn('sent size:  0 option: 14 rapid-commit', output)
 
         # Check json format
-        output = check_output(*networkctl_cmd, '--json=short', 'status', 'veth99', env=env)
-        check_json(output)
+        check_json(networkctl_json('veth99'))
 
         # Testing without rapid commit support
         with open(os.path.join(network_unit_dir, '25-dhcp-client-ipv6-only.network'), mode='a', encoding='utf-8') as f:
@@ -5414,7 +5492,7 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
         self.assertRegex(output, 'via fe80::1034:56ff:fe78:9abd')
 
         # Make manager and link state file updated
-        check_output(*resolvectl_cmd, 'revert', 'veth99', env=env)
+        resolvectl('revert', 'veth99')
 
         # Check link state file
         print('## link state file')
@@ -5441,8 +5519,7 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
         self.assertNotIn('rapid-commit', output)
 
         # Check json format
-        output = check_output(*networkctl_cmd, '--json=short', 'status', 'veth99', env=env)
-        check_json(output)
+        check_json(networkctl_json('veth99'))
 
     def test_dhcp_client_ipv6_dbus_status(self):
         copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-ipv6-only.network')
@@ -5482,7 +5559,7 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
 
         # Test renew command
         # See https://github.com/systemd/systemd/pull/29472#issuecomment-1759092138
-        check_output(*networkctl_cmd, 'renew', 'veth99', env=env)
+        networkctl('renew', 'veth99')
 
         for _ in range(100):
             state = get_dhcp4_client_state('veth99')
@@ -5589,8 +5666,7 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
         self.assertIn('DOMAINS=example.com', output)
 
         print('## json')
-        output = check_output(*networkctl_cmd, '--json=short', 'status', 'veth99', env=env)
-        j = json.loads(output)
+        j = json.loads(networkctl_json('veth99'))
 
         self.assertEqual(len(j['DNS']), 2)
         for i in j['DNS']:
@@ -5685,8 +5761,7 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
         self.assertIn('DOMAINS=foo.example.com', output)
 
         print('## json')
-        output = check_output(*networkctl_cmd, '--json=short', 'status', 'veth99', env=env)
-        j = json.loads(output)
+        j = json.loads(networkctl_json('veth99'))
 
         self.assertEqual(len(j['DNS']), 3)
         for i in j['DNS']:
@@ -5908,8 +5983,7 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
             self.assertNotRegex(output, r'8.8.8.8 via 192.168.5.[0-9]* proto dhcp src 192.168.5.[0-9]* metric 1024')
             self.assertNotRegex(output, r'9.9.9.9 via 192.168.5.[0-9]* proto dhcp src 192.168.5.[0-9]* metric 1024')
 
-        output = check_output(*networkctl_cmd, '--json=short', 'status', env=env)
-        check_json(output)
+        check_json(networkctl_json())
 
     def test_dhcp_client_settings_anonymize(self):
         copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-anonymize.network')
@@ -6086,7 +6160,7 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
         start_dnsmasq()
         self.wait_online(['veth99:routable', 'veth-peer:routable'])
 
-        output = check_output(*networkctl_cmd, '-n', '0', 'status', 'veth99', env=env)
+        output = networkctl_status('veth99')
         print(output)
         self.assertRegex(output, '192.168.5')
 
@@ -6150,9 +6224,9 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
             self.wait_address('veth99', r'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', ipv='-6')
 
             # make resolved re-read the link state file
-            check_output(*resolvectl_cmd, 'revert', 'veth99', env=env)
+            resolvectl('revert', 'veth99')
 
-            output = check_output(*resolvectl_cmd, 'dns', 'veth99', env=env)
+            output = resolvectl('dns', 'veth99')
             print(output)
             if ipv4:
                 self.assertIn('192.168.5.1', output)
@@ -6163,8 +6237,7 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
             else:
                 self.assertNotIn('2600::1', output)
 
-            output = check_output(*networkctl_cmd, '--json=short', 'status', env=env)
-            check_json(output)
+            check_json(networkctl_json())
 
         copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client.network', copy_dropins=False)
 
@@ -6195,15 +6268,14 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
             self.wait_address('veth99', r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic', ipv='-4')
             self.wait_address('veth99', r'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', ipv='-6')
 
-            output = check_output(*networkctl_cmd, 'status', 'veth99', env=env)
+            output = networkctl_status('veth99')
             print(output)
             if ipv4 or ipv6:
                 self.assertIn('Captive Portal: http://systemd.io', output)
             else:
                 self.assertNotIn('Captive Portal: http://systemd.io', output)
 
-            output = check_output(*networkctl_cmd, '--json=short', 'status', env=env)
-            check_json(output)
+            check_json(networkctl_json())
 
         copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client.network', copy_dropins=False)
 
@@ -6234,13 +6306,12 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
             self.wait_address('veth99', r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic', ipv='-4')
             self.wait_address('veth99', r'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', ipv='-6')
 
-            output = check_output(*networkctl_cmd, 'status', 'veth99', env=env)
+            output = networkctl_status('veth99')
             print(output)
             self.assertNotIn('Captive Portal: ', output)
             self.assertNotIn('invalid/url', output)
 
-            output = check_output(*networkctl_cmd, '--json=short', 'status', env=env)
-            check_json(output)
+            check_json(networkctl_json())
 
         copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client.network', copy_dropins=False)
 
@@ -6798,18 +6869,17 @@ class NetworkdIPv6PrefixTests(unittest.TestCase, Utilities):
         self.assertIn('inet6 2001:db8:0:2:fa:de:ca:fe', output)
         self.assertNotIn('inet6 2001:db8:0:3:', output)
 
-        output = check_output(*resolvectl_cmd, 'dns', 'veth-peer', env=env)
+        output = resolvectl('dns', 'veth-peer')
         print(output)
         self.assertRegex(output, '2001:db8:1:1::2')
 
-        output = check_output(*resolvectl_cmd, 'domain', 'veth-peer', env=env)
+        output = resolvectl('domain', 'veth-peer')
         print(output)
         self.assertIn('example.com', output)
 
-        output = check_output(*networkctl_cmd, '--json=short', 'status', env=env)
-        check_json(output)
+        check_json(networkctl_json())
 
-        output = check_output(*networkctl_cmd, '--json=short', 'status', 'veth-peer', env=env)
+        output = networkctl_json('veth-peer')
         check_json(output)
 
         # PREF64 or NAT64
@@ -6845,11 +6915,11 @@ class NetworkdIPv6PrefixTests(unittest.TestCase, Utilities):
         self.assertNotIn('inet6 2001:db8:0:1:', output)
         self.assertIn('inet6 2001:db8:0:2:', output)
 
-        output = check_output(*resolvectl_cmd, 'dns', 'veth-peer', env=env)
+        output = resolvectl('dns', 'veth-peer')
         print(output)
         self.assertRegex(output, '2001:db8:1:1::2')
 
-        output = check_output(*resolvectl_cmd, 'domain', 'veth-peer', env=env)
+        output = resolvectl('domain', 'veth-peer')
         print(output)
         self.assertIn('example.com', output)
 
diff --git a/test/testsuite-04.units/verbose-success.service b/test/testsuite-04.units/verbose-success.service
new file mode 100644 (file)
index 0000000..67c8bf1
--- /dev/null
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=Verbose successful service
+
+[Service]
+ExecStart=/bin/echo success
index 637fea43875951e8dbcd7c6065dd8a3866699d17..9a3e31b0804e97008d78255cdb1b37f1f7549de9 100644 (file)
@@ -5,6 +5,6 @@ BindsTo=testsuite-23-bound-by.service
 After=testsuite-23-bound-by.service
 
 [Service]
-ExecStart=/bin/sleep infinity
+ExecStart=sleep infinity
 # --kill-who= (no 'm') to check that the short form is accepted
 ExecStopPost=systemctl kill --kill-whom=main -sRTMIN+1 testsuite-23.service
index a2df5a1954b87a697a1700324e1b0f0158ceba1a..c999c2e4fcfe311e7b04a27e3f0db6fa0b408f39 100644 (file)
@@ -3,4 +3,4 @@
 Description=Unit with BoundBy=
 
 [Service]
-ExecStart=/bin/sleep 0.7
+ExecStart=sleep 0.7
index 36f8baa6ba4b589e8e916ef057e23b041f14b93d..597810797d099fd06483f22d6acf28e129f9868e 100644 (file)
@@ -4,4 +4,4 @@ Description=Failing unit
 OnFailure=testsuite-23-uphold.service
 
 [Service]
-ExecStart=/bin/false
+ExecStart=false
index 9919a9fa8216181bf19220614a923b03321368ea..47f0452919ec98b1d5641e03651cd5065d31c231 100644 (file)
@@ -4,4 +4,4 @@ Type=notify
 NotifyAccess=all
 MountAPIVFS=yes
 PrivateTmp=yes
-ExecStart=/bin/bash -c 'touch /tmp/shared-private-file && systemd-notify --ready && sleep infinity'
+ExecStart=bash -c 'touch /tmp/shared-private-file && systemd-notify --ready && sleep infinity'
index 5e823a177810b8432771f7e7182cf1ec2bb127cb..a70f8fca998d7d47de87d622a878d60a504d4597 100644 (file)
@@ -7,4 +7,4 @@ Type=notify
 NotifyAccess=all
 MountAPIVFS=yes
 PrivateTmp=yes
-ExecStart=/bin/bash -c 'touch /tmp/shared-private-file && systemd-notify --ready && sleep infinity'
+ExecStart=bash -c 'touch /tmp/shared-private-file && systemd-notify --ready && sleep infinity'
index bbbfd7c67db398c9356a80a20de3e326a64cd9de..dae533cc369c6fb2edc706e849cb9b6aa9df39f9 100644 (file)
@@ -7,4 +7,4 @@ Type=notify
 NotifyAccess=all
 MountAPIVFS=yes
 PrivateTmp=yes
-ExecStart=/bin/bash -c 'touch /tmp/shared-private-file-x && systemd-notify --ready && sleep infinity'
+ExecStart=bash -c 'touch /tmp/shared-private-file-x && systemd-notify --ready && sleep infinity'
index dac1cea7bdf30a766a4aeef6aa9afd65f505c57f..8e4944a8e7f4620ca7abbb3b3a65812ac104f4ec 100644 (file)
@@ -6,4 +6,4 @@ MountAPIVFS=yes
 PrivateTmp=yes
 ExecStartPre=test -e /tmp/shared-private-file-x
 ExecStartPre=test -e /tmp/hoge
-ExecStart=/bin/bash -c 'touch /tmp/shared-private-file-y && systemd-notify --ready && sleep infinity'
+ExecStart=bash -c 'touch /tmp/shared-private-file-y && systemd-notify --ready && sleep infinity'
index 5a6f5cd873a0f39649b099245e0cbbcc2c740567..46c27ca5d836a12307b5c786841c16c36094df55 100644 (file)
@@ -10,4 +10,4 @@ PrivateTmp=yes
 BindPaths=/run/testsuite-23-marker-fixed:/tmp/testfile-marker-fixed
 InaccessiblePaths=/run/inaccessible
 ExecStartPre=grep -q -F MARKER_FIXED /tmp/testfile-marker-fixed
-ExecStart=/bin/sh -c 'systemd-notify --ready; until grep -q -F MARKER_RUNTIME /tmp/testfile-marker-runtime; do sleep 0.1; done; test ! -f /run/inaccessible/testfile-marker-fixed'
+ExecStart=sh -c 'systemd-notify --ready; until grep -q -F MARKER_RUNTIME /tmp/testfile-marker-runtime; do sleep 0.1; done; test ! -f /run/inaccessible/testfile-marker-fixed'
index 6f93c3bde57070564f19112ef885219c8291584a..699b6080d337700f839b75e81c269f5476a00146 100644 (file)
@@ -3,4 +3,4 @@
 RuntimeMaxSec=5
 Type=notify
 RemainAfterExit=yes
-ExecStart=/bin/sh -c 'systemd-notify --ready; until grep -q -F MARKER_RUNTIME /tmp/testfile-marker-runtime; do sleep 0.1; done; exit 0'
+ExecStart=sh -c 'systemd-notify --ready; until grep -q -F MARKER_RUNTIME /tmp/testfile-marker-runtime; do sleep 0.1; done; exit 0'
index f068daf2b3bf806aeec7a6d3b0c91077a74486d4..03189550a7f0423e878398fe0dd25ec7264790a7 100644 (file)
@@ -6,5 +6,5 @@ After=testsuite-23-prop-stop-two.service
 StopPropagatedFrom=testsuite-23-prop-stop-two.service
 
 [Service]
-ExecStart=/bin/sleep infinity
+ExecStart=sleep infinity
 ExecStopPost=systemctl kill --kill-whom=main -sUSR2 testsuite-23.service
index 2bcd209e167243ab37005832be7fbad6391297b4..b2bb869524acd0b946ee856a52d38d89ccd08b87 100644 (file)
@@ -3,4 +3,4 @@
 Description=Stop Propagation Sender
 
 [Service]
-ExecStart=/bin/sleep 1.5
+ExecStart=sleep 1.5
index 0fc27c41fee0128b607b9c4aafc5309ae7fe6bda..2e0972cbf0dcdad4d86a87ec3a913dfee6871e98 100644 (file)
@@ -5,5 +5,5 @@ Description=Failed Dependency Unit
 [Service]
 Type=oneshot
 RemainAfterExit=yes
-ExecStart=/bin/sh -c "if [ -f /tmp/testsuite-23-retry-fail ]; then exit 0; else exit 1; fi"
+ExecStart=sh -c "if [ -f /tmp/testsuite-23-retry-fail ]; then exit 0; else exit 1; fi"
 Restart=no
index 0426d76a54649eb2f745d94ae95b36c8c1f4005d..3c20e4338c55e0fbf1a9258078a58257f62c95ea 100644 (file)
@@ -7,4 +7,4 @@ After=testsuite-23-retry-fail.service
 [Service]
 Type=oneshot
 RemainAfterExit=yes
-ExecStart=/bin/echo ok
+ExecStart=echo ok
index f35e8424de8ece0c04c6e33f233d7f0286ca240b..7f15f068b41b3253af7ecb30ff276cec60c8072d 100644 (file)
@@ -4,4 +4,4 @@ Description=Upholding Unit
 Upholds=testsuite-23-retry-upheld.service
 
 [Service]
-ExecStart=/bin/sleep infinity
+ExecStart=sleep infinity
index 410d4f83c3468e099fd2be347cb1d677de045595..2ace6fa370c7229dbc72f4104c2301a633101c96 100644 (file)
@@ -4,4 +4,4 @@ Description=Succeeding unit
 OnSuccess=testsuite-23-fail.service
 
 [Service]
-ExecStart=/bin/true
+ExecStart=true
index a4562077db49da023ffe2577794be156ae9cf731..bcfacd21dd4c5ebe3435c50d6cf66eee34e26464 100644 (file)
@@ -3,7 +3,7 @@
 Description=Unit that sets UpheldBy= through [Install]
 
 [Service]
-ExecStart=/bin/sleep infinity
+ExecStart=sleep infinity
 
 [Install]
 UpheldBy=testsuite-23-retry-uphold.service
index 3549d6a8f50691a206696a55ec03889ed3784278..67f0ac885992bded83fe660f2661320fcb9f5ed0 100644 (file)
@@ -4,4 +4,4 @@ Description=Upholding Unit
 Upholds=testsuite-23-short-lived.service
 
 [Service]
-ExecStart=/bin/sleep infinity
+ExecStart=sleep infinity
index 3a7c9e11edf717ba6ba2491b461668e9700a605f..13927e79b4761b1faed03bf0b38208d7cc8c95e1 100644 (file)
@@ -6,4 +6,4 @@ After=a.service
 Before=a.service
 
 [Service]
-ExecStart=/bin/true
+ExecStart=true
index ec5d0594c3356d6b989e0fa2db9d3b7a472dece3..0cc13201607df442c08c4426721dd49ff5291811 100644 (file)
@@ -5,4 +5,4 @@ Requires=b.service
 Before=b.service
 
 [Service]
-ExecStart=/bin/true
+ExecStart=true
index 4503cf312898c495f0e9584b555f5b2d5d6863aa..e875714b8738444e0780729a1c5eaf556c6f07c7 100644 (file)
@@ -4,4 +4,4 @@ Description=B
 Wants=f.service
 
 [Service]
-ExecStart=/bin/true
+ExecStart=true
index a1ce28c82a56d9b23af963fb1aa693aaa88adb1d..3fc3717a5033f924463f88fccd4e79e92f375c2c 100644 (file)
@@ -4,4 +4,4 @@ Description=C
 Requires=a.service
 
 [Service]
-ExecStart=/bin/true
+ExecStart=true
index 82023258e36ee48aa07aa810b242dc8f305280ea..0438607bf7d29d428df9fbacebcc1b3693caa134 100644 (file)
@@ -6,4 +6,4 @@ Before=a.service
 Requires=a.service
 
 [Service]
-ExecStart=/bin/true
+ExecStart=true
index 385fbed4921390bf4b0db6332e38f7032750b171..0ee4f24a4e96b3dc4c20e98c33c60facc73d5708 100644 (file)
@@ -5,5 +5,5 @@ Description=Daughter Service
 [Service]
 Slice=parent.slice
 Type=oneshot
-ExecStart=/bin/true
+ExecStart=true
 CPUAccounting=true
index 720c1da00a44f2dee6f240a6fc5c9926df891ae5..c17698999ab09f360113432b642a991d56d612a2 100644 (file)
@@ -5,4 +5,4 @@ Description=DML discard empty service
 [Service]
 Slice=dml-discard.slice
 Type=oneshot
-ExecStart=/bin/true
+ExecStart=true
index 93246ac0dbbb65e3c34ec2433ceaa3b406d40b85..0fba2acdafb4750c69db9a2b6698ae3ccc9fb934 100644 (file)
@@ -5,5 +5,5 @@ Description=DML discard set ml service
 [Service]
 Slice=dml-discard.slice
 Type=oneshot
-ExecStart=/bin/true
+ExecStart=true
 MemoryLow=15
index ac96de01cbbbf75ef549c712d3a06149d2d95b5f..5f0c143d66b039ed1561628f85caab2e52511616 100644 (file)
@@ -5,4 +5,4 @@ Description=DML override empty service
 [Service]
 Slice=dml-override.slice
 Type=oneshot
-ExecStart=/bin/true
+ExecStart=true
index 1e1ba3416e19af162d5f62334124fc2abd42bc83..896622689fc073bb9418524cacb6f05284fac9f0 100644 (file)
@@ -5,4 +5,4 @@ Description=DML passthrough empty service
 [Service]
 Slice=dml-passthrough.slice
 Type=oneshot
-ExecStart=/bin/true
+ExecStart=true
index 9a1531125baead2f3d715164d6bfa52e4aa34bc5..ec82174e6958608885258abdc34518fba7ca7c67 100644 (file)
@@ -5,5 +5,5 @@ Description=DML passthrough set DML service
 [Service]
 Slice=dml-passthrough.slice
 Type=oneshot
-ExecStart=/bin/true
+ExecStart=true
 DefaultMemoryLow=15
index 65083bc24dd3907d0fc97089955c3d710bb4eff7..63ec3058cf01b12028e263451c9f04720dfd41d5 100644 (file)
@@ -5,5 +5,5 @@ Description=DML passthrough set ML service
 [Service]
 Slice=dml-passthrough.slice
 Type=oneshot
-ExecStart=/bin/true
+ExecStart=true
 MemoryLow=0
index 5bbcde26dd9ace354d736f47447aa4c240d36a1b..c2acb92f6ea6bef15926f05a8dc215d573fa2ad9 100644 (file)
@@ -6,4 +6,4 @@ Before=a.service
 Wants=a.service
 
 [Service]
-ExecStart=/bin/true
+ExecStart=true
index ca20053ee619767f4606692caf443e112d757363..a66043e96d894aec8f587ffcf4007a3772716569 100644 (file)
@@ -3,4 +3,4 @@
 Description=F
 
 [Service]
-ExecStart=/bin/true
+ExecStart=true
index 5fd794dfd1a98eb8caf3ba1e1de3e3b0625efd32..bfb3d7898c64547d4d0951428d32ad822a52e2f3 100644 (file)
@@ -4,4 +4,4 @@ Description=G
 Conflicts=e.service
 
 [Service]
-ExecStart=/bin/true
+ExecStart=true
index 4fe77b4a7db6ef75e55664394fbb0d6940c8118a..bdccfe1846040e124d4d82f11a7e7d78b3ab9094 100644 (file)
@@ -5,4 +5,4 @@ Description=Grandchild Service
 [Service]
 Slice=parent-deep.slice
 Type=oneshot
-ExecStart=/bin/true
+ExecStart=true
index 5361d42db7eab096fbcd25d767742c142ae2a5c8..1c4dbb4c90403598bf92832f5ce8e25391d4bcec 100644 (file)
@@ -4,4 +4,4 @@ Description=H
 Wants=g.service
 
 [Service]
-ExecStart=/bin/true
+ExecStart=true
index 2b5e821638e632ccb510948a8720e17eaba3c86b..783ac6539135440daacbffe1e360483c42152533 100644 (file)
@@ -6,4 +6,4 @@ Wants=b.service
 After=b.service
 
 [Service]
-ExecStart=/bin/true
+ExecStart=true
index 7fc0e4241ad6078781294148dcfc35f2c659f530..4c1a4a3cfd59f004fa8e9b359ab5acf69ccdf9da 100644 (file)
@@ -1,3 +1,3 @@
 # SPDX-License-Identifier: LGPL-2.1-or-later
 [Service]
-ExecStart=/bin/true
+ExecStart=true
index 7fc0e4241ad6078781294148dcfc35f2c659f530..4c1a4a3cfd59f004fa8e9b359ab5acf69ccdf9da 100644 (file)
@@ -1,3 +1,3 @@
 # SPDX-License-Identifier: LGPL-2.1-or-later
 [Service]
-ExecStart=/bin/true
+ExecStart=true
index b2af20ab41828730ce78d148acb88f82dd3aae75..f7a2f67a1190af3462134f30cf5b72983058aa59 100644 (file)
@@ -1,6 +1,6 @@
 # SPDX-License-Identifier: LGPL-2.1-or-later
 [Service]
-ExecStart=/bin/true
+ExecStart=true
 
 [Unit]
 Conflicts=loopy4.service
index b2af20ab41828730ce78d148acb88f82dd3aae75..f7a2f67a1190af3462134f30cf5b72983058aa59 100644 (file)
@@ -1,6 +1,6 @@
 # SPDX-License-Identifier: LGPL-2.1-or-later
 [Service]
-ExecStart=/bin/true
+ExecStart=true
 
 [Unit]
 Conflicts=loopy4.service
index 14ce5ad326cf6fea0e351f9cf0f005cbefecc82c..2e5c8ced0201d5c7511ada8c2bddc25e37fb1d95 100644 (file)
@@ -5,6 +5,6 @@ Description=Nomem Leaf Service
 [Service]
 Slice=nomem.slice
 Type=oneshot
-ExecStart=/bin/true
+ExecStart=true
 IOWeight=200
 MemoryAccounting=true
index be8f1c27c395cf42beded19bab4d8b2641a26a0c..d9fd6cc47a718343d76d37c1c0a1708bd8d6533d 100644 (file)
@@ -3,5 +3,5 @@
 Description=Bad sched priority for Idle
 
 [Service]
-ExecStart=/bin/true
+ExecStart=true
 CPUSchedulingPriority=1
index 5a1d809339b18c7faaac2576bc58168897061414..a7238ed76b14a0737d7d6b6643115f49e487e7c0 100644 (file)
@@ -3,5 +3,5 @@
 Description=Sched idle with prio 0
 
 [Service]
-ExecStart=/bin/true
+ExecStart=true
 CPUSchedulingPriority=0
index b51b868c2a98695d434cf69f6cb8b572ffc32e87..3f3bf39707a2c450173c14b9f3109d97866b3996 100644 (file)
@@ -3,7 +3,7 @@
 Description=Bad sched priority for RR
 
 [Service]
-ExecStart=/bin/true
+ExecStart=true
 CPUSchedulingPriority=-1
 CPUSchedulingPriority=100
 CPUSchedulingPolicy=rr
index 6ae1febc8fd6f33577df051acdf7b472dbbc499a..3a72bd6476d697076f981d76c011145610f979a4 100644 (file)
@@ -3,7 +3,7 @@
 Description=Change prio
 
 [Service]
-ExecStart=/bin/true
+ExecStart=true
 CPUSchedulingPriority=1
 CPUSchedulingPriority=2
 CPUSchedulingPriority=99
index 00b98220971e36f2da7eee1c7318f290a254abf7..5c71f30239e8394d8622300c41fef9c98bec9a69 100644 (file)
@@ -3,5 +3,5 @@
 Description=Default prio for RR
 
 [Service]
-ExecStart=/bin/true
+ExecStart=true
 CPUSchedulingPolicy=rr
index 2059118f01e1dd9d0fffd6cc64e88d758bbfbced..0242509f3bd3d2ec0eca865435babc419d6fe2b5 100644 (file)
@@ -5,5 +5,5 @@ Description=Son Service
 [Service]
 Slice=parent.slice
 Type=oneshot
-ExecStart=/bin/true
+ExecStart=true
 CPUShares=100
index e3567c2d7c598777542038dc12407ce55c7399d6..115b941f926826781a5f70ca8500014bd7194b34 100755 (executable)
@@ -80,13 +80,13 @@ cat <<EOF >/run/systemd/system/wait2.service
 [Unit]
 Description=Wait for 2 seconds
 [Service]
-ExecStart=/bin/sh -ec 'sleep 2'
+ExecStart=sh -ec 'sleep 2'
 EOF
 cat <<EOF >/run/systemd/system/wait5fail.service
 [Unit]
 Description=Wait for 5 seconds and fail
 [Service]
-ExecStart=/bin/sh -ec 'sleep 5; false'
+ExecStart=sh -ec 'sleep 5; false'
 EOF
 
 # wait2 succeeds
index 4d9e48717acd6847ecc212ce9fb9961b7245abad..2c0c50f5b83efbc7d33e1e8ad5506cb5804afb33 100755 (executable)
@@ -107,6 +107,16 @@ systemctl start silent-success
 journalctl --sync
 [[ -z "$(journalctl -b -q -u silent-success.service)" ]]
 
+# Test syslog identifiers exclusion
+systemctl start verbose-success.service
+timeout 30 bash -xec 'while systemctl -q is-active verbose-success.service; do sleep 1; done'
+journalctl --sync
+[[ -n "$(journalctl -b -q -u verbose-success.service -t systemd)" ]]
+[[ -n "$(journalctl -b -q -u verbose-success.service -t echo)" ]]
+[[ -n "$(journalctl -b -q -u verbose-success.service -T systemd)" ]]
+[[ -n "$(journalctl -b -q -u verbose-success.service -T echo)" ]]
+[[ -z "$(journalctl -b -q -u verbose-success.service -T echo -T '(echo)' -T systemd -T '(systemd)' -T systemd-executor)" ]]
+
 # Exercise the matching machinery
 SYSTEMD_LOG_LEVEL=debug journalctl -b -n 1 /dev/null /dev/zero /dev/null /dev/null /dev/null
 journalctl -b -n 1 /bin/true /bin/false
diff --git a/test/units/testsuite-05.effective-limit.sh b/test/units/testsuite-05.effective-limit.sh
new file mode 100755 (executable)
index 0000000..3ff8e83
--- /dev/null
@@ -0,0 +1,68 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+pre=test05
+cat >/run/systemd/system/"$pre"alpha.slice <<EOF
+[Slice]
+MemoryMax=40M
+MemoryHigh=40M
+TasksMax=400
+EOF
+
+cat >/run/systemd/system/"$pre"alpha-beta.slice <<EOF
+[Slice]
+MemoryMax=10M
+MemoryHigh=10M
+TasksMax=100
+EOF
+
+cat >/run/systemd/system/"$pre"alpha-beta-gamma.slice <<EOF
+[Slice]
+MemoryMax=20M
+MemoryHigh=20M
+TasksMax=200
+EOF
+
+systemctl daemon-reload
+
+srv=probe.service
+slc0="$pre"alpha.slice
+slc="$pre"alpha-beta-gamma.slice
+
+systemd-run --unit "$srv" --slice "$slc"  \
+    -p MemoryMax=5M \
+    -p MemoryHigh=5M \
+    -p TasksMax=50 \
+    sleep inf
+
+# Compare with inequality because test can run in a constrained container
+assert_le "$(systemctl show -P EffectiveMemoryMax "$srv")" "5242880"
+assert_le "$(systemctl show -P EffectiveMemoryHigh "$srv")" "5242880"
+assert_le "$(systemctl show -P EffectiveTasksMax "$srv")" "50"
+
+systemctl stop "$srv"
+
+systemd-run --unit "$srv" --slice "$slc"  \
+    sleep inf
+
+assert_le "$(systemctl show -P EffectiveMemoryMax "$srv")" "10485760"
+assert_le "$(systemctl show -P EffectiveMemoryHigh "$srv")" "10485760"
+assert_le "$(systemctl show -P EffectiveTasksMax "$srv")" "100"
+
+systemctl set-property "$slc0" \
+    MemoryMax=5M \
+    MemoryHigh=5M \
+    TasksMax=50
+
+assert_le "$(systemctl show -P EffectiveMemoryMax "$srv")" "5242880"
+assert_le "$(systemctl show -P EffectiveMemoryHigh "$srv")" "5242880"
+assert_le "$(systemctl show -P EffectiveTasksMax "$srv")" "50"
+
+systemctl stop "$srv"
+
+rm -f /run/systemd/system/"$pre"* || :
diff --git a/test/units/testsuite-05.rlimit.sh b/test/units/testsuite-05.rlimit.sh
new file mode 100755 (executable)
index 0000000..bbf3adb
--- /dev/null
@@ -0,0 +1,25 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+P=/run/systemd/system.conf.d
+mkdir $P
+
+cat >$P/rlimits.conf <<EOF
+[Manager]
+DefaultLimitNOFILE=10000:16384
+EOF
+
+systemctl daemon-reload
+
+[[ "$(systemctl show -P DefaultLimitNOFILESoft)" = "10000" ]]
+[[ "$(systemctl show -P DefaultLimitNOFILE)" = "16384" ]]
+
+[[ "$(systemctl show -P LimitNOFILESoft testsuite-05.service)" = "10000" ]]
+[[ "$(systemctl show -P LimitNOFILE testsuite-05.service)" = "16384" ]]
+
+# shellcheck disable=SC2016
+systemd-run --wait -t bash -c '[[ "$(ulimit -n -S)" = "10000" ]]'
+# shellcheck disable=SC2016
+systemd-run --wait -t bash -c '[[ "$(ulimit -n -H)" = "16384" ]]'
index ab72d8fe27ad35f43c2ae9e35b9ddfc69dd300dc..cf32accb8c555b460d6d0c6f3fa9984d21006c8d 100644 (file)
@@ -1,6 +1,6 @@
 # SPDX-License-Identifier: LGPL-2.1-or-later
 [Unit]
-Description=TEST-05-RLIMITS
+Description=TEST-05-LIMITS
 
 [Service]
 ExecStartPre=rm -f /failed /testok
index 870845d14bf42cd3105f16451826010161dee803..9c2a033aa98d17ce1517bf42cf30c31c187ab0ea 100755 (executable)
@@ -3,25 +3,9 @@
 set -eux
 set -o pipefail
 
-P=/run/systemd/system.conf.d
-mkdir $P
+# shellcheck source=test/units/test-control.sh
+. "$(dirname "$0")"/test-control.sh
 
-cat >$P/rlimits.conf <<EOF
-[Manager]
-DefaultLimitNOFILE=10000:16384
-EOF
-
-systemctl daemon-reload
-
-[[ "$(systemctl show -P DefaultLimitNOFILESoft)" = "10000" ]]
-[[ "$(systemctl show -P DefaultLimitNOFILE)" = "16384" ]]
-
-[[ "$(systemctl show -P LimitNOFILESoft testsuite-05.service)" = "10000" ]]
-[[ "$(systemctl show -P LimitNOFILE testsuite-05.service)" = "16384" ]]
-
-# shellcheck disable=SC2016
-systemd-run --wait -t bash -c '[[ "$(ulimit -n -S)" = "10000" ]]'
-# shellcheck disable=SC2016
-systemd-run --wait -t bash -c '[[ "$(ulimit -n -H)" = "16384" ]]'
+run_subtests
 
 touch /testok
index c84974f1de4777f042790f12c4a2feb35b702816..dd63163008ee77b64d8445b5ac5c80a377eeaba1 100755 (executable)
@@ -93,6 +93,13 @@ systemd-run --wait --pipe -p BindPaths="/etc /home:/mnt:norbind -/foo/bar/baz:/u
     bash -xec "mountpoint /etc; test -d /etc/systemd; mountpoint /mnt; ! mountpoint /usr"
 systemd-run --wait --pipe -p BindReadOnlyPaths="/etc /home:/mnt:norbind -/foo/bar/baz:/usr:rbind" \
     bash -xec "test ! -w /etc; test ! -w /mnt; ! mountpoint /usr"
+# Make sure we properly serialize/deserialize paths with spaces
+# See: https://github.com/systemd/systemd/issues/30747
+touch "/tmp/test file with spaces"
+systemd-run --wait --pipe -p TemporaryFileSystem="/tmp" -p BindPaths="/etc /home:/mnt:norbind /tmp/test\ file\ with\ spaces" \
+    bash -xec "mountpoint /etc; test -d /etc/systemd; mountpoint /mnt; stat '/tmp/test file with spaces'"
+systemd-run --wait --pipe -p TemporaryFileSystem="/tmp" -p BindPaths="/etc /home:/mnt:norbind /tmp/test\ file\ with\ spaces:/tmp/destination\ wi\:th\ spaces" \
+    bash -xec "mountpoint /etc; test -d /etc/systemd; mountpoint /mnt; stat '/tmp/destination wi:th spaces'"
 
 # Check if we correctly serialize, deserialize, and set directives that
 # have more complex internal handling
@@ -206,18 +213,20 @@ fi
 
 # {Cache,Configuration,Logs,Runtime,State}Directory=
 ARGUMENTS=(
-    -p CacheDirectory="foo/bar/baz"
+    -p CacheDirectory="foo/bar/baz also\ with\ spaces"
     -p CacheDirectory="foo"
     -p CacheDirectory="context"
     -p CacheDirectoryMode="0123"
     -p CacheDirectoryMode="0666"
-    -p ConfigurationDirectory="context/foo also_context/bar context/nested/baz"
+    -p ConfigurationDirectory="context/foo also_context/bar context/nested/baz context/semi\:colon"
     -p ConfigurationDirectoryMode="0400"
     -p LogsDirectory="context/foo"
     -p LogsDirectory=""
     -p LogsDirectory="context/a/very/nested/logs/dir"
-    -p RuntimeDirectory="context"
-    -p RuntimeDirectory="also_context"
+    -p RuntimeDirectory="context/with\ spaces"
+    # Note: {Runtime,State,Cache,Logs}Directory= directives support the directory:symlink syntax, which
+    #       requires an additional level of escaping for the colon character
+    -p RuntimeDirectory="also_context:a\ symlink\ with\ \\\:\ col\\\:ons\ and\ \ spaces"
     -p RuntimeDirectoryPreserve=yes
     -p StateDirectory="context"
     -p StateDirectory="./././././././context context context"
@@ -226,21 +235,22 @@ ARGUMENTS=(
 
 rm -rf /run/context
 systemd-run --wait --pipe "${ARGUMENTS[@]}" \
-    bash -xec '[[ $CACHE_DIRECTORY == /var/cache/context:/var/cache/foo:/var/cache/foo/bar/baz ]];
-               [[ $(stat -c "%a" ${CACHE_DIRECTORY##*:}) == 666 ]]'
+    bash -xec '[[ $CACHE_DIRECTORY == "/var/cache/also with spaces:/var/cache/context:/var/cache/foo:/var/cache/foo/bar/baz" ]];
+               [[ $(stat -c "%a" "${CACHE_DIRECTORY##*:}") == 666 ]]'
 systemd-run --wait --pipe "${ARGUMENTS[@]}" \
-    bash -xec '[[ $CONFIGURATION_DIRECTORY == /etc/also_context/bar:/etc/context/foo:/etc/context/nested/baz ]];
-               [[ $(stat -c "%a" ${CONFIGURATION_DIRECTORY##*:}) == 400 ]]'
+    bash -xec '[[ $CONFIGURATION_DIRECTORY == /etc/also_context/bar:/etc/context/foo:/etc/context/nested/baz:/etc/context/semi:colon ]];
+               [[ $(stat -c "%a" "${CONFIGURATION_DIRECTORY%%:*}") == 400 ]]'
 systemd-run --wait --pipe "${ARGUMENTS[@]}" \
     bash -xec '[[ $LOGS_DIRECTORY == /var/log/context/a/very/nested/logs/dir:/var/log/context/foo ]];
-               [[ $(stat -c "%a" ${LOGS_DIRECTORY##*:}) == 755 ]]'
+               [[ $(stat -c "%a" "${LOGS_DIRECTORY##*:}") == 755 ]]'
 systemd-run --wait --pipe "${ARGUMENTS[@]}" \
-    bash -xec '[[ $RUNTIME_DIRECTORY == /run/also_context:/run/context ]];
-               [[ $(stat -c "%a" ${RUNTIME_DIRECTORY##*:}) == 755 ]];
-               [[ $(stat -c "%a" ${RUNTIME_DIRECTORY%%:*}) == 755 ]]'
+    bash -xec '[[ $RUNTIME_DIRECTORY == "/run/also_context:/run/context/with spaces" ]];
+               [[ $(stat -c "%a" "${RUNTIME_DIRECTORY##*:}") == 755 ]];
+               [[ $(stat -c "%a" "${RUNTIME_DIRECTORY%%:*}") == 755 ]]'
 systemd-run --wait --pipe "${ARGUMENTS[@]}" \
     bash -xec '[[ $STATE_DIRECTORY == /var/lib/context ]]; [[ $(stat -c "%a" $STATE_DIRECTORY) == 0 ]]'
-test -d /run/context
+test -d "/run/context/with spaces"
+test -s "/run/a symlink with : col:ons and  spaces"
 rm -rf /var/{cache,lib,log}/context /etc/{also_,}context
 
 # Limit*=
index a0aa5809fcc7e68997027968a1b7b8671a974860..04f17c88e9dbcfc3552aaec1d4694fdfdc621592 100755 (executable)
@@ -92,7 +92,7 @@ testcase_added_before() {
     # Add one new ExecStart= before the existing ones.
     #
     # Since, after reload, we should continue running from the "sleep 3" statement, the newly added "echo
-    # bar" one will have no efect and we should end up with the same output as in the previous case.
+    # bar" one will have no effect and we should end up with the same output as in the previous case.
     cat >"$unit_path" <<EOF
 [Service]
 Type=oneshot
@@ -167,7 +167,7 @@ testcase_removal() {
 
     # Remove the currently executed ExecStart= line.
     #
-    # In this case we completely drop the currently excuted "sleep 3" statement, so after reload systemd
+    # In this case we completely drop the currently executed "sleep 3" statement, so after reload systemd
     # should complain that the currently executed command vanished and simply finish executing the unit,
     # resulting in an empty log.
     cat >"$unit_path" <<EOF
index 6eb802c93fb40e4892f71c456f737e7a4bbb4932..dcfa9b17b0d62c2be70f3ce58cc980d46f128583 100755 (executable)
@@ -24,7 +24,7 @@ Type=oneshot
 ExecStartPre=sh -c 'test "$TRIGGER_UNIT" = my.timer'
 ExecStartPre=sh -c 'test -n "$TRIGGER_TIMER_REALTIME_USEC"'
 ExecStartPre=sh -c 'test -n "$TRIGGER_TIMER_MONOTONIC_USEC"'
-ExecStart=/bin/echo Timer runs me
+ExecStart=echo Timer runs me
 EOF
 
 cat >/run/systemd/system/my.timer <<EOF
index db17c25f9049f1e487dcbc1828e56f582937c334..374df542b8e7f7dae583dac23b7aae21360753f4 100755 (executable)
@@ -24,7 +24,7 @@ cat >/run/systemd/system/issue-3171@.service <<EOF
 Description=Test service
 [Service]
 StandardInput=socket
-ExecStart=/bin/sh -x -c cat
+ExecStart=sh -x -c cat
 EOF
 
 systemctl start issue-3171.socket
index 480d7ee8dffd6d0d9b00e189442d6004fc6c193f..ca988b2321dd78fc02bd6391730d8c0034e51ac4 100755 (executable)
@@ -5,12 +5,12 @@ set -o pipefail
 
 systemd-analyze log-level debug
 
-cat > /run/systemd/system/floodme@.service <<EOF
+cat >/run/systemd/system/floodme@.service <<EOF
 [Service]
-ExecStart=/bin/true
+ExecStart=true
 EOF
 
-cat > /run/systemd/system/floodme.socket <<EOF
+cat >/run/systemd/system/floodme.socket <<EOF
 [Socket]
 ListenStream=/tmp/floodme
 PollLimitIntervalSec=10s
@@ -24,7 +24,7 @@ systemctl start floodme.socket
 START=$(date +%s%N)
 
 # Trigger this 100 times in a flood
-for (( i=0 ; i < 100; i++ )) ; do
+for _ in {1..100}; do
     logger -u /tmp/floodme foo &
 done
 
index 9af706fae165664a926b7d0227be320f946a68c6..d19d85074f51e1bae00825fb9d241b5f72df4b1f 100755 (executable)
@@ -1,7 +1,8 @@
 #!/usr/bin/env bash
 # SPDX-License-Identifier: LGPL-2.1-or-later
-set -ex
+set -eux
 
+TMPDIR=
 TEST_RULE="/run/udev/rules.d/49-test.rules"
 KILL_PID=
 
@@ -10,8 +11,15 @@ setup() {
     [[ -e /etc/udev/udev.conf ]] && cp -f /etc/udev/udev.conf /etc/udev/udev.conf.bak
 
     cat >"${TEST_RULE}" <<EOF
-ACTION=="add", SUBSYSTEM=="mem", KERNEL=="null", OPTIONS="log_level=debug"
-ACTION=="add", SUBSYSTEM=="mem", KERNEL=="null", PROGRAM=="/bin/sleep 60"
+ACTION!="add", GOTO="test_end"
+SUBSYSTEM!="mem", GOTO="test_end"
+KERNEL!="null", GOTO="test_end"
+
+OPTIONS="log_level=debug"
+PROGRAM=="/bin/touch /tmp/test-udev-marker"
+PROGRAM!="/bin/sleep 60", ENV{PROGRAM_RESULT}="KILLED"
+
+LABEL="test_end"
 EOF
     cat >/etc/udev/udev.conf <<EOF
 event_timeout=10
@@ -35,19 +43,51 @@ teardown() {
     systemctl restart systemd-udevd.service
 }
 
-run_test() {
-    local since
+run_test_timeout() {
+    TMPDIR=$(mktemp -d -p /tmp udev-tests.XXXXXX)
+    udevadm monitor --udev --property --subsystem-match=mem >"$TMPDIR"/monitor.txt &
+    KILL_PID="$!"
+
+    SYSTEMD_LOG_LEVEL=debug udevadm trigger --verbose --action add /dev/null
+
+    for _ in {1..40}; do
+        if grep -q 'PROGRAM_RESULT=KILLED' "$TMPDIR"/monitor.txt; then
+            sleep .5
+            kill "$KILL_PID"
+            KILL_PID=
+
+            cat "$TMPDIR"/monitor.txt
+            (! grep -q 'UDEV_WORKER_FAILED=1' "$TMPDIR"/monitor.txt)
+            (! grep -q 'UDEV_WORKER_SIGNAL=6' "$TMPDIR"/monitor.txt)
+            (! grep -q 'UDEV_WORKER_SIGNAL_NAME=ABRT' "$TMPDIR"/monitor.txt)
+            grep -q 'PROGRAM_RESULT=KILLED' "$TMPDIR"/monitor.txt
+            rm -rf "$TMPDIR"
+            return 0
+        fi
+        sleep .5
+    done
+
+    return 1
+}
 
-    since="$(date '+%F %T')"
+run_test_killed() {
+    local killed=
 
     TMPDIR=$(mktemp -d -p /tmp udev-tests.XXXXXX)
     udevadm monitor --udev --property --subsystem-match=mem >"$TMPDIR"/monitor.txt &
     KILL_PID="$!"
 
+    rm -f /tmp/test-udev-marker
     SYSTEMD_LOG_LEVEL=debug udevadm trigger --verbose --action add /dev/null
 
     for _ in {1..40}; do
-        if coredumpctl --since "$since" --no-legend --no-pager | grep /bin/udevadm ; then
+        if [[ -z "$killed" ]]; then
+            if [[ -e /tmp/test-udev-marker ]]; then
+                killall --signal ABRT --regexp udev-worker
+                killed=1
+            fi
+        elif grep -q 'UDEV_WORKER_FAILED=1' "$TMPDIR"/monitor.txt; then
+            sleep .5
             kill "$KILL_PID"
             KILL_PID=
 
@@ -55,6 +95,8 @@ run_test() {
             grep -q 'UDEV_WORKER_FAILED=1' "$TMPDIR"/monitor.txt
             grep -q 'UDEV_WORKER_SIGNAL=6' "$TMPDIR"/monitor.txt
             grep -q 'UDEV_WORKER_SIGNAL_NAME=ABRT' "$TMPDIR"/monitor.txt
+            (! grep -q 'PROGRAM_RESULT=KILLED' "$TMPDIR"/monitor.txt)
+            rm -rf "$TMPDIR"
             return 0
         fi
         sleep .5
@@ -66,6 +108,7 @@ run_test() {
 trap teardown EXIT
 
 setup
-run_test
+run_test_timeout
+run_test_killed
 
 exit 0
diff --git a/test/units/testsuite-17.link-property.sh b/test/units/testsuite-17.link-property.sh
new file mode 100755 (executable)
index 0000000..a43ad22
--- /dev/null
@@ -0,0 +1,201 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -ex
+set -o pipefail
+
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+udevadm control --log-level=debug
+
+mkdir -p /run/systemd/network/
+cat >/run/systemd/network/10-test.link <<EOF
+[Match]
+Kind=dummy
+MACAddress=00:50:56:c0:00:19
+
+[Link]
+Name=test1
+EOF
+
+mkdir /run/systemd/network/10-test.link.d
+cat >/run/systemd/network/10-test.link.d/10-override.conf <<EOF
+[Link]
+Property=HOGE=foo BAR=baz SHOULD_BE_UNSET=unset
+UnsetProperty=SHOULD_BE_UNSET
+EOF
+
+udevadm control --reload
+
+ip link add address 00:50:56:c0:00:19 type dummy
+udevadm wait --settle --timeout=30 /sys/class/net/test1
+output=$(udevadm info --query property /sys/class/net/test1)
+assert_in "HOGE=foo" "$output"
+assert_in "BAR=baz" "$output"
+assert_not_in "SHOULD_BE_UNSET=" "$output"
+assert_in "ID_NET_LINK_FILE=/run/systemd/network/10-test.link" "$output"
+assert_in "ID_NET_LINK_FILE_DROPINS=/run/systemd/network/10-test.link.d/10-override.conf" "$output"
+assert_in "ID_NET_NAME=test1" "$output"
+
+cat >/run/systemd/network/10-test.link.d/11-override.conf <<EOF
+[Link]
+Property=
+Property=HOGE2=foo2 BAR2=baz2 SHOULD_BE_UNSET=unset
+ImportProperty=HOGE
+EOF
+
+udevadm control --reload
+
+udevadm trigger --settle --action add /sys/class/net/test1
+output=$(udevadm info --query property /sys/class/net/test1)
+assert_in "HOGE=foo" "$output"
+assert_in "HOGE2=foo2" "$output"
+assert_not_in "BAR=" "$output"
+assert_in "BAR2=baz2" "$output"
+assert_not_in "SHOULD_BE_UNSET=" "$output"
+assert_in "ID_NET_LINK_FILE=/run/systemd/network/10-test.link" "$output"
+assert_in "ID_NET_LINK_FILE_DROPINS=/run/systemd/network/10-test.link.d/10-override.conf:/run/systemd/network/10-test.link.d/11-override.conf" "$output"
+assert_in "ID_NET_NAME=test1" "$output"
+
+# On change event, .link file will not be applied.
+udevadm trigger --settle --action change /sys/class/net/test1
+output=$(udevadm info --query property /sys/class/net/test1)
+assert_not_in "HOGE=" "$output"
+assert_not_in "HOGE2=" "$output"
+assert_not_in "BAR=" "$output"
+assert_not_in "BAR2=" "$output"
+assert_not_in "SHOULD_BE_UNSET=" "$output"
+assert_in "ID_NET_LINK_FILE=/run/systemd/network/10-test.link" "$output"
+assert_in "ID_NET_LINK_FILE_DROPINS=/run/systemd/network/10-test.link.d/10-override.conf:/run/systemd/network/10-test.link.d/11-override.conf" "$output"
+assert_in "ID_NET_NAME=test1" "$output"
+
+### testing with udevadm test-builtin
+output=$(udevadm test-builtin --action add net_setup_link /sys/class/net/test1)
+assert_not_in "HOGE=" "$output"
+assert_in "HOGE2=foo2" "$output"
+assert_not_in "BAR=" "$output"
+assert_in "BAR2=baz2" "$output"
+assert_in "SHOULD_BE_UNSET=" "$output"  # this is expected, as an empty assignment is also logged.
+assert_in "ID_NET_LINK_FILE=/run/systemd/network/10-test.link" "$output"
+assert_in "ID_NET_LINK_FILE_DROPINS=/run/systemd/network/10-test.link.d/10-override.conf:/run/systemd/network/10-test.link.d/11-override.conf" "$output"
+assert_in "ID_NET_NAME=test1" "$output"
+
+# check that test-builtin command does not update udev database.
+output=$(udevadm info --query property /sys/class/net/test1)
+assert_not_in "HOGE=" "$output"
+assert_not_in "HOGE2=" "$output"
+assert_not_in "BAR=" "$output"
+assert_not_in "BAR2=" "$output"
+assert_not_in "SHOULD_BE_UNSET=" "$output"
+assert_in "ID_NET_LINK_FILE=/run/systemd/network/10-test.link" "$output"
+assert_in "ID_NET_LINK_FILE_DROPINS=/run/systemd/network/10-test.link.d/10-override.conf:/run/systemd/network/10-test.link.d/11-override.conf" "$output"
+assert_in "ID_NET_NAME=test1" "$output"
+
+output=$(udevadm test-builtin --action change net_setup_link /sys/class/net/test1)
+assert_not_in "HOGE=" "$output"
+assert_not_in "HOGE2=" "$output"
+assert_not_in "BAR=" "$output"
+assert_not_in "BAR2=" "$output"
+assert_not_in "SHOULD_BE_UNSET=" "$output"
+assert_in "ID_NET_LINK_FILE=/run/systemd/network/10-test.link" "$output"
+assert_in "ID_NET_LINK_FILE_DROPINS=/run/systemd/network/10-test.link.d/10-override.conf:/run/systemd/network/10-test.link.d/11-override.conf" "$output"
+assert_in "ID_NET_NAME=test1" "$output"
+
+output=$(udevadm info --query property /sys/class/net/test1)
+assert_not_in "HOGE=" "$output"
+assert_not_in "HOGE2=" "$output"
+assert_not_in "BAR=" "$output"
+assert_not_in "BAR2=" "$output"
+assert_not_in "SHOULD_BE_UNSET=" "$output"
+assert_in "ID_NET_LINK_FILE=/run/systemd/network/10-test.link" "$output"
+assert_in "ID_NET_LINK_FILE_DROPINS=/run/systemd/network/10-test.link.d/10-override.conf:/run/systemd/network/10-test.link.d/11-override.conf" "$output"
+assert_in "ID_NET_NAME=test1" "$output"
+
+### testing with udevadm test
+output=$(udevadm test --action add /sys/class/net/test1)
+assert_not_in "HOGE=" "$output"
+assert_in "HOGE2=foo2" "$output"
+assert_not_in "BAR=" "$output"
+assert_in "BAR2=baz2" "$output"
+assert_not_in "SHOULD_BE_UNSET=" "$output"
+assert_in "ID_NET_LINK_FILE=/run/systemd/network/10-test.link" "$output"
+assert_in "ID_NET_LINK_FILE_DROPINS=/run/systemd/network/10-test.link.d/10-override.conf:/run/systemd/network/10-test.link.d/11-override.conf" "$output"
+assert_in "ID_NET_NAME=test1" "$output"
+
+# check that test command _does_ update udev database.
+output=$(udevadm info --query property /sys/class/net/test1)
+assert_not_in "HOGE=" "$output"
+assert_in "HOGE2=foo2" "$output"
+assert_not_in "BAR=" "$output"
+assert_in "BAR2=baz2" "$output"
+assert_not_in "SHOULD_BE_UNSET=" "$output"
+assert_in "ID_NET_LINK_FILE=/run/systemd/network/10-test.link" "$output"
+assert_in "ID_NET_LINK_FILE_DROPINS=/run/systemd/network/10-test.link.d/10-override.conf:/run/systemd/network/10-test.link.d/11-override.conf" "$output"
+assert_in "ID_NET_NAME=test1" "$output"
+
+output=$(udevadm test --action change /sys/class/net/test1)
+assert_not_in "HOGE=" "$output"
+assert_not_in "HOGE2=" "$output"
+assert_not_in "BAR=" "$output"
+assert_not_in "BAR2=" "$output"
+assert_not_in "SHOULD_BE_UNSET=" "$output"
+assert_in "ID_NET_LINK_FILE=/run/systemd/network/10-test.link" "$output"
+assert_in "ID_NET_LINK_FILE_DROPINS=/run/systemd/network/10-test.link.d/10-override.conf:/run/systemd/network/10-test.link.d/11-override.conf" "$output"
+assert_in "ID_NET_NAME=test1" "$output"
+
+output=$(udevadm info --query property /sys/class/net/test1)
+assert_not_in "HOGE=" "$output"
+assert_not_in "HOGE2=" "$output"
+assert_not_in "BAR=" "$output"
+assert_not_in "BAR2=" "$output"
+assert_not_in "SHOULD_BE_UNSET=" "$output"
+assert_in "ID_NET_LINK_FILE=/run/systemd/network/10-test.link" "$output"
+assert_in "ID_NET_LINK_FILE_DROPINS=/run/systemd/network/10-test.link.d/10-override.conf:/run/systemd/network/10-test.link.d/11-override.conf" "$output"
+assert_in "ID_NET_NAME=test1" "$output"
+
+# test for specifiers
+cat >/run/systemd/network/10-test.link.d/12-override.conf <<EOF
+[Link]
+Property=
+Property=LINK_VERSION=%v
+EOF
+
+udevadm control --reload
+
+output=$(udevadm test --action add /sys/class/net/test1)
+assert_in "LINK_VERSION=$(uname -r)" "$output"
+
+output=$(udevadm info --query property /sys/class/net/test1)
+assert_in "LINK_VERSION=$(uname -r)" "$output"
+
+# test for constant properties
+cat >/run/systemd/network/10-test.link.d/13-override.conf <<EOF
+[Link]
+Property=
+Property=ACTION=foo IFINDEX=bar
+UnsetProperty=DEVPATH
+EOF
+
+udevadm control --reload
+
+output=$(udevadm test --action add /sys/class/net/test1)
+assert_in "ACTION=add" "$output"
+assert_not_in "ACTION=foo" "$output"
+assert_in "IFINDEX=" "$output"
+assert_not_in "IFINDEX=bar" "$output"
+assert_in "DEVPATH=" "$output"
+
+output=$(udevadm info --query property /sys/class/net/test1)
+assert_not_in "ACTION=foo" "$output"
+assert_in "IFINDEX=" "$output"
+assert_not_in "IFINDEX=bar" "$output"
+assert_in "DEVPATH=" "$output"
+
+# cleanup
+ip link del dev test1
+
+rm -f /run/systemd/network/10-test.link
+rm -rf /run/systemd/network/10-test.link.d
+udevadm control --reload --log-level=info
+
+exit 0
diff --git a/test/units/testsuite-22.18.sh b/test/units/testsuite-22.18.sh
new file mode 100755 (executable)
index 0000000..14e434f
--- /dev/null
@@ -0,0 +1,33 @@
+#!/bin/bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+#
+# Tests for the --purge switch
+#
+set -eux
+set -o pipefail
+
+export SYSTEMD_LOG_LEVEL=debug
+
+systemd-tmpfiles --create - <<EOF
+d /tmp/somedir
+f /tmp/somedir/somefile - - - - baz
+EOF
+
+test -f /tmp/somedir/somefile
+grep -q baz /tmp/somedir/somefile
+
+systemd-tmpfiles --purge - <<EOF
+d /tmp/somedir
+f /tmp/somedir/somefile - - - - baz
+EOF
+
+test ! -f /tmp/somedir/somefile
+test ! -d /tmp/somedir/
+
+systemd-tmpfiles --create --purge - <<EOF
+d /tmp/somedir
+f /tmp/somedir/somefile - - - - baz
+EOF
+
+test -f /tmp/somedir/somefile
+grep -q baz /tmp/somedir/somefile
index b497f73dfe53b1eaebdbe269a4e3c6a77f178c44..d544ce6047c87e97c3c1db9d7104bfb241d0bbca 100755 (executable)
@@ -14,7 +14,7 @@ SERVICE_NAME="${SERVICE_PATH##*/}"
 echo "[#1] Failing ExecReload= should not kill the service"
 cat >"$SERVICE_PATH" <<EOF
 [Service]
-ExecStart=/bin/sleep infinity
+ExecStart=sleep infinity
 ExecReload=/bin/false
 EOF
 
@@ -30,7 +30,7 @@ systemctl stop "$SERVICE_NAME"
 echo "[#2] Failing ExecReload= should not kill the service (multiple ExecReload=)"
 cat >"$SERVICE_PATH" <<EOF
 [Service]
-ExecStart=/bin/sleep infinity
+ExecStart=sleep infinity
 ExecReload=/bin/true
 ExecReload=/bin/false
 ExecReload=/bin/true
@@ -47,7 +47,7 @@ systemctl stop "$SERVICE_NAME"
 echo "[#3] Failing ExecReload=- should not affect reload's exit code"
 cat >"$SERVICE_PATH" <<EOF
 [Service]
-ExecStart=/bin/sleep infinity
+ExecStart=sleep infinity
 ExecReload=-/bin/false
 EOF
 
index a82b54fe9e286cacc4962d56dd7495206d8ede85..a88324332638bc5d808c1537a630c7e9e50cbe67 100755 (executable)
@@ -26,7 +26,7 @@ StateDirectory=test-service
 CacheDirectory=test-service
 LogsDirectory=test-service
 RuntimeDirectoryPreserve=yes
-ExecStart=/bin/sleep infinity
+ExecStart=sleep infinity
 Type=exec
 EOF
 
@@ -97,7 +97,7 @@ StateDirectory=test-service
 CacheDirectory=test-service
 LogsDirectory=test-service
 RuntimeDirectoryPreserve=yes
-ExecStart=/bin/sleep infinity
+ExecStart=sleep infinity
 Type=exec
 EOF
 
index 9c4f17d7a29efba0b1edee03631d749e19294477..e40990bdbb8fe47f78eba4b66791cdcf2735cad8 100755 (executable)
@@ -37,7 +37,7 @@ sleep 3.1
 
 cat >/run/systemd/system/testsuite-23-no-reload.service <<EOF
 [Service]
-ExecStart=/bin/sleep infinity
+ExecStart=sleep infinity
 EOF
 
 systemctl start testsuite-23-no-reload.service
@@ -53,7 +53,7 @@ sleep 3.1
 
 cat >/run/systemd/system/testsuite-23-no-reload.service <<EOF
 [Service]
-ExecStart=/bin/sleep infinity
+ExecStart=sleep infinity
 EOF
 
 # Start a non-existing unit first, so that the cache is reloaded for an unrelated
@@ -85,7 +85,7 @@ systemctl start testsuite-23-no-reload.target
 
 cat >/run/systemd/system/testsuite-23-no-reload.service <<EOF
 [Service]
-ExecStart=/bin/sleep infinity
+ExecStart=sleep infinity
 EOF
 
 systemctl restart testsuite-23-no-reload.target
index eeec411e9c8f4fb49af5da00ac9045ade1b92c85..4d2a71843350620647faef15d45efddee7bd1d66 100755 (executable)
@@ -231,14 +231,26 @@ 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=RSATestKey;type=cert" --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"
+
+    PIN="1234" systemd-cryptenroll --pkcs11-token-uri="pkcs11:token=TestToken;object=ECTestKey;type=cert" --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
index b250a5c18fb1527f1e793247f79783291044b091..8ea801ee96ee44069b0eb3e4219f3065b59fa947 100755 (executable)
@@ -25,14 +25,22 @@ setup_test_user() {
     trap cleanup_test_user EXIT
 }
 
-test_enable_debug() {
-    mkdir -p /run/systemd/system/systemd-logind.service.d
-    cat >/run/systemd/system/systemd-logind.service.d/debug.conf <<EOF
+test_write_dropin() {
+    systemctl edit --runtime --stdin systemd-logind.service --drop-in=debug.conf <<EOF
 [Service]
 Environment=SYSTEMD_LOG_LEVEL=debug
 EOF
-    systemctl daemon-reload
-    systemctl stop systemd-logind.service
+
+    # We test "coldplug" (completely stop and start logind) here. So we need to preserve
+    # the fdstore, which might contain session leader pidfds. This is extremely rare use case
+    # and shall not be considered fully supported.
+    # See also: https://github.com/systemd/systemd/pull/30610#discussion_r1440507850
+    systemctl edit --runtime --stdin systemd-logind.service --drop-in=fdstore-preserve.conf <<EOF
+[Service]
+FileDescriptorStorePreserve=yes
+EOF
+
+    systemctl restart systemd-logind.service
 }
 
 testcase_properties() {
@@ -673,7 +681,7 @@ EOF
 }
 
 setup_test_user
-test_enable_debug
+test_write_dropin
 run_testcases
 
 touch /testok
index 8a53b984a652d586931a73f049c60ea742f1d93a..12bf1393e4b60ce50c87080ae0d3021c86485ab1 100755 (executable)
@@ -80,7 +80,7 @@ EOF
 
 writeTestUnit() {
     mkdir -p "$testUnitFile.d/"
-    printf "[Service]\nExecStart=/bin/sleep 3600\n" >"$testUnitFile"
+    printf "[Service]\nExecStart=sleep 3600\n" >"$testUnitFile"
 }
 
 writeTestUnitNUMAPolicy() {
index c116c80981b4793f1ff9b5fcb3fb53e0fc9a5a15..1bb9ddf1b70ccf600c09cac7730e4c78a7bff08e 100644 (file)
@@ -1,3 +1,3 @@
 # SPDX-License-Identifier: LGPL-2.1-or-later
 [Service]
-ExecStart=/bin/sleep 3600
+ExecStart=sleep 3600
index fbd4ae692c0c4df6288dadea1f952d1987cc8471..0819a4b8433923b37a4d71f14ed082284fe3c465 100755 (executable)
@@ -4,12 +4,24 @@ set -eux
 
 systemd-analyze log-level debug
 
+journalctl --list-namespaces -o json | jq .
+
 systemd-run --wait -p LogNamespace=foobar echo "hello world"
+systemd-run --wait -p LogNamespace=foobaz echo "hello world"
 
 journalctl --namespace=foobar --sync
+journalctl --namespace=foobaz --sync
+ls -l /var/log/journal/
+journalctl --list-namespaces
+
 journalctl -o cat --namespace=foobar >/tmp/hello-world
 journalctl -o cat >/tmp/no-hello-world
 
+journalctl --list-namespaces | grep foobar
+journalctl --list-namespaces | grep foobaz
+journalctl --list-namespaces -o json | jq .
+[[ "$(journalctl --root=/tmp --list-namespaces --quiet)" == "" ]]
+
 grep "^hello world$" /tmp/hello-world
 (! grep "^hello world$" /tmp/no-hello-world)
 
index f6801da0a7042cac72c549564550be7cc4fa30fb..35d5503f73a7dcf3fdafa2c002b322aa11f4c6fc 100755 (executable)
@@ -258,7 +258,7 @@ ConditionVirtualization=
 Type=simple
 AmbientCapabilities=
 ExecStart=
-ExecStart=/bin/sleep infinity
+ExecStart=sleep infinity
 EOF
         systemctl daemon-reload
     fi
index 28218ab6d7456ede0fa6a3c5854912d56c95e532..0e33ec9a26f210fc00890f4c4b5b85f4bcec35d6 100755 (executable)
@@ -352,7 +352,7 @@ Type=notify
 RemainAfterExit=yes
 MountAPIVFS=yes
 PrivateTmp=yes
-ExecStart=/bin/sh -c ' \\
+ExecStart=sh -c ' \\
     systemd-notify --ready; \\
     while [ ! -f /tmp/img/usr/lib/os-release ] || ! grep -q -F MARKER /tmp/img/usr/lib/os-release; do \\
         sleep 0.1; \\
@@ -416,8 +416,8 @@ RootImage=${image}.raw
 ExtensionImages=/usr/share/app0.raw /usr/share/app1.raw:nosuid
 # Relevant only for sanitizer runs
 UnsetEnvironment=LD_PRELOAD
-ExecStart=/bin/bash -c '/opt/script0.sh | grep ID'
-ExecStart=/bin/bash -c '/opt/script1.sh | grep ID'
+ExecStart=bash -c '/opt/script0.sh | grep ID'
+ExecStart=bash -c '/opt/script1.sh | grep ID'
 Type=oneshot
 RemainAfterExit=yes
 EOF
@@ -449,8 +449,8 @@ RootImage=${image}.raw
 ExtensionDirectories=${image_dir}/app0 ${image_dir}/app1
 # Relevant only for sanitizer runs
 UnsetEnvironment=LD_PRELOAD
-ExecStart=/bin/bash -c '/opt/script0.sh | grep ID'
-ExecStart=/bin/bash -c '/opt/script1.sh | grep ID'
+ExecStart=bash -c '/opt/script0.sh | grep ID'
+ExecStart=bash -c '/opt/script1.sh | grep ID'
 Type=oneshot
 RemainAfterExit=yes
 EOF
@@ -493,6 +493,20 @@ systemd-sysext unmerge
 rm -rf /run/extensions/app-reject
 rm /var/lib/extensions/app-nodistro.raw
 
+# Some super basic test that RootImage= works with .v/ dirs
+VBASE="vtest$RANDOM"
+VDIR="/tmp/${VBASE}.v"
+mkdir "$VDIR"
+
+ln -s "${image}.raw" "$VDIR/${VBASE}_33.raw"
+ln -s "${image}.raw" "$VDIR/${VBASE}_34.raw"
+ln -s "${image}.raw" "$VDIR/${VBASE}_35.raw"
+
+systemd-run -P -p RootImage="$VDIR" cat /usr/lib/os-release | grep -q -F "MARKER=1"
+
+rm "$VDIR/${VBASE}_33.raw" "$VDIR/${VBASE}_34.raw" "$VDIR/${VBASE}_35.raw"
+rmdir "$VDIR"
+
 mkdir -p /run/machines /run/portables /run/extensions
 touch /run/machines/a.raw /run/portables/b.raw /run/extensions/c.raw
 
index 0c4e3205eb87a6112f848760b8f152b7574905ee..c64b2039f300f1f5346a063e37b1467c698bd2af 100755 (executable)
@@ -1264,6 +1264,29 @@ EOF
     assert_in "${loop}p3 : start= *${start}, size= *${size}, type=0FC63DAF-8483-4772-8E79-3D69D8477DE4, uuid=DB081670-07AE-48CA-9F5E-813D5E40B976, name=\"linux-generic-2\"" "$output"
 }
 
+testcase_dropped_partitions() {
+    local workdir image defs
+
+    workdir="$(mktemp --directory "/tmp/test-repart.dropped-partitions.XXXXXXXXXX")"
+    # shellcheck disable=SC2064
+    trap "rm -rf '${workdir:?}'" RETURN
+
+    image="$workdir/image.img"
+    truncate -s 32M "$image"
+
+    defs="$workdir/defs"
+    mkdir "$defs"
+    echo -ne "[Partition]\nType=root\n" >"$defs/10-part1.conf"
+    echo -ne "[Partition]\nType=root\nSizeMinBytes=1T\nPriority=1\n" >"$defs/11-dropped-first.conf"
+    echo -ne "[Partition]\nType=root\n" >"$defs/12-part2.conf"
+    echo -ne "[Partition]\nType=root\nSizeMinBytes=1T\nPriority=2\n" >"$defs/13-dropped-second.conf"
+
+    systemd-repart --empty=allow --pretty=yes --dry-run=no --definitions="$defs" "$image"
+
+    sfdisk -q -l "$image"
+    [[ "$(sfdisk -q -l "$image" | grep -c "$image")" -eq 2 ]]
+}
+
 OFFLINE="yes"
 run_testcases
 
index 1b622b3d8edfc1428f842318cddcd5ae56f27fb5..61564dc1664e1800f8a3278cc051350219ace053 100755 (executable)
@@ -36,7 +36,7 @@ Description=TEST-59-RELOADING-RESTART Normal exit
 
 [Service]
 Type=notify
-ExecStart=/bin/bash -c "systemd-notify --ready; systemd-notify RELOADING=1; sleep 1; exit 1"
+ExecStart=bash -c "systemd-notify --ready; systemd-notify RELOADING=1; sleep 1; exit 1"
 EOF
 
 cat >/run/systemd/system/testservice-fail-restart-59.service <<EOF
@@ -45,7 +45,7 @@ Description=TEST-59-RELOADING-RESTART Restart=on-failure
 
 [Service]
 Type=notify
-ExecStart=/bin/bash -c "systemd-notify --ready; systemd-notify RELOADING=1; sleep 1; exit 1"
+ExecStart=bash -c "systemd-notify --ready; systemd-notify RELOADING=1; sleep 1; exit 1"
 Restart=on-failure
 StartLimitBurst=1
 EOF
@@ -57,7 +57,7 @@ Description=TEST-59-RELOADING-RESTART Restart=on-abort
 
 [Service]
 Type=notify
-ExecStart=/bin/bash -c "systemd-notify --ready; systemd-notify RELOADING=1; sleep 5; exit 1"
+ExecStart=bash -c "systemd-notify --ready; systemd-notify RELOADING=1; sleep 5; exit 1"
 Restart=on-abort
 EOF
 
index fa3a7e77b2f0ee65935ff2c5212ee15910944e9b..16695c18a2b9bbc18505b08310db245495e8d8ce 100644 (file)
@@ -2,8 +2,8 @@
 [Unit]
 Description=TEST-62-RESTRICT-IFACES-all-pings-work
 [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'
+ExecStart=sh -c 'ping -c 1 -W 0.2 192.168.113.1'
+ExecStart=sh -c 'ping -c 1 -W 0.2 192.168.113.5'
+ExecStart=sh -c 'ping -c 1 -W 0.2 192.168.113.9'
 RestrictNetworkInterfaces=
 Type=oneshot
index b83362db89820dda8d31c93f12b1bfc8a21ce42b..bce7e8e6cbaca0986ce4175185b09fcdbfbdbffa 100644 (file)
@@ -2,9 +2,9 @@
 [Unit]
 Description=TEST-62-RESTRICT-IFACES-allow-list
 [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'
+ExecStart=sh -c 'ping -c 1 -W 0.2 192.168.113.1'
+ExecStart=sh -c 'ping -c 1 -W 0.2 192.168.113.5'
+ExecStart=sh -c '! ping -c 1 -W 0.2 192.168.113.9'
 RestrictNetworkInterfaces=veth0
 RestrictNetworkInterfaces=veth1
 Type=oneshot
index b6c8e7aa87f35789ffd50706d26383ea6e7f0f8a..116530bad3507f074bd5abd918da4d8e9ae842f4 100644 (file)
@@ -2,9 +2,9 @@
 [Unit]
 Description=TEST-62-RESTRICT-IFACES-deny-list
 [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'
+ExecStart=sh -c '! ping -c 1 -W 0.2 192.168.113.1'
+ExecStart=sh -c '! ping -c 1 -W 0.2 192.168.113.5'
+ExecStart=sh -c 'ping -c 1 -W 0.2 192.168.113.9'
 RestrictNetworkInterfaces=~veth0
 RestrictNetworkInterfaces=~veth1
 Type=oneshot
index 053e6d205f45ab354654c452d62f11b46b709b42..200a38355a300d31d9b845744166137cf52dd59d 100644 (file)
@@ -2,9 +2,9 @@
 [Unit]
 Description=TEST-62-RESTRICT-IFACES-empty-assignment
 [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'
+ExecStart=sh -c 'ping -c 1 -W 0.2 192.168.113.1'
+ExecStart=sh -c 'ping -c 1 -W 0.2 192.168.113.5'
+ExecStart=sh -c 'ping -c 1 -W 0.2 192.168.113.9'
 RestrictNetworkInterfaces=veth0
 RestrictNetworkInterfaces=
 Type=oneshot
index a8f268d1d8196bb9d8ccb4c8679dfbb162eb2b41..51761ba27eba88b08c4f266be850854693ddaca1 100644 (file)
@@ -2,9 +2,9 @@
 [Unit]
 Description=TEST-62-RESTRICT-IFACES-invert-assignment
 [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'
+ExecStart=sh -c '! ping -c 1 -W 0.2 192.168.113.1'
+ExecStart=sh -c 'ping -c 1 -W 0.2 192.168.113.5'
+ExecStart=sh -c '! ping -c 1 -W 0.2 192.168.113.9'
 RestrictNetworkInterfaces=veth0
 RestrictNetworkInterfaces=veth0 veth1
 RestrictNetworkInterfaces=~veth0
index 078bc8b5f5301d361e8cce07c44656a5981b188b..e14bdbefa78e7cf665aca618187ed1ccf89e58b4 100755 (executable)
@@ -107,7 +107,7 @@ ConditionKernelVersion=>1.0
 ConditionPathExists=/etc/os-release
 
 [Service]
-ExecStart=/bin/true
+ExecStart=true
 EOF
 systemctl daemon-reload
 systemd-analyze condition --unit="$UNIT_NAME"
index 2d815a96da21cefad636c8c96a3887b3f893adff..14b0d8987af08bfbfe71dc0cd2ede5181cde0664 100644 (file)
@@ -7,4 +7,4 @@ DevicePolicy=strict
 DeviceAllow=/dev/null r
 StandardOutput=file:/tmp/testsuite66serviceresults
 ExecStartPre=rm -f /tmp/testsuite66serviceresults
-ExecStart=/bin/bash -c "while true; do sleep 0.01 && echo meow >/dev/null && echo thisshouldnotbehere; done"
+ExecStart=bash -c "while true; do sleep 0.01 && echo meow >/dev/null && echo thisshouldnotbehere; done"
index 7aa0664b85f6ddfc82e41b61ec66493c784a01f6..3910369c0ffd91cb3fccac026be26329caa31c80 100644 (file)
@@ -4,4 +4,4 @@ Description=TEST-69-SHUTDOWN
 
 [Service]
 Type=oneshot
-ExecStart=/bin/true
+ExecStart=true
index da765a9d97cb7c6c3d67bc340edcd1fa4ae054cc..813a676c928d28218f3c88f729acebd943af5195 100755 (executable)
@@ -61,6 +61,11 @@ get_chassis() (
     echo "$CHASSIS"
 )
 
+stop_hostnamed() {
+    systemctl stop systemd-hostnamed.service
+    systemctl reset-failed systemd-hostnamed # reset trigger limit
+}
+
 testcase_chassis() {
     local i
 
@@ -80,7 +85,7 @@ testcase_chassis() {
         assert_eq "$(get_chassis)" "$i"
     done
 
-    systemctl stop systemd-hostnamed.service
+    stop_hostnamed
     rm -f /etc/machine-info
 
     # fallback chassis type
@@ -95,7 +100,7 @@ restore_sysfs_dmi() {
     umount /sys/class/dmi/id
     rm -rf /run/systemd/system/systemd-hostnamed.service.d
     systemctl daemon-reload
-    systemctl stop systemd-hostnamed
+    stop_hostnamed
 }
 
 testcase_firmware_date() {
@@ -120,15 +125,15 @@ EOF
     echo '1' >/sys/class/dmi/id/uevent
 
     echo '09/08/2000' >/sys/class/dmi/id/bios_date
-    systemctl stop systemd-hostnamed
+    stop_hostnamed
     assert_in '2000-09-08' "$(hostnamectl)"
 
     echo '2022' >/sys/class/dmi/id/bios_date
-    systemctl stop systemd-hostnamed
+    stop_hostnamed
     assert_not_in 'Firmware Date' "$(hostnamectl)"
 
     echo 'garbage' >/sys/class/dmi/id/bios_date
-    systemctl stop systemd-hostnamed
+    stop_hostnamed
     assert_not_in 'Firmware Date' "$(hostnamectl)"
 }
 
@@ -223,6 +228,14 @@ testcase_nss-myhostname() {
     (! getent hosts -s myhostname fd00:dead:beef:cafe::1)
 }
 
+test_varlink() {
+    A="$(mktemp -u)"
+    B="$(mktemp -u)"
+    varlinkctl call /run/systemd/io.systemd.Hostname io.systemd.Hostname.Describe '{}' --json=short > "$A"
+    hostnamectl --json=short > "$B"
+    cmp "$A" "$B"
+}
+
 run_testcases
 
 touch /testok
index a0e1cb52dd70994485951ede64d66114211feb6f..dabe2347093539e964feeae8342d33b7cedfa94f 100755 (executable)
@@ -14,14 +14,14 @@ trap at_exit EXIT
 # Extended unit
 cat >"/run/systemd/system/delta-test-unit-extended.service" <<EOF
 [Service]
-ExecStart=/bin/true
+ExecStart=true
 EOF
 mkdir -p "/run/systemd/system/delta-test-unit-extended.service.d"
 cat >"/run/systemd/system/delta-test-unit-extended.service.d/override.conf" <<EOF
 [Unit]
 Description=Foo Bar
 [Service]
-ExecStartPre=/bin/true
+ExecStartPre=true
 EOF
 # Masked unit
 cp -fv /run/systemd/system/delta-test-unit-extended.service /run/systemd/system/delta-test-unit-masked.service
diff --git a/test/units/testsuite-74.network-generator.sh b/test/units/testsuite-74.network-generator.sh
new file mode 100755 (executable)
index 0000000..e7ccea1
--- /dev/null
@@ -0,0 +1,27 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+# shellcheck disable=SC2016
+set -eux
+set -o pipefail
+
+at_exit() {
+    rm -f /run/credstore/network.network.50-testme
+    rm -f /run/systemd/system/systemd-network-generator.service.d/50-testme.conf
+}
+
+trap at_exit EXIT
+
+mkdir -p /run/credstore
+cat > /run/credstore/network.network.50-testme <<EOF
+[Match]
+Property=IDONTEXIST
+EOF
+
+systemctl edit systemd-network-generator.service --stdin --drop-in=50-testme.conf <<EOF
+[Service]
+LoadCredential=network.network.50-testme
+EOF
+
+systemctl restart systemd-network-generator
+
+test -f /run/systemd/network/50-testme.network
index 5a962699c70b2aca2fbb93ee7051f210eb772146..7912360315e97187f266b19365d4e9231b46e452 100755 (executable)
@@ -53,6 +53,32 @@ if [[ -x /usr/lib/systemd/systemd-pcrextend ]]; then
     varlinkctl introspect /usr/lib/systemd/systemd-pcrextend io.systemd.PCRExtend
 fi
 
+# SSH transport
+SSHBINDIR="$(mktemp -d)"
+
+rm_rf_sshbindir() {
+    rm -rf "$SSHBINDIR"
+}
+
+trap rm_rf_sshbindir EXIT
+
+# Create a fake "ssh" binary that validates everything works as expected
+cat > "$SSHBINDIR"/ssh <<'EOF'
+#!/bin/sh
+
+set -xe
+
+test "$1" = "-W"
+test "$2" = "/run/systemd/journal/io.systemd.journal"
+test "$3" = "foobar"
+
+exec socat - UNIX-CONNECT:/run/systemd/journal/io.systemd.journal
+EOF
+
+chmod +x "$SSHBINDIR"/ssh
+
+SYSTEMD_SSH="$SSHBINDIR/ssh" varlinkctl info ssh:foobar:/run/systemd/journal/io.systemd.journal
+
 # Go through all varlink sockets we can find under /run/systemd/ for some extra coverage
 find /run/systemd/ -name "io.systemd*" -type s | while read -r socket; do
     varlinkctl info "$socket"
@@ -87,3 +113,7 @@ done
 (! varlinkctl call /run/systemd/userdb/io.systemd.Multiplexer io.systemd.UserDatabase.GetUserRecord </dev/null)
 (! varlinkctl validate-idl "")
 (! varlinkctl validate-idl </dev/null)
+
+varlinkctl info /run/systemd/io.systemd.Hostname
+varlinkctl introspect /run/systemd/io.systemd.Hostname io.systemd.Hostname
+varlinkctl call /run/systemd/io.systemd.Hostname io.systemd.Hostname.Describe '{}'
diff --git a/test/units/testsuite-74.vpick.sh b/test/units/testsuite-74.vpick.sh
new file mode 100755 (executable)
index 0000000..400097f
--- /dev/null
@@ -0,0 +1,116 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+at_exit() {
+    set +e
+    rm -rf /var/lib/machines/mymachine.raw.v
+    rm -rf /var/lib/machines/mytree.v
+    rm -rf /var/lib/machines/testroot.v
+    umount -l /tmp/dotvroot
+    rmdir /tmp/dotvroot
+}
+
+trap at_exit EXIT
+
+mkdir -p /var/lib/machines/mymachine.raw.v
+
+touch /var/lib/machines/mymachine.raw.v/mymachine_7.5.13.raw
+touch /var/lib/machines/mymachine.raw.v/mymachine_7.5.14_x86-64.raw
+touch /var/lib/machines/mymachine.raw.v/mymachine_7.6.0_arm64.raw
+touch /var/lib/machines/mymachine.raw.v/mymachine_7.7.0_x86-64+0-5.raw
+
+mkdir -p /var/lib/machines/mytree.v
+
+mkdir /var/lib/machines/mytree.v/mytree_33.4
+mkdir /var/lib/machines/mytree.v/mytree_33.5
+mkdir /var/lib/machines/mytree.v/mytree_36.0+0-5
+mkdir /var/lib/machines/mytree.v/mytree_37.0_arm64+2-3
+mkdir /var/lib/machines/mytree.v/mytree_38.0_arm64+0-5
+
+ARCH="$(busctl get-property org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager Architecture | cut -d\" -f 2)"
+
+export SYSTEMD_LOG_LEVEL=debug
+
+if [ "$ARCH" = "x86-64" ] ; then
+    test "$(systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw)" = "/var/lib/machines/mymachine.raw.v/mymachine_7.5.14_x86-64.raw"
+
+    test "$(systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -V 7.5.13)" = "/var/lib/machines/mymachine.raw.v/mymachine_7.5.13.raw"
+    test "$(systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -V 7.5.14)" = "/var/lib/machines/mymachine.raw.v/mymachine_7.5.14_x86-64.raw"
+    (! systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -V 7.6.0)
+    test "$(systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -V 7.7.0)" = "/var/lib/machines/mymachine.raw.v/mymachine_7.7.0_x86-64+0-5.raw"
+
+    systemd-dissect --discover | grep "/var/lib/machines/mymachine.raw.v/mymachine_7.5.14_x86-64.raw"
+elif [ "$ARCH" = "arm64" ] ; then
+    test "$(systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw)" = "/var/lib/machines/mymachine.raw.v/mymachine_7.6.0_arm64.raw"
+
+    test "$(systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -V 7.5.13)" = "/var/lib/machines/mymachine.raw.v/mymachine_7.5.13.raw"
+    (! systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -V 7.5.14)
+    test "$(systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -V 7.6.0)" = "/var/lib/machines/mymachine.raw.v/mymachine_7.6.0_arm64.raw"
+    (! systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -V 7.7.0)
+
+    systemd-dissect --discover | grep "/var/lib/machines/mymachine.raw.v/mymachine_7.6.0_arm64.raw"
+else
+    test "$(systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw)" = "/var/lib/machines/mymachine.raw.v/mymachine_7.5.13.raw"
+
+    test "$(systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -V 7.5.13)" = "/var/lib/machines/mymachine.raw.v/mymachine_7.5.13.raw"
+    (! systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -V 7.5.14)
+    (! systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -V 7.6.0)
+    (! systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -V 7.7.0)
+
+    systemd-dissect --discover | grep "/var/lib/machines/mymachine.raw.v/mymachine_7.5.13.raw"
+fi
+
+test "$(systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -A x86-64)" = "/var/lib/machines/mymachine.raw.v/mymachine_7.5.14_x86-64.raw"
+test "$(systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -A arm64)" = "/var/lib/machines/mymachine.raw.v/mymachine_7.6.0_arm64.raw"
+(! systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -A ia64)
+
+test "$(systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -A arm64 -p version)" = "7.6.0"
+test "$(systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -A arm64 -p type)" = "reg"
+test "$(systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -A arm64 -p filename)" = "mymachine_7.6.0_arm64.raw"
+test "$(systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -A arm64 -p arch)" = "arm64"
+
+test "$(systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -A arm64 -t reg)" = "/var/lib/machines/mymachine.raw.v/mymachine_7.6.0_arm64.raw"
+(! systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -A arm64 -t dir)
+(! systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -A arm64 -t fifo)
+(! systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -A arm64 -t sock)
+
+if [ "$ARCH" != "arm64" ] ; then
+    test "$(systemd-vpick /var/lib/machines/mytree.v)" = "/var/lib/machines/mytree.v/mytree_33.5/"
+    test "$(systemd-vpick /var/lib/machines/mytree.v --type=dir)" = "/var/lib/machines/mytree.v/mytree_33.5/"
+else
+    test "$(systemd-vpick /var/lib/machines/mytree.v)" = "/var/lib/machines/mytree.v/mytree_37.0_arm64+2-3/"
+    test "$(systemd-vpick /var/lib/machines/mytree.v --type=dir)" = "/var/lib/machines/mytree.v/mytree_37.0_arm64+2-3/"
+fi
+
+(! systemd-vpick /var/lib/machines/mytree.v --type=reg)
+
+mkdir /tmp/dotvroot
+mount --bind / /tmp/dotvroot
+
+mkdir /var/lib/machines/testroot.v
+mkdir /var/lib/machines/testroot.v/testroot_32
+ln -s /tmp/dotvroot /var/lib/machines/testroot.v/testroot_33
+mkdir /var/lib/machines/testroot.v/testroot_34
+
+ls -l /var/lib/machines/testroot.v
+
+test "$(systemd-vpick /var/lib/machines/testroot.v)" = /var/lib/machines/testroot.v/testroot_34/
+test "$(systemd-vpick --resolve=yes /var/lib/machines/testroot.v)" = /var/lib/machines/testroot.v/testroot_34/
+(! systemd-run --wait -p RootDirectory=/var/lib/machines/testroot.v /bin/true)
+
+find /var/lib/machines/testroot.v/testroot_34
+rm -rf /var/lib/machines/testroot.v/testroot_34
+test "$(systemd-vpick /var/lib/machines/testroot.v)" = /var/lib/machines/testroot.v/testroot_33/
+test "$(systemd-vpick --resolve=yes /var/lib/machines/testroot.v)" = /tmp/dotvroot/
+systemd-run --wait -p RootDirectory=/var/lib/machines/testroot.v /bin/true
+
+rm /var/lib/machines/testroot.v/testroot_33
+test "$(systemd-vpick /var/lib/machines/testroot.v)" = /var/lib/machines/testroot.v/testroot_32/
+test "$(systemd-vpick --resolve=yes /var/lib/machines/testroot.v)" = /var/lib/machines/testroot.v/testroot_32/
+(! systemd-run --wait -p RootDirectory=/var/lib/machines/testroot.v /bin/true)
+
+rm -rf /var/lib/machines/testroot.v/testroot_32
+(! systemd-vpick /var/lib/machines/testroot.v)
+(! systemd-run --wait -p RootDirectory=/var/lib/machines/testroot.v /bin/true)
index 54234484c460bb680b834c12eecf791a98f34927..f1fb5d943a5ff5b18ab62cb1511dd069b535ced6 100755 (executable)
@@ -197,6 +197,25 @@ DNSSEC=allow-downgrade
 DNS=10.0.0.1
 DNS=fd00:dead:beef:cafe::1
 EOF
+cat >/etc/systemd/network/10-dns1.netdev <<EOF
+[NetDev]
+Name=dns1
+Kind=dummy
+EOF
+cat >/etc/systemd/network/10-dns1.network <<EOF
+[Match]
+Name=dns1
+
+[Network]
+Address=10.99.0.1/24
+DNSSEC=no
+EOF
+systemctl edit --stdin --full --runtime --force "resolved-dummy-server.service" <<EOF
+[Service]
+Type=notify
+Environment=SYSTEMD_LOG_LEVEL=debug
+ExecStart=/usr/lib/systemd/tests/unit-tests/manual/test-resolved-dummy-server 10.99.0.1:53
+EOF
 
 DNS_ADDRESSES=(
     "10.0.0.1"
@@ -236,6 +255,7 @@ ln -svf /etc/bind.keys /etc/bind/bind.keys
 systemctl unmask systemd-networkd
 systemctl start systemd-networkd
 restart_resolved
+systemctl start resolved-dummy-server
 # Create knot's runtime dir, since from certain version it's provided only by
 # the package and not created by tmpfiles/systemd
 if [[ ! -d /run/knot ]]; then
@@ -246,6 +266,7 @@ systemctl start knot
 # Wait a bit for the keys to propagate
 sleep 4
 
+systemctl status resolved-dummy-server
 networkctl status
 resolvectl status
 resolvectl log-level debug
@@ -254,9 +275,13 @@ resolvectl log-level debug
 systemd-run -u resolvectl-monitor.service -p Type=notify resolvectl monitor
 systemd-run -u resolvectl-monitor-json.service -p Type=notify resolvectl monitor --json=short
 
-# Check if all the zones are valid (zone-check always returns 0, so let's check
-# if it produces any errors/warnings)
-run knotc zone-check
+# FIXME: knot, unfortunately, incorrectly complains about missing zone files for zones
+#        that are forwarded using the `dnsproxy` module. Until the issue is resolved,
+#        let's fall back to pre-processing the `zone-check` output a bit before checking it
+#
+# See: https://gitlab.nic.cz/knot/knot-dns/-/issues/913
+run knotc zone-check || :
+sed -i '/forwarded.test./d' "$RUN_OUT"
 [[ ! -s "$RUN_OUT" ]]
 # We need to manually propagate the DS records of onlinesign.test. to the parent
 # zone, since they're generated online
@@ -416,6 +441,18 @@ grep -qF "; fully validated" "$RUN_OUT"
 run resolvectl openpgp mr.smith@signed.test
 grep -qF "5a786cdc59c161cdafd818143705026636962198c66ed4c5b3da321e._openpgpkey.signed.test" "$RUN_OUT"
 grep -qF "authenticated: yes" "$RUN_OUT"
+# Check zone transfers (AXFR/IXFR)
+# Note: since resolved doesn't support zone transfers, let's just make sure it
+#       simply refuses such requests without choking on them
+# See: https://github.com/systemd/systemd/pull/30809#issuecomment-1880102804
+run dig @ns1.unsigned.test AXFR signed.test
+grep -qE "SOA\s+ns1.unsigned.test. root.unsigned.test." "$RUN_OUT"
+run dig AXFR signed.test
+grep -qF "; Transfer failed" "$RUN_OUT"
+run dig @ns1.unsigned.test IXFR=43 signed.test
+grep -qE "SOA\s+ns1.unsigned.test. root.unsigned.test." "$RUN_OUT"
+run dig IXFR=43 signed.test
+grep -qF "; Transfer failed" "$RUN_OUT"
 
 # DNSSEC validation with multiple records of the same type for the same name
 # Issue: https://github.com/systemd/systemd/issues/22002
@@ -543,6 +580,61 @@ grep -qF "fd00:dead:beef:cafe::123" "$RUN_OUT"
 #run dig +dnssec this.does.not.exist.untrusted.test
 #grep -qF "status: NXDOMAIN" "$RUN_OUT"
 
+: "--- ZONE: forwarded.test (queries forwarded to our dummy test server) ---"
+JOURNAL_CURSOR="$(mktemp)"
+journalctl -n0 -q --cursor-file="$JOURNAL_CURSOR"
+
+# See "test-resolved-dummy-server.c" for the server part
+(! run resolvectl query nope.forwarded.test)
+grep -qF "nope.forwarded.test" "$RUN_OUT"
+grep -qF "not found" "$RUN_OUT"
+
+# SERVFAIL + EDE code 6: DNSSEC Bogus
+(! run resolvectl query edns-bogus-dnssec.forwarded.test)
+grep -qE "^edns-bogus-dnssec.forwarded.test:.+: upstream-failure \(DNSSEC Bogus\)" "$RUN_OUT"
+# Same thing, but over Varlink
+(! run varlinkctl call /run/systemd/resolve/io.systemd.Resolve io.systemd.Resolve.ResolveHostname '{"name" : "edns-bogus-dnssec.forwarded.test"}')
+grep -qF "io.systemd.Resolve.DNSSECValidationFailed" "$RUN_OUT"
+grep -qF '{"result":"upstream-failure","extendedDNSErrorCode":6}' "$RUN_OUT"
+journalctl --sync
+journalctl -u systemd-resolved.service --cursor-file="$JOURNAL_CURSOR" --grep "Server returned error: SERVFAIL \(DNSSEC Bogus\). Lookup failed."
+
+# SERVFAIL + EDE code 16: Censored + extra text
+(! run resolvectl query edns-extra-text.forwarded.test)
+grep -qE "^edns-extra-text.forwarded.test.+: SERVFAIL \(Censored: Nothing to see here!\)" "$RUN_OUT"
+(! run varlinkctl call /run/systemd/resolve/io.systemd.Resolve io.systemd.Resolve.ResolveHostname '{"name" : "edns-extra-text.forwarded.test"}')
+grep -qF "io.systemd.Resolve.DNSError" "$RUN_OUT"
+grep -qF '{"rcode":2,"extendedDNSErrorCode":16,"extendedDNSErrorMessage":"Nothing to see here!"}' "$RUN_OUT"
+journalctl --sync
+journalctl -u systemd-resolved.service --cursor-file="$JOURNAL_CURSOR" --grep "Server returned error: SERVFAIL \(Censored: Nothing to see here!\)"
+
+# SERVFAIL + EDE code 0: Other + extra text
+(! run resolvectl query edns-code-zero.forwarded.test)
+grep -qE "^edns-code-zero.forwarded.test:.+: SERVFAIL \(Other: 🐱\)" "$RUN_OUT"
+(! run varlinkctl call /run/systemd/resolve/io.systemd.Resolve io.systemd.Resolve.ResolveHostname '{"name" : "edns-code-zero.forwarded.test"}')
+grep -qF "io.systemd.Resolve.DNSError" "$RUN_OUT"
+grep -qF '{"rcode":2,"extendedDNSErrorCode":0,"extendedDNSErrorMessage":"🐱"}' "$RUN_OUT"
+journalctl --sync
+journalctl -u systemd-resolved.service --cursor-file="$JOURNAL_CURSOR" --grep "Server returned error: SERVFAIL \(Other: 🐱\)"
+
+# SERVFAIL + invalid EDE code
+(! run resolvectl query edns-invalid-code.forwarded.test)
+grep -qE "^edns-invalid-code.forwarded.test:.+: SERVFAIL \([0-9]+\)" "$RUN_OUT"
+(! run varlinkctl call /run/systemd/resolve/io.systemd.Resolve io.systemd.Resolve.ResolveHostname '{"name" : "edns-invalid-code.forwarded.test"}')
+grep -qF "io.systemd.Resolve.DNSError" "$RUN_OUT"
+grep -qE '{"rcode":2,"extendedDNSErrorCode":[0-9]+}' "$RUN_OUT"
+journalctl --sync
+journalctl -u systemd-resolved.service --cursor-file="$JOURNAL_CURSOR" --grep "Server returned error: SERVFAIL \(\d+\)"
+
+# SERVFAIL + invalid EDE code + extra text
+(! run resolvectl query edns-invalid-code-with-extra-text.forwarded.test)
+grep -qE '^edns-invalid-code-with-extra-text.forwarded.test:.+: SERVFAIL \([0-9]+: Hello \[#\]\$%~ World\)' "$RUN_OUT"
+(! run varlinkctl call /run/systemd/resolve/io.systemd.Resolve io.systemd.Resolve.ResolveHostname '{"name" : "edns-invalid-code-with-extra-text.forwarded.test"}')
+grep -qF "io.systemd.Resolve.DNSError" "$RUN_OUT"
+grep -qE '{"rcode":2,"extendedDNSErrorCode":[0-9]+,"extendedDNSErrorMessage":"Hello \[#\]\$%~ World"}' "$RUN_OUT"
+journalctl --sync
+journalctl -u systemd-resolved.service --cursor-file="$JOURNAL_CURSOR" --grep "Server returned error: SERVFAIL \(\d+: Hello \[\#\]\\$%~ World\)"
+
 ### Test resolvectl show-cache
 run resolvectl show-cache
 run resolvectl show-cache --json=short
index 4aca9047a3d5a57e6740da4692d731898e641008..9ac3c8c68e3444da547cf1e9d3ba019fcfa8d9e6 100644 (file)
@@ -4,4 +4,4 @@ Description=A unit with multiple dashes
 Documentation=man:test
 
 [Service]
-ExecStart=/bin/true
+ExecStart=true
index a18c8980cf0ee7185077523c4e42758aaa8264e2..66f9c73ba6a6b9e174d6ad72de8023cb4fc6d83e 100755 (executable)
@@ -28,6 +28,15 @@ assert_eq() {(
     fi
 )}
 
+assert_le() {(
+    set +ex
+
+    if [[ "${1:?}" -gt "${2:?}" ]]; then
+        echo "FAIL: '$1' > '$2'" >&2
+        exit 1
+    fi
+)}
+
 assert_in() {(
     set +ex
 
index 1c2c9a8f65924effdbf8bc9b39df1add131eb8fb..3f10a29c473897cffa087d2cf98467ce275f590a 100755 (executable)
@@ -85,6 +85,7 @@ def main():
     pages = glob.glob(source_glob)
     pages = (p for p in pages
              if Path(p).name not in {
+                     'standard-conf.xml',
                      'systemd.directives.xml',
                      'systemd.index.xml',
                      'directives-template.xml'})
index 40cc3d10d35ff9f6621b77be051de44b1a008445..efd2eac58356d3cf085e2a28bfa7c377a88d7309 100644 (file)
@@ -321,6 +321,11 @@ units = [
           'conditions' : ['ENABLE_HOSTNAMED'],
           'symlinks' : ['dbus-org.freedesktop.hostname1.service'],
         },
+        {
+          'file' : 'systemd-hostnamed.socket',
+          'conditions' : ['ENABLE_HOSTNAMED'],
+          'symlinks' : ['sockets.target.wants/'],
+        },
         {
           'file' : 'systemd-hwdb-update.service.in',
           'conditions' : ['ENABLE_HWDB'],
index 794fac19a848b0afef6c005c78991f3fb25eda9a..ac72ccc82f1d25d254e3740fa5f05214b3bd1fff 100644 (file)
@@ -16,7 +16,7 @@ Before=sockets.target
 [Socket]
 ListenStream=/run/systemd/io.systemd.Credentials
 FileDescriptorName=varlink
-SocketMode=0600
+SocketMode=0666
 Accept=yes
 
 [Install]
index 31b45e0fa8b11c81ae55605ae6eee5a1e2e994d9..ab00c24b53b27554ef23c95b643fbf1989e29f39 100644 (file)
@@ -15,6 +15,7 @@ Documentation=man:machine-info(5)
 Documentation=man:org.freedesktop.hostname1(5)
 
 [Service]
+Type=notify
 BusName=org.freedesktop.hostname1
 CapabilityBoundingSet=CAP_SYS_ADMIN
 ExecStart={{LIBEXECDIR}}/systemd-hostnamed
@@ -22,7 +23,7 @@ IPAddressDeny=any
 LockPersonality=yes
 MemoryDenyWriteExecute=yes
 NoNewPrivileges=yes
-PrivateDevices=yes
+DeviceAllow=/dev/vsock r
 PrivateNetwork=yes
 PrivateTmp=yes
 ProtectProc=invisible
diff --git a/units/systemd-hostnamed.socket b/units/systemd-hostnamed.socket
new file mode 100644 (file)
index 0000000..8d0a06c
--- /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=Hostname Service Varlink Socket
+Documentation=man:systemd-hostnamed.service(8)
+Documentation=man:hostname(5)
+Documentation=man:machine-info(5)
+
+[Socket]
+ListenStream=/run/systemd/io.systemd.Hostname
+FileDescriptorName=varlink
+SocketMode=0666
index fc24a050981452bbd77d4cfe0755d3f60d040952..daa93776e178c3ea7910ad4c0c3ebf2cb8e0ac41 100644 (file)
@@ -13,6 +13,7 @@ Documentation=man:systemd-importd.service(8)
 Documentation=man:org.freedesktop.import1(5)
 
 [Service]
+Type=notify
 ExecStart={{LIBEXECDIR}}/systemd-importd
 BusName=org.freedesktop.import1
 KillMode=mixed
index 19383ae42397cd8a9569cc1dfa377e8c52375c17..4de89aa8ddd9563afc0611a0797d4e7008966fd2 100644 (file)
@@ -15,6 +15,7 @@ Documentation=man:vconsole.conf(5)
 Documentation=man:org.freedesktop.locale1(5)
 
 [Service]
+Type=notify
 BusName=org.freedesktop.locale1
 CapabilityBoundingSet=
 ExecStart={{LIBEXECDIR}}/systemd-localed
index 39dc0c2241964a2fac250cc3f7ea3411ed45c874..cc1b6be429c9632a30df88c4bad276dab5ec233f 100644 (file)
@@ -31,7 +31,7 @@ DeviceAllow=char-input rw
 DeviceAllow=char-tty rw
 DeviceAllow=char-vcs rw
 ExecStart={{LIBEXECDIR}}/systemd-logind
-FileDescriptorStoreMax=512
+FileDescriptorStoreMax=768
 IPAddressDeny=any
 LockPersonality=yes
 MemoryDenyWriteExecute=yes
index d87e1a4adcd9e9825cace02a2fbe62c6889ccd8a..f7d13d3084679924eeb32043deaf7e454ee23506 100644 (file)
@@ -21,6 +21,9 @@ Before=shutdown.target initrd-switch-root.target
 Type=oneshot
 RemainAfterExit=yes
 ExecStart={{LIBEXECDIR}}/systemd-network-generator
+ImportCredential=network.netdev.*
+ImportCredential=network.link.*
+ImportCredential=network.network.*
 
 [Install]
 WantedBy=sysinit.target
index 3608458aa57aff363e905107f34e64568e9dbfa2..32b6e9fa2ffed33562b84cf031503b63d25d99e1 100644 (file)
@@ -50,6 +50,7 @@ SystemCallErrorNumber=EPERM
 SystemCallFilter=@system-service
 Type=notify-reload
 User=systemd-network
+ImportCredential=network.wireguard.*
 {{SERVICE_WATCHDOG}}
 
 [Install]
index 00f6643ba7e676aeb07a2553d34239a8ed994dec..06c3306a6eb652b30d66142c1faf6858ff48e3cd 100644 (file)
@@ -14,6 +14,7 @@ Documentation=man:localtime(5)
 Documentation=man:org.freedesktop.timedate1(5)
 
 [Service]
+Type=notify
 BusName=org.freedesktop.timedate1
 CapabilityBoundingSet=CAP_SYS_TIME
 DeviceAllow=char-rtc r