]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
Merge pull request #30661 from rpigott/resolved-https-record
authorLuca Boccassi <bluca@debian.org>
Wed, 17 Jan 2024 16:20:35 +0000 (16:20 +0000)
committerGitHub <noreply@github.com>
Wed, 17 Jan 2024 16:20:35 +0000 (16:20 +0000)
resolved: support RFC 9460 SVCB and HTTPS records

665 files changed:
.github/labeler.yml
.github/workflows/build_test.sh
.github/workflows/mkosi.yml
.github/workflows/unit_tests.sh
TODO
coccinelle/mfree.cocci
coccinelle/mfree_return.cocci [deleted file]
docs/CONTAINER_INTERFACE.md
docs/ENVIRONMENT.md
docs/HACKING.md
hwdb.d/60-sensor.hwdb
man/homectl.xml
man/journalctl.xml
man/kernel-command-line.xml
man/loginctl.xml
man/org.freedesktop.hostname1.xml
man/org.freedesktop.login1.xml
man/org.freedesktop.network1.xml
man/org.freedesktop.systemd1.xml
man/pam_systemd.xml
man/rules/meson.build
man/sd_session_is_active.xml
man/standard-options.xml
man/systemd-analyze.xml
man/systemd-creds.xml
man/systemd-cryptenroll.xml
man/systemd-cryptsetup.xml
man/systemd-detect-virt.xml
man/systemd-firstboot.xml
man/systemd-id128.xml
man/systemd-network-generator.service.xml
man/systemd-path.xml
man/systemd-resolved.service.xml
man/systemd-sleep.conf.xml
man/systemd-ssh-generator.xml [new file with mode: 0644]
man/systemd-ssh-proxy.xml [new file with mode: 0644]
man/systemd-sysctl.service.xml
man/systemd-sysusers.xml
man/systemd-tmpfiles.xml
man/systemd-vconsole-setup.service.xml
man/systemd.link.xml
man/systemd.mount.xml
man/systemd.netdev.xml
man/systemd.network.xml
man/systemd.swap.xml
man/systemd.system-credentials.xml
man/systemd.unit.xml
man/systemd.v.xml
man/udev.conf.xml
man/varlinkctl.xml
meson.build
meson_options.txt
mkosi.conf
mkosi.images/system/mkosi.conf
mkosi.images/system/mkosi.conf.d/10-centos-fedora.conf
mkosi.images/system/mkosi.conf.d/10-debian-ubuntu.conf
mkosi.images/system/mkosi.conf.d/10-opensuse.conf
mkosi.images/system/mkosi.conf.d/10-ubuntu.conf
modprobe.d/systemd.conf
network/80-6rd-tunnel.link [new file with mode: 0644]
network/80-6rd-tunnel.network
network/80-container-vb.link [new file with mode: 0644]
network/80-container-vb.network
network/80-container-ve.link [new file with mode: 0644]
network/80-container-ve.network
network/80-container-vz.link [new file with mode: 0644]
network/80-vm-vt.link [new file with mode: 0644]
network/meson.build
po/pa.po
rules.d/60-persistent-storage.rules.in
shell-completion/bash/journalctl
shell-completion/bash/loginctl
shell-completion/bash/systemd-analyze
shell-completion/bash/systemd-id128
shell-completion/zsh/_journalctl
src/analyze/analyze-pcrs.c
src/analyze/analyze.c
src/basic/alloc-util.h
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/parse-util.c
src/basic/parse-util.h
src/basic/path-lookup.c
src/basic/process-util.c
src/basic/socket-util.c
src/basic/socket-util.h
src/basic/stat-util.h
src/basic/string-util.c
src/basic/strv.c
src/basic/strv.h
src/basic/time-util.c
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/user-util.c
src/basic/user-util.h
src/basic/virt.c
src/basic/virt.h
src/boot/bootctl-install.c
src/boot/bootctl-util.c
src/boot/bootctl-util.h
src/boot/efi/part-discovery.c
src/boot/measure.c
src/core/cgroup.c
src/core/cgroup.h
src/core/dbus-execute.c
src/core/dbus-manager.c
src/core/dbus-mount.c
src/core/dbus-unit.c
src/core/dynamic-user.c
src/core/exec-credential.c
src/core/exec-invoke.c
src/core/execute-serialize.c
src/core/execute.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-serialize.c
src/core/manager.c
src/core/namespace.c
src/core/path.c
src/core/service.c
src/core/unit-printf.c
src/core/unit.c
src/coredump/coredump.c
src/coredump/coredumpctl.c
src/creds/creds.c
src/cryptenroll/cryptenroll-pkcs11.c
src/cryptenroll/cryptenroll-tpm2.c
src/cryptenroll/cryptenroll.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/environment-d-generator/environment-d-generator.c
src/firstboot/firstboot.c
src/fsck/fsck.c
src/hibernate-resume/hibernate-resume-config.c
src/home/homectl.c
src/home/homed-home.c
src/home/homed-home.h
src/home/homed-manager-bus.c
src/home/homed-manager.c
src/home/homework-fscrypt.c
src/home/pam_systemd_home.c
src/hostname/hostnamectl.c
src/hostname/hostnamed.c
src/id128/id128.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/dhcp-client-id-internal.h
src/libsystemd-network/sd-dhcp-client-id.c
src/libsystemd-network/sd-dhcp-client.c
src/libsystemd-network/sd-dhcp-duid.c
src/libsystemd-network/sd-dhcp-lease.c
src/libsystemd-network/sd-dhcp-server-lease.c
src/libsystemd-network/sd-dhcp-server.c
src/libsystemd-network/test-dhcp-server.c
src/libsystemd/sd-bus/bus-message.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/libsystemd/sd-login/sd-login.c
src/libsystemd/sd-netlink/netlink-message-rtnl.c
src/libsystemd/sd-netlink/netlink-message.c
src/libsystemd/sd-netlink/netlink-types-rtnl.c
src/libsystemd/sd-netlink/netlink-util.c
src/libsystemd/sd-netlink/test-netlink.c
src/libsystemd/sd-network/network-util.c
src/libsystemd/sd-network/network-util.h
src/locale/localed.c
src/login/loginctl.c
src/login/logind-action.c
src/login/logind-action.h
src/login/logind-button.c
src/login/logind-core.c
src/login/logind-dbus.c
src/login/logind-session-dbus.c
src/login/logind-session.c
src/login/logind-session.h
src/login/logind-user.c
src/login/logind-user.h
src/login/logind.c
src/login/org.freedesktop.login1.conf
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/vxlan.c
src/network/netdev/wireguard.c
src/network/networkctl.c
src/network/networkd-address.c
src/network/networkd-address.h
src/network/networkd-dhcp-prefix-delegation.c
src/network/networkd-dhcp-server.c
src/network/networkd-dhcp4.c
src/network/networkd-dhcp6.c
src/network/networkd-ipv4ll.c
src/network/networkd-json.c
src/network/networkd-link.c
src/network/networkd-link.h
src/network/networkd-manager-bus.c
src/network/networkd-manager-varlink.c
src/network/networkd-manager.c
src/network/networkd-manager.h
src/network/networkd-ndisc.c
src/network/networkd-neighbor.c
src/network/networkd-neighbor.h
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-queue.h
src/network/networkd-radv.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-util.c
src/network/networkd-route-util.h
src/network/networkd-route.c
src/network/networkd-route.h
src/network/networkd-routing-policy-rule.c
src/network/networkd-state-file.c
src/network/networkd-wifi.c
src/network/wait-online/manager.c
src/network/wait-online/wait-online.c
src/nspawn/nspawn-bind-user.c
src/nspawn/nspawn-oci.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/battery-util.c
src/shared/boot-entry.c
src/shared/boot-entry.h
src/shared/bus-polkit.c
src/shared/bus-unit-util.c
src/shared/bus-util.c
src/shared/condition.c
src/shared/conf-parser.c
src/shared/conf-parser.h
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/efi-api.c
src/shared/find-esp.c
src/shared/fstab-util.c
src/shared/generator.c
src/shared/generator.h
src/shared/group-record.c
src/shared/hibernate-util.c
src/shared/install.c
src/shared/install.h
src/shared/json.c
src/shared/json.h
src/shared/logs-show.c
src/shared/meson.build
src/shared/mkfs-util.c
src/shared/module-util.c
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/selinux-util.c
src/shared/socket-netlink.c
src/shared/socket-netlink.h
src/shared/tests.h
src/shared/tpm2-util.c
src/shared/tpm2-util.h
src/shared/udev-util.c
src/shared/udev-util.h
src/shared/user-record.c
src/shared/userdb.c
src/shared/varlink-idl.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.Network.c
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
src/shared/vpick.h
src/shared/watchdog.c
src/shared/wifi-util.c
src/ssh-generator/20-systemd-ssh-proxy.conf.in [new file with mode: 0644]
src/ssh-generator/meson.build [new file with mode: 0644]
src/ssh-generator/ssh-generator.c [new file with mode: 0644]
src/ssh-generator/ssh-proxy.c [new file with mode: 0644]
src/systemctl/fuzz-systemctl-parse-argv.c
src/systemctl/systemctl-is-system-running.c
src/systemctl/systemctl-show.c
src/systemd/sd-netlink.h
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-socket-netlink.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/timedate/timedated.c
src/timesync/timesyncd-manager.c
src/tmpfiles/tmpfiles.c
src/udev/ata_id/ata_id.c
src/udev/net/link-config-gperf.gperf
src/udev/net/link-config.c
src/udev/net/link-config.h
src/udev/scsi_id/scsi_id.c
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/udevadm-test-builtin.c
src/udev/udevadm-test.c
src/udev/udevd.c
src/ukify/ukify.py
src/userdb/userdbctl.c
src/userdb/userdbd-manager.c
src/veritysetup/veritysetup-generator.c
src/veritysetup/veritysetup.c
src/vmspawn/vmspawn-util.c
src/vmspawn/vmspawn-util.h
src/vmspawn/vmspawn.c
src/vpick/vpick-tool.c
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-agent-bridge-port.network [new file with mode: 0644]
test/test-network/conf/25-agent-bridge.netdev [new file with mode: 0644]
test/test-network/conf/25-agent-bridge.network [new file with mode: 0644]
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-nexthop-test1.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/test-systemctl-enable.sh
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-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-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-43.sh
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.id128.sh
test/units/testsuite-74.network-generator.sh [new file with mode: 0755]
test/units/testsuite-74.ssh.sh [new file with mode: 0755]
test/units/testsuite-74.varlinkctl.sh
test/units/testsuite-75.sh
test/units/unit-with-multiple-dashes.service
tmpfiles.d/20-systemd-ssh-generator.conf.in [new file with mode: 0644]
tmpfiles.d/meson.build
units/initrd-parse-etc.service.in
units/meson.build
units/systemd-hibernate.service.in
units/systemd-hostnamed.service.in
units/systemd-hostnamed.socket [new file with mode: 0644]
units/systemd-hybrid-sleep.service.in
units/systemd-importd.service.in
units/systemd-localed.service.in
units/systemd-network-generator.service.in
units/systemd-networkd.service.in
units/systemd-suspend-then-hibernate.service.in
units/systemd-timedated.service.in

index 45b1ff49eebab0cf8e3a8833897b2d7642e00ae7..f74d680ce9d2920e960773a6a88bf77a9431568b 100644 (file)
@@ -7,6 +7,9 @@ apparmor:
 binfmt:
   - changed-files:
     - any-glob-to-any-file: '**/*binfmt*'
+bsod:
+  - changed-files:
+    - any-glob-to-any-file: '**/*bsod*'
 btrfs:
   - changed-files:
     - any-glob-to-any-file: '**/*btrfs*'
@@ -46,6 +49,9 @@ fstab-generator:
 growfs:
   - changed-files:
     - any-glob-to-any-file: '**/*growfs*'
+hibernate-resume:
+  - changed-files:
+    - any-glob-to-any-file: '**/*hibernate-resume*'
 hwdb:
   - changed-files:
     - any-glob-to-any-file: 'hwdb.d/**/*'
@@ -70,6 +76,9 @@ mkosi:
 network:
   - changed-files:
     - any-glob-to-any-file: ['src/libsystemd-network/**/*', 'src/network/**/*']
+nspawn:
+  - changed-files:
+    - any-glob-to-any-file: '**/*nspawn*'
 portable:
   - changed-files:
     - any-glob-to-any-file: 'src/portable/**/*'
@@ -184,6 +193,9 @@ util-lib:
 vconsole:
   - changed-files:
     - any-glob-to-any-file: '**/*vconsole*'
+vmspawn:
+  - changed-files:
+    - any-glob-to-any-file: '**/*vmspawn*'
 xdg-autostart:
   - changed-files:
     - any-glob-to-any-file: '**/**xdg-autostart-generator*'
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..dce515800cbf9d8891193e1a7c02ab69c339eeea 100644 (file)
@@ -76,21 +76,35 @@ 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
+        [Output]
+        # Build a disk image in CI as this logic is much more prone to breakage.
+        Format=disk
+
         [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 +112,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 +127,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]
index a5b98e089be106abe88b1341247d700e2b035fe3..dc5bf5dd4fcb62bf32f587757fe7ada9a0d3fc30 100755 (executable)
@@ -22,6 +22,7 @@ ADDITIONAL_DEPS=(
     python3-pefile
     python3-pyelftools
     python3-pyparsing
+    python3-pytest
     rpm
     zstd
 )
diff --git a/TODO b/TODO
index ae710eede00682b406ecb68c1313ff9c63db61af..90971fbcd0d909661f6664152d0fa68bf1abe6b1 100644 (file)
--- a/TODO
+++ b/TODO
@@ -132,6 +132,18 @@ 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.
+
+* machined: make machine registration available via varlink to simplify
+  nspawn/vmspawn, and to have an extensible way to register VM/machine metadata
+
+* ssh-proxy: add support for "ssh machine/foobar" to automatically connect to
+  machined registered machine "foobar". Requires updating machined to track CID
+  and unix-export dir of containers.
+
 * 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.
@@ -296,15 +308,6 @@ Features:
   the realized cgroup, to pin it (and later execute all cgroup operations over,
   once we drop cgroupv1 compat).
 
-* add new "systemd-ssh-generator", which allows basic ssh config via
-  credentials (host key). It generates sshd.socket for IP, but also
-  sshd-vsock.socket for listening on AF_VSOCK when running in a VM, and
-  sshd-unix.socket on AF_UNIX when running in a container. It also generates a
-  matching sshd.service file with a host key passed in on the cmdline via
-  credentials. Then, add a ssh_config drop-in that matches some suitable
-  hostname pattern and has a ProxyCommand set that allows connecting to any
-  local VM/container that way without any networking configured.
-
 * Varlinkification of the following command line tools, to open them up to
   other programs via IPC:
   - bootctl
@@ -476,7 +479,7 @@ Features:
 
 * 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 versin images if newer ones
+  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
@@ -824,10 +827,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
@@ -921,8 +920,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.
@@ -1214,14 +1211,6 @@ Features:
   .p7s is available in the image, use it to protect the system.attached copy
   with fs-verity, so that it cannot be tampered with
 
-* logind introduce two types of sessions: "heavy" and "light". The former would
-  be our current sessions. But the latter would be a new type of session that
-  is mostly the same but does not pull in user@.service or wait for it. Then,
-  allow configuration which type of session is desired via pam_systemd
-  parameters, and then make user@.service's session one of these "light" ones.
-  People could then choose to make FTP sessions and suchlike "light" if they
-  don't want the service manager to be started for that.
-
 * /etc/veritytab: allow that the roothash column can be specified as fs path
   including a path to an AF_UNIX path, similar to how we do things with the
   keys of /etc/crypttab. That way people can store/provide the roothash
@@ -1321,8 +1310,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
@@ -1829,8 +1819,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/
@@ -2325,14 +2313,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 7fa8558c7cf18a2f560322b7e36d9e9f9cc25bbe..dcecdecc3eb71d7f8d1d2317ba6cc4b1d1994c33 100644 (file)
@@ -273,6 +273,30 @@ care should be taken to avoid naming conflicts. `systemd` (and in particular
 7. The `/run/host/credentials/` directory is a good place to pass credentials
    into the container, using the `$CREDENTIALS_DIRECTORY` protocol, see above.
 
+8. The `/run/host/unix-export/` directory shall be writable from the container
+   payload, and is where container payload can bind `AF_UNIX` sockets in that
+   shall be *exported* to the host, so that the host can connect to them. The
+   container manager should bind mount this directory on the host side
+   (read-only ideally), so that the host can connect to contained sockets. This
+   is most prominently used by `systemd-ssh-generator` when run in such a
+   container to automatically bind an SSH socket into that directory, which
+   then can be used to connect to the container.
+
+9. The `/run/host/unix-export/ssh` `AF_UNIX` socket will be automatically bound
+   by `systemd-ssh-generator` in the container if possible, and can be used to
+   connect to the container.
+
+10. The `/run/host/userdb/` directory may be used to drop-in additional JSON
+    user records that `nss-systemd` inside the container shall include in the
+    system's user database. This is useful to make host users and their home
+    directories automatically accessible to containers in transitive
+    fashion. See `nss-systemd(8)` for details.
+
+11. The `/run/host/home/` directory may be used to bind mount host home
+    directories of users that shall be made available in the container to. This
+    may be used in combination with `/run/host/userdb/` above: one defines the
+    user record, the other contains the user's home directory.
+
 ## What You Shouldn't Do
 
 1. Do not drop `CAP_MKNOD` from the container. `PrivateDevices=` is a commonly
index b9a96be82c770be4d378c6dc11e582e74280c788..454a02991d948e5167576ee3694643a6647c0a14 100644 (file)
@@ -253,7 +253,8 @@ All tools:
   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.
+  the worker process is killed by the manager process. Defaults to 10 seconds,
+  maximum allowed is 5 hours.
 
 `udevadm` and `systemd-hwdb`:
 
@@ -609,3 +610,14 @@ 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.
+
+* `$SYSTEMD_VARLINK_LISTEN` – interpreted by some tools that provide a Varlink
+  service. Takes a file system path: if specified the tool will listen on an
+  `AF_UNIX` stream socket on the specified path in addition to whatever else it
+  would listen on.
index feec6b4537ee045b910aad26283fdefd8c8ef331..bd792092f798051022c2b7e7575a65b521ecf72d 100644 (file)
@@ -56,6 +56,23 @@ $ mkosi qemu
 Every time you rerun the `mkosi` command a fresh image is built, incorporating
 all current changes you made to the project tree.
 
+By default a directory image is built. This requires `virtiofsd` to be installed
+on the host. To build a disk image instead which does not require `virtiofsd`,
+add the following to `mkosi.local.conf`:
+
+```conf
+[Output]
+Format=disk
+```
+
+To boot in UEFI mode instead of using QEMU's direct kernel boot, add the following
+to `mkosi.local.conf`:
+
+```conf
+[Host]
+QemuFirmware=uefi
+```
+
 Putting this all together, here's a series of commands for preparing a patch
 for systemd:
 
index 1086940b78250225fc23a64adcc3a59942d7ddd6..b002be4ece902e98c18963d69a0c32a1d24899fe 100644 (file)
@@ -309,6 +309,10 @@ sensor:modalias:acpi:BOSC0200*:dmi:bvnAmericanMegatrendsInc.:bvrY13D_KB133.103:b
 sensor:modalias:acpi:BOSC0200*:dmi:*:svnHampoo*:pnC3W6_AP108_4GB:*
  ACCEL_MOUNT_MATRIX=-1, 0, 0; 0, 1, 0; 0, 0, 1
 
+# Chuwi Ubook X (CWI535)
+sensor:modalias:acpi:MXC6655*:dmi*:svnCHUWIInnovationAndTechnology*:pnUBookX:*
+ ACCEL_MOUNT_MATRIX=0, 0, -1; 1, 0, 0; 0, 1, 0
+
 #########################################
 # Connect
 #########################################
@@ -1021,6 +1025,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 65f6698ef496dbc9e3f9dec2cb44f4c6a4874b6b..0143b2ac4ebdb7b723215d3f7048e0d6fa87805b 100644 (file)
     <para>When invoked with the <command>firstboot</command> command, <command>homectl</command> supports the
     service credentials logic as implemented by
     <varname>ImportCredential=</varname>/<varname>LoadCredential=</varname>/<varname>SetCredential=</varname>
-    (see <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>1</manvolnum></citerefentry> for
+    (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'>
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 25ad770dcd085cefc75071fc91c487b436cc9cbd..ded41ffc36ceb7078bac7205049c42ad6eeb21f4 100644 (file)
         </listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><varname>systemd.ssh_auto=</varname></term>
+        <term><varname>systemd.ssh_listen=</varname></term>
+        <listitem>
+          <para>These parameters are interpreted by
+          <citerefentry><refentrytitle>systemd-ssh-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+          and may be used to control SSH sockets the system shall be reachable on.</para>
+
+          <xi:include href="version-info.xml" xpointer="v256"/>
+        </listitem>
+      </varlistentry>
+
       <varlistentry>
         <term><varname>systemd.volatile=</varname></term>
         <listitem>
index 8a093c6e2982d71dd4d474973242904a8adeced6..099de1dfe5e961fa8587a561186832bda0d2f924 100644 (file)
@@ -50,7 +50,8 @@
       <varlistentry>
         <term><command>list-sessions</command></term>
 
-        <listitem><para>List current sessions.</para></listitem>
+        <listitem><para>List current sessions. The JSON format output can be toggled using <option>--json=</option>
+        or <option>-j</option> option.</para></listitem>
       </varlistentry>
 
       <varlistentry>
       <varlistentry>
         <term><command>list-users</command></term>
 
-        <listitem><para>List currently logged in users.
-        </para></listitem>
+        <listitem><para>List currently logged in users. The JSON format output can be toggled using
+        <option>--json=</option> or <option>-j</option> option.</para></listitem>
       </varlistentry>
 
       <varlistentry>
       <varlistentry>
         <term><command>list-seats</command></term>
 
-        <listitem><para>List currently available seats on the local
-        system.</para></listitem>
+        <listitem><para>List currently available seats on the local system. The JSON format output can be
+        toggled using <option>--json=</option> or <option>-j</option> option.</para></listitem>
       </varlistentry>
 
       <varlistentry>
 
       <xi:include href="standard-options.xml" xpointer="no-pager" />
       <xi:include href="standard-options.xml" xpointer="no-legend" />
+      <xi:include href="standard-options.xml" xpointer="json" />
+      <xi:include href="standard-options.xml" xpointer="j" />
       <xi:include href="standard-options.xml" xpointer="help" />
       <xi:include href="standard-options.xml" xpointer="version" />
     </variablelist>
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 581a261c798826176e49acd12884d5ba8dda430f..9151926e64d5c1efb8300ec1912d42222f008ea8 100644 (file)
@@ -53,6 +53,7 @@ node /org/freedesktop/login1 {
       GetSeat(in  s seat_id,
               out o object_path);
       ListSessions(out a(susso) sessions);
+      ListSessionsEx(out a(sussussbto) sessions);
       ListUsers(out a(uso) users);
       ListSeats(out a(so) seats);
       ListInhibitors(out a(ssssuu) inhibitors);
@@ -257,7 +258,6 @@ node /org/freedesktop/login1 {
       readonly (st) ScheduledShutdown = ...;
       @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
       readonly b Docked = ...;
-      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
       readonly b LidClosed = ...;
       @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
       readonly b OnExternalPower = ...;
@@ -316,6 +316,8 @@ node /org/freedesktop/login1 {
 
     <variablelist class="dbus-method" generated="True" extra-ref="ListSessions()"/>
 
+    <variablelist class="dbus-method" generated="True" extra-ref="ListSessionsEx()"/>
+
     <variablelist class="dbus-method" generated="True" extra-ref="ListUsers()"/>
 
     <variablelist class="dbus-method" generated="True" extra-ref="ListSeats()"/>
@@ -550,8 +552,17 @@ node /org/freedesktop/login1 {
       is any.</para>
 
       <para><function>ListSessions()</function> returns an array of all current sessions. The structures in
-      the array consist of the following fields: session id, user id, user name, seat id, session object
-      path. If a session does not have a seat attached, the seat id field will be an empty string.</para>
+      the array consist of the following fields: <varname>session id</varname>, <varname>user id</varname>,
+      <varname>user name</varname>, <varname>seat id</varname>, and <varname>session object path</varname>.
+      If a session does not have a seat attached, the seat id field will be an empty string.</para>
+
+      <para><function>ListSessionsEx()</function> returns an array of all current sessions with more metadata
+      than <function>ListSessions()</function>. The structures in the array consist of the following fields:
+      <varname>session id</varname>, <varname>user id</varname>, <varname>user name</varname>,
+      <varname>seat id</varname>, <varname>leader pid</varname>, <varname>session class</varname>,
+      <varname>tty name</varname>, <varname>idle hint</varname>, <varname>idle hint monotonic timestamp</varname>,
+      and <varname>session object path</varname>. <varname>tty</varname> and <varname>seat id</varname> fields
+      could be empty, if the session has no associated tty or session has no seat attached, respectively.</para>
 
       <para><function>ListUsers()</function> returns an array of all currently logged in users. The
       structures in the array consist of the following fields: user id, user name, user object path.</para>
@@ -1560,8 +1571,9 @@ node /org/freedesktop/login1/session/1 {
       <para><function>PrepareForShutdownWithMetadata</function> and
       <function>CreateSessionWithPIDFD()</function> were added in version 255.</para>
       <para><function>Sleep()</function>,
-      <function>CanSleep()</function>, and
-      <varname>SleepOperation</varname> were added in version 256.</para>
+      <function>CanSleep()</function>,
+      <varname>SleepOperation</varname>, and
+      <function>ListSessionsEx()</function> were added in version 256.</para>
     </refsect2>
     <refsect2>
       <title>Session Objects</title>
index c6cadee177f134fe056ef6822f01e38b53862297..1d8ce0de8114e8888322fad0f6e00091351afcff 100644 (file)
@@ -87,6 +87,8 @@ node /org/freedesktop/network1 {
       readonly s OnlineState = '...';
       @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
       readonly t NamespaceId = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly u NamespaceNSID = ...;
   };
   interface org.freedesktop.DBus.Peer { ... };
   interface org.freedesktop.DBus.Introspectable { ... };
@@ -148,8 +150,6 @@ node /org/freedesktop/network1 {
 
     <!--property OnlineState is not documented!-->
 
-    <!--property NamespaceId is not documented!-->
-
     <!--Autogenerated cross-references for systemd.directives, do not edit-->
 
     <variablelist class="dbus-interface" generated="True" extra-ref="org.freedesktop.network1.Manager"/>
@@ -212,11 +212,24 @@ node /org/freedesktop/network1 {
 
     <variablelist class="dbus-property" generated="True" extra-ref="NamespaceId"/>
 
+    <variablelist class="dbus-property" generated="True" extra-ref="NamespaceNSID"/>
+
     <!--End of Autogenerated section-->
 
     <para>
       Provides information about the manager.
     </para>
+
+    <refsect2>
+      <title>Properties</title>
+
+      <para><varname>NamespaceId</varname> contains the inode number of the network namespace that the
+      network service runs in. A client may compare this with the inode number of its own network namespace
+      to verify whether the service manages the same network namespace.</para>
+
+      <para><varname>NamespaceNSID</varname> contains the "nsid" identifier the kernel maintains for the
+      network namespace, if there's one assigned.</para>
+    </refsect2>
   </refsect1>
 
   <refsect1>
@@ -584,5 +597,9 @@ $ gdbus introspect --system \
       <title>DHCPv6 Client Object</title>
       <para><varname>State</varname> was added in version 255.</para>
     </refsect2>
+    <refsect2>
+      <title>Manager Object</title>
+      <para><varname>NamespaceNSID</varname> was added in version 256.</para>
+    </refsect2>
   </refsect1>
 </refentry>
index 878bd0d99d95a428529138b70b7a006b886ff0cc..e2814b7e42e185e602c66ef96ea5c9692444f921 100644 (file)
@@ -2632,7 +2632,7 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
       enqueued and complete successfully. The key value pairs correspond (in lowercase) to the environment
       variables described in the <literal>Environment Variables Set or Propagated by the Service
       Manager</literal> section in
-      <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>1</manvolnum></citerefentry>. Note
+      <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry>. Note
       that new key value pair may be added at any time in future versions. Existing entries will not be
       removed.</para>
     </refsect2>
index c9a3ccbb5f29fecd65a9e37cfb1a827a493c75cb..2c3bbec5d8147531ed39e5c1bde2abb930802ac8 100644 (file)
       <varlistentry>
         <term><varname>class=</varname></term>
 
-        <listitem><para>Takes a string argument which sets the session class. The <varname>XDG_SESSION_CLASS</varname>
-        environment variable (see below) takes precedence. One of <literal>user</literal>, <literal>greeter</literal>,
-        <literal>lock-screen</literal> or <literal>background</literal>. See
-        <citerefentry><refentrytitle>sd_session_get_class</refentrytitle><manvolnum>3</manvolnum></citerefentry> for
-        details about the session class.</para>
+        <listitem><para>Takes a string argument which sets the session class. The
+        <varname>XDG_SESSION_CLASS</varname> environment variable (see below) takes precedence. See
+        <citerefentry><refentrytitle>sd_session_get_class</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+        for a way to query the class of a session. The following session classes are defined:</para>
+
+        <table>
+          <title>Session Classes</title>
+          <tgroup cols='2' align='left' colsep='1' rowsep='1'>
+            <colspec colname="name" />
+            <colspec colname="explanation" />
+            <thead>
+              <row>
+                <entry>Name</entry>
+                <entry>Explanation</entry>
+              </row>
+            </thead>
+            <tbody>
+              <row>
+                <entry><constant>user</constant></entry>
+                <entry>A regular interactive user session. This is the default class for sessions for which a TTY or X display is known at session registration time.</entry>
+              </row>
+              <row>
+                <entry><constant>user-early</constant></entry>
+                <entry>Similar to <literal>user</literal> but sessions of this class are not ordered after <filename>systemd-user-sessions.service</filename>, i.e. may be started before regular sessions are allowed to be established. This session class is the default for sessions of the root user that would otherwise qualify for the <constant>user</constant> class, see above. (Added in v256.)</entry>
+              </row>
+              <row>
+                <entry><constant>greeter</constant></entry>
+                <entry>Similar to <literal>user</literal> but for sessions that are spawned by a display manager ephemerally and which prompt the user for login credentials.</entry>
+              </row>
+              <row>
+                <entry><constant>lock-screen</constant></entry>
+                <entry>Similar to <literal>user</literal> but for sessions that are spawned by a display manager ephemerally and which show a lock screen that can be used to unlock locked user accounts or sessions.</entry>
+              </row>
+              <row>
+                <entry><constant>background</constant></entry>
+                <entry>Used for background sessions, such as those invoked by <command>cron</command> and similar tools. This is the default class for sessions for which no TTY or X display is known at session registration time.</entry>
+              </row>
+              <row>
+                <entry><constant>background-light</constant></entry>
+                <entry>Similar to <constant>background</constant>, but sessions of this class will not pull in the <filename>user@.service</filename> of the user, and thus possibly have no services of the user running. (Added in v256.)</entry>
+              </row>
+              <row>
+                <entry><constant>manager</constant></entry>
+                <entry>The <filename>user@.service</filename> service of the user is registered under this session class. (Added in v256.)</entry>
+              </row>
+              <row>
+                <entry><constant>manager-early</constant></entry>
+                <entry>Similar to <constant>manager</constant>, but for the root user. Compare with the <constant>user</constant> vs. <constant>user-early</constant> situation. (Added in v256.)</entry>
+              </row>
+            </tbody>
+          </tgroup>
+        </table>
 
         <xi:include href="version-info.xml" xpointer="v197"/></listitem>
       </varlistentry>
index 3592b862f718d1633d7bbc3800aa285358e08469..1e8747e4ef4ab971267595195a85a5e35fb279fd 100644 (file)
@@ -1054,6 +1054,8 @@ manpages = [
  ['systemd-socket-activate', '1', [], ''],
  ['systemd-socket-proxyd', '8', [], ''],
  ['systemd-soft-reboot.service', '8', [], ''],
+ ['systemd-ssh-generator', '8', [], ''],
+ ['systemd-ssh-proxy', '1', [], ''],
  ['systemd-stdio-bridge', '1', [], ''],
  ['systemd-storagetm.service', '8', ['systemd-storagetm'], 'ENABLE_STORAGETM'],
  ['systemd-stub',
@@ -1174,7 +1176,7 @@ manpages = [
  ['timesyncd.conf', '5', ['timesyncd.conf.d'], 'ENABLE_TIMESYNCD'],
  ['tmpfiles.d', '5', [], ''],
  ['udev', '7', [], ''],
- ['udev.conf', '5', [], ''],
+ ['udev.conf', '5', ['udev.conf.d'], ''],
  ['udev_device_get_syspath',
   '3',
   ['udev_device_get_action',
index e69ef59fd0eda0886be75beb913b26506c1b8393..747fab4c68ff2ceb7e99b8614a490e1d14409923 100644 (file)
     <citerefentry project='man-pages'><refentrytitle>free</refentrytitle><manvolnum>3</manvolnum></citerefentry>
     call after use.</para>
 
-    <para><function>sd_session_get_class()</function> may be used to
-    determine the class of the session identified by the specified
-    session identifier. The returned string is one of
-    <literal>user</literal>, <literal>greeter</literal>,
-    <literal>lock-screen</literal>, or <literal>background</literal>
-    and needs to be freed with the libc
-    <citerefentry project='man-pages'><refentrytitle>free</refentrytitle><manvolnum>3</manvolnum></citerefentry>
-    call after use.</para>
+    <para><function>sd_session_get_class()</function> may be used to determine the class of the session
+    identified by the specified session identifier. The returned string is one of <literal>user</literal>,
+    <literal>user-early</literal>, <literal>greeter</literal>, <literal>lock-screen</literal>,
+    <literal>background</literal>, <literal>background-light</literal>, <literal>manager</literal> or
+    <literal>manager-early</literal> and needs to be freed with the libc <citerefentry
+    project='man-pages'><refentrytitle>free</refentrytitle><manvolnum>3</manvolnum></citerefentry> call after
+    use.</para>
 
     <para><function>sd_session_get_desktop()</function> may be used to
     determine the brand of the desktop running on the session
index aec9a0d75091e457caa4f9051ce67d08817dc69b..87058ad657b52203bfd914b6234cd2310f12f406 100644 (file)
@@ -3,9 +3,7 @@
   "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd">
 <!-- SPDX-License-Identifier: LGPL-2.1-or-later -->
 
-<variablelist
-    xmlns:xi="http://www.w3.org/2001/XInclude">
-
+<variablelist>
   <varlistentry id='help'>
     <term><option>-h</option></term>
     <term><option>--help</option></term>
     </listitem>
   </varlistentry>
 
-  <varlistentry id='no-pager-255'>
-    <term><option>--no-pager</option></term>
-
-    <listitem>
-      <para>Do not pipe output into a pager.</para>
-
-      <xi:include href="version-info.xml" xpointer="v255"/>
-    </listitem>
-  </varlistentry>
-
   <varlistentry id='no-ask-password'>
     <term><option>--no-ask-password</option></term>
 
     off JSON output, the default).</para></listitem>
   </varlistentry>
 
+  <varlistentry id='j'>
+    <term><option>-j</option></term>
+
+    <listitem><para>Equivalent to <option>--json=pretty</option> if running on a terminal, and
+    <option>--json=short</option> otherwise.</para></listitem>
+  </varlistentry>
+
   <varlistentry id='signal'>
     <term><option>-s</option></term>
     <term><option>--signal=</option></term>
index 41c0e45df63198ee5fd9aa4ec2b11bcc94ab9997..db14038bfbcf10301222c2e6396f9226d9654d96 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
@@ -1107,8 +1107,10 @@ x86-64      native</programlisting>
       <varlistentry>
         <term><option>--root=<replaceable>PATH</replaceable></option></term>
 
-        <listitem><para>With <command>cat-files</command> and <command>verify</command>,
-        operate on files underneath the specified root path <replaceable>PATH</replaceable>.</para>
+        <listitem><para>With <command>cat-config</command>, <command>verify</command>,
+        <command>condition</command> and <command>security</command> when used with
+        <option>--offline=</option>, operate on files underneath the specified root path
+        <replaceable>PATH</replaceable>.</para>
 
         <xi:include href="version-info.xml" xpointer="v239"/></listitem>
       </varlistentry>
@@ -1116,8 +1118,10 @@ x86-64      native</programlisting>
       <varlistentry>
         <term><option>--image=<replaceable>PATH</replaceable></option></term>
 
-        <listitem><para>With <command>cat-files</command> and <command>verify</command>,
-        operate on files inside the specified image path <replaceable>PATH</replaceable>.</para>
+        <listitem><para>With <command>cat-config</command>, <command>verify</command>,
+        <command>condition</command> and <command>security</command> when used with
+        <option>--offline=</option>, operate on files inside the specified image path
+        <replaceable>PATH</replaceable>.</para>
 
         <xi:include href="version-info.xml" xpointer="v250"/></listitem>
       </varlistentry>
@@ -1513,6 +1517,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 232d199e0fc6f69ca21e9fdd21ccdb69b9ae6fa9..5f12dc4748a8b38f71a10a25788db65917f68192 100644 (file)
 
     <para><command>systemd-cryptsetup</command> supports the service credentials logic as implemented by
     <varname>ImportCredential=</varname>/<varname>LoadCredential=</varname>/<varname>SetCredential=</varname>
-    (see <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>1</manvolnum></citerefentry> for
+    (see <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry> for
     details). The following credentials are used by <literal>systemd-crypsetup@root.service</literal>
     (generated by <command>systemd-gpt-auto-generator</command>) when passed in:</para>
 
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 cd1c4b8874cf6de511a804eded2fac265fdae76b..a6ba07a3a8c2d8d7068565cb4884c09856e988ff 100644 (file)
 
     <para><command>systemd-firstboot</command> supports the service credentials logic as implemented by
     <varname>ImportCredential=</varname>/<varname>LoadCredential=</varname>/<varname>SetCredential=</varname>
-    (see <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>1</manvolnum></citerefentry> for
+    (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'>
index b1e6e245865eee8a530e086dfe090a34924df89d..e23532d65d883b0d3b5969000de13729930d590e 100644 (file)
         <xi:include href="version-info.xml" xpointer="v244"/></listitem>
       </varlistentry>
 
+      <xi:include href="standard-options.xml" xpointer="no-pager" />
+      <xi:include href="standard-options.xml" xpointer="no-legend" />
+      <xi:include href="standard-options.xml" xpointer="json" />
+      <xi:include href="standard-options.xml" xpointer="j" />
       <xi:include href="standard-options.xml" xpointer="help" />
       <xi:include href="standard-options.xml" xpointer="version" />
     </variablelist>
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 d5a14bc1719c0e3ed42487ab33297696a6649655..153a9bd0d561356d15096aa343ba59c497eea58f 100644 (file)
@@ -59,7 +59,7 @@
         <xi:include href="version-info.xml" xpointer="v215"/></listitem>
       </varlistentry>
 
-      <xi:include href="standard-options.xml" xpointer="no-pager-255"/>
+      <xi:include href="standard-options.xml" xpointer="no-pager"/>
       <xi:include href="standard-options.xml" xpointer="help" />
       <xi:include href="standard-options.xml" xpointer="version" />
     </variablelist>
index 94882b38bd93d49dfc05a38ca82a6d5b1ed93dab..813e5f398cbf8bf78894128f109c0c81e05a1825 100644 (file)
@@ -424,7 +424,7 @@ search foobar.com barbar.com
 
     <para><command>systemd-resolved</command> supports the service credentials logic as implemented by
     <varname>ImportCredential=</varname>/<varname>LoadCredential=</varname>/<varname>SetCredential=</varname>
-    (see <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>1</manvolnum></citerefentry> for
+    (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'>
index f7e753990a0ac28ce0a7fb2363fc75c8f257887c..f984bcb33fe8741f31430d43bccfffc034dda1ae 100644 (file)
         <term>suspend-then-hibernate</term>
 
         <listitem>
-          <para>A low power state where the system is initially suspended (the state is stored in
-          RAM). If the system supports low-battery alarms (ACPI _BTP), then the system will be woken up by
-          the ACPI low-battery signal and hibernated (the state is then stored on disk). Also, if not
-          interrupted within the timespan specified by <varname>HibernateDelaySec=</varname> or the estimated
-          timespan until the system battery charge level goes down to 5%, then the system will be woken up by the
-          RTC alarm and hibernated. The estimated timespan is calculated from the change of the battery
-          capacity level after the time specified by <varname>SuspendEstimationSec=</varname> or when
-          the system is woken up from the suspend.</para>
+          <para>A low power state where the system is initially suspended (the state is stored in RAM).
+          When the battery level is too low (less than 5%) or a certain timespan has passed, whichever
+          happens first, the system is automatically woken up and then hibernated. This establishes a balance
+          between speed and safety.</para>
+
+          <para>If the system has no battery, it would be hibernated after <varname>HibernateDelaySec=</varname>
+          has passed. If not set, then defaults to <literal>2h</literal>.</para>
+
+          <para>If the system has battery and <varname>HibernateDelaySec=</varname> is not set, low-battery
+          alarms (ACPI _BTP) are tried first for detecting battery percentage and wake up the system for hibernation.
+          If not available, or <varname>HibernateDelaySec=</varname> is set, the system would regularly wake
+          up to check the time and detect the battery percentage/discharging rate. The rate is used to
+          schedule the next detection. If that is also not available, <varname>SuspendEstimationSec=</varname>
+          is used as last resort.</para>
 
           <xi:include href="version-info.xml" xpointer="v239"/>
         </listitem>
           <para>The amount of time the system spends in suspend mode before the system is
           automatically put into hibernate mode. Only used by
           <citerefentry><refentrytitle>systemd-suspend-then-hibernate.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>.
-          If the system has a battery, then defaults to the estimated timespan until the system battery charge level goes down to 5%.
-          If the system has no battery, then defaults to 2h.</para>
+          Refer to <command>suspend-then-hibernate</command> for details on how this option interacts with
+          other options/system battery state.</para>
 
           <xi:include href="version-info.xml" xpointer="v239"/>
         </listitem>
 
         <listitem>
           <para>The RTC alarm will wake the system after the specified timespan to measure the system battery
-          capacity level and estimate battery discharging rate, which is used for estimating timespan until the system battery charge
-          level goes down to 5%. Only used by
+          capacity level and estimate battery discharging rate. Only used by
           <citerefentry><refentrytitle>systemd-suspend-then-hibernate.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>.
-          Defaults to 1h.</para>
+          Refer to <command>suspend-then-hibernate</command> for details on how this option interacts with
+          other options/system battery state.</para>
 
           <xi:include href="version-info.xml" xpointer="v253"/></listitem>
       </varlistentry>
diff --git a/man/systemd-ssh-generator.xml b/man/systemd-ssh-generator.xml
new file mode 100644 (file)
index 0000000..2f4f2a6
--- /dev/null
@@ -0,0 +1,141 @@
+<?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" [
+<!ENTITY % entities SYSTEM "custom-entities.ent" >
+%entities;
+]>
+<!-- SPDX-License-Identifier: LGPL-2.1-or-later -->
+<refentry id="systemd-ssh-generator"
+          xmlns:xi="http://www.w3.org/2001/XInclude">
+
+  <refentryinfo>
+    <title>systemd-ssh-generator</title>
+    <productname>systemd</productname>
+  </refentryinfo>
+
+  <refmeta>
+    <refentrytitle>systemd-ssh-generator</refentrytitle>
+    <manvolnum>8</manvolnum>
+  </refmeta>
+
+  <refnamediv>
+    <refname>systemd-ssh-generator</refname>
+    <refpurpose>Generator for binding a socket-activated SSH server to local <constant>AF_VSOCK</constant>
+    and <constant>AF_UNIX</constant> sockets</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv>
+    <para><filename>/usr/lib/systemd/system-generators/systemd-ssh-generator</filename></para>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>Description</title>
+
+    <para><command>systemd-ssh-generator</command> binds a socket-activated SSH server to local
+    <constant>AF_VSOCK</constant> and <constant>AF_UNIX</constant> sockets under certain conditions. It only
+    has an effect if the <citerefentry
+    project="man-pages"><refentrytitle>sshd</refentrytitle><manvolnum>8</manvolnum></citerefentry> binary is
+    installed. Specifically, it does the following:</para>
+
+    <itemizedlist>
+      <listitem><para>If invoked in a VM with <constant>AF_VSOCK</constant> support, a socket-activated SSH
+      per-connection service is bound to <constant>AF_VSOCK</constant> port 22.</para></listitem>
+
+      <listitem><para>If invoked in a container environment with a writable directory
+      <filename>/run/host/unix-export/</filename> pre-mounted it binds SSH to an <constant>AF_UNIX</constant>
+      socket <filename>/run/host/unix-export/ssh</filename>. The assumption is that this directory is bind
+      mounted to the host side as well, and can be used to connect to the container from there. See <ulink
+      url="https://systemd.io/CONTAINER_INTERFACE">Container Interface</ulink> for more information about
+      this interface.</para></listitem>
+
+      <listitem><para>A local <constant>AF_UNIX</constant> socket
+      <filename>/run/ssh-unix-local/socket</filename> is also bound, unconditionally. This may be used for
+      SSH communication from the host to itself, without involving networking, for example to traverse
+      security boundaries safely and with secure authentication.</para></listitem>
+
+      <listitem><para>Additional <constant>AF_UNIX</constant> and <constant>AF_VSOCK</constant> sockets are
+      optionally bound, based on the <varname>systemd.ssh_listen=</varname> kernel command line option or the
+      <filename>ssh.listen</filename> system credential (see below).</para></listitem>
+    </itemizedlist>
+
+    <para>See
+    <citerefentry><refentrytitle>systemd-ssh-proxy</refentrytitle><manvolnum>1</manvolnum></citerefentry> for
+    details on how to connect to these sockets via the <command>ssh</command> client.</para>
+
+    <para>The generator will use a packaged <filename>sshd@.service</filename> service template file if one
+    exists, and otherwise generate a suitable service template file.</para>
+
+    <para><filename>systemd-ssh-generator</filename> implements
+    <citerefentry><refentrytitle>systemd.generator</refentrytitle><manvolnum>7</manvolnum></citerefentry>.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>Kernel Command Line</title>
+
+    <para><filename>systemd-ssh-generator</filename> understands the following
+    <citerefentry><refentrytitle>kernel-command-line</refentrytitle><manvolnum>7</manvolnum></citerefentry>
+    parameters:</para>
+
+    <variablelist class='kernel-commandline-options'>
+      <varlistentry>
+        <term><varname>systemd.ssh_auto=</varname></term>
+
+        <listitem><para>This option takes an optional boolean argument, and defaults to yes. If enabled, the
+        automatic binding to the <constant>AF_VSOCK</constant> and <constant>AF_UNIX</constant> sockets
+        listed above is done. If disable, this is not done, except for those explicitly requested via
+        <varname>systemd.ssh_listen=</varname> on the kernel command line or via the
+        <varname>ssh.listen</varname> system credential.</para>
+
+        <xi:include href="version-info.xml" xpointer="v256"/></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>systemd.ssh_listen=</varname></term>
+
+        <listitem><para>This option configures an additional socket to bind SSH to. It may be used multiple
+        times to bind multiple sockets. The syntax should follow the one of <varname>ListenStream=</varname>,
+        see
+        <citerefentry><refentrytitle>systemd.socket</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+        for details. This functionality supports all socket families systemd supports, including
+        <constant>AF_INET</constant> and <constant>AF_INET6</constant>.</para>
+
+        <xi:include href="version-info.xml" xpointer="v256"/></listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+
+  <refsect1>
+    <title>Credentials</title>
+
+    <para><command>systemd-ssh-generator</command> supports the system credentials logic. The following
+    credentials are used when passed in:</para>
+
+    <variablelist class='system-credentials'>
+      <varlistentry>
+        <term><varname>ssh.listen</varname></term>
+
+        <listitem><para>This credential should be a text file, with each line referencing one additional
+        socket to bind SSH to. The syntax should follow the one of <varname>ListenStream=</varname>, see
+        <citerefentry><refentrytitle>systemd.socket</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+        for details. This functionality supports all socket families systemd supports, including
+        <constant>AF_INET</constant> and <constant>AF_INET6</constant>.</para>
+
+        <xi:include href="version-info.xml" xpointer="v256"/></listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+
+  <refsect1>
+    <title>See Also</title>
+    <para><simplelist type="inline">
+      <member><citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
+      <member><citerefentry><refentrytitle>kernel-command-line</refentrytitle><manvolnum>7</manvolnum></citerefentry></member>
+      <member><citerefentry><refentrytitle>systemd.system-credentials</refentrytitle><manvolnum>7</manvolnum></citerefentry></member>
+      <member><citerefentry project="man-pages"><refentrytitle>vsock</refentrytitle><manvolnum>7</manvolnum></citerefentry></member>
+      <member><citerefentry project="man-pages"><refentrytitle>unix</refentrytitle><manvolnum>7</manvolnum></citerefentry></member>
+      <member><citerefentry project="man-pages"><refentrytitle>ssh</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
+      <member><citerefentry project="man-pages"><refentrytitle>sshd</refentrytitle><manvolnum>8</manvolnum></citerefentry></member>
+    </simplelist></para>
+  </refsect1>
+</refentry>
diff --git a/man/systemd-ssh-proxy.xml b/man/systemd-ssh-proxy.xml
new file mode 100644 (file)
index 0000000..d9615ff
--- /dev/null
@@ -0,0 +1,116 @@
+<?xml version='1.0'?> <!--*-nxml-*-->
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
+  "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd">
+<!-- SPDX-License-Identifier: LGPL-2.1-or-later -->
+
+<refentry id="systemd-ssh-proxy"
+    xmlns:xi="http://www.w3.org/2001/XInclude">
+
+  <refentryinfo>
+    <title>systemd-ssh-proxy</title>
+    <productname>systemd</productname>
+  </refentryinfo>
+
+  <refmeta>
+    <refentrytitle>systemd-ssh-proxy</refentrytitle>
+    <manvolnum>1</manvolnum>
+  </refmeta>
+
+  <refnamediv>
+    <refname>systemd-ssh-proxy</refname>
+    <refpurpose>SSH client plugin for connecting to <constant>AF_VSOCK</constant> and
+    <constant>AF_UNIX</constant> sockets</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv>
+    <programlisting>
+Host unix/* vsock/*
+    ProxyCommand /usr/lib/systemd/systemd-ssh-proxy %h %p
+    ProxyUseFdpass yes
+</programlisting>
+    <cmdsynopsis>
+      <command>/usr/lib/systemd/systemd-ssh-proxy</command> <arg>ADDRESS</arg> <arg>PORT</arg>
+    </cmdsynopsis>
+  </refsynopsisdiv>
+
+
+  <refsect1>
+    <title>Description</title>
+
+    <para><command>systemd-ssh-proxy</command> is a small "proxy" plugin for the <citerefentry
+    project="man-pages"><refentrytitle>ssh</refentrytitle><manvolnum>1</manvolnum></citerefentry>
+    tool that allows connecting to <constant>AF_UNIX</constant> and <constant>AF_VSOCK</constant> sockets. It
+    implements the interface defined by <filename>ssh</filename>'s <varname>ProxyCommand</varname>
+    configuration option. It's supposed to be used with an <citerefentry
+    project="man-pages"><refentrytitle>ssh_config</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+    configuration fragment like the following:</para>
+
+    <programlisting>
+Host unix/* vsock/*
+    ProxyCommand /usr/lib/systemd/systemd-ssh-proxy %h %p
+    ProxyUseFdpass yes
+    CheckHostIP no
+
+Host .host
+    ProxyCommand /usr/lib/systemd/systemd-ssh-proxy unix/run/ssh-unix-local/socket %p
+    ProxyUseFdpass yes
+    CheckHostIP no
+</programlisting>
+
+    <para>A configuration fragment along these lines is by default installed into
+    <filename>/etc/ssh/ssh_config.d/20-systemd-ssh-proxy.conf.in</filename>.</para>
+
+    <para>With this in place, SSH connections to host string <literal>unix/</literal> followed by an absolute
+    <constant>AF_UNIX</constant> file system path to a socket will be directed to the specified socket, which
+    must be of type <constant>SOCK_STREAM</constant>. Similar, SSH connections to <literal>vsock/</literal>
+    followed by an <constant>AF_VSOCK</constant> CID will result in an SSH connection made to that
+    CID. Moreover connecting to <literal>.host</literal> will connect to the local host via SSH, without
+    involving networking.</para>
+
+    <para>This tool is supposed to be used together with
+    <citerefentry><refentrytitle>systemd-ssh-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+    which when run inside a VM or container will bind SSH to suitable
+    addresses. <command>systemd-ssh-generator</command> is supposed to run in the container of VM guest, and
+    <command>systemd-ssh-proxy</command> is run on the host, in order to connect to the container or VM
+    guest.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>Exit status</title>
+
+    <para>On success, 0 is returned, a non-zero failure code
+    otherwise.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>Examples</title>
+
+    <example>
+      <title>Talk to a local VM with CID 4711</title>
+
+      <programlisting>ssh vsock/4711</programlisting>
+    </example>
+
+    <example>
+      <title>Talk to the local host via ssh</title>
+
+      <programlisting>ssh .host</programlisting>
+
+      <para>or equivalent:</para>
+
+      <programlisting>ssh unix/run/ssh-unix-local/socket</programlisting>
+    </example>
+  </refsect1>
+
+  <refsect1>
+    <title>See Also</title>
+    <para><simplelist type="inline">
+      <member><citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
+      <member><citerefentry><refentrytitle>systemd-ssh-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry></member>
+      <member><citerefentry project="man-pages"><refentrytitle>vsock</refentrytitle><manvolnum>7</manvolnum></citerefentry></member>
+      <member><citerefentry project="man-pages"><refentrytitle>unix</refentrytitle><manvolnum>7</manvolnum></citerefentry></member>
+      <member><citerefentry project="man-pages"><refentrytitle>ssh</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
+      <member><citerefentry project="man-pages"><refentrytitle>sshd</refentrytitle><manvolnum>8</manvolnum></citerefentry></member>
+    </simplelist></para>
+  </refsect1>
+</refentry>
index 950e0c82ef1e23c2506ac0cd49aa5113038aa409..720beed8eec23c43068ff205140b8132376b9d08 100644 (file)
@@ -91,7 +91,7 @@
 
     <para><command>systemd-sysctl</command> supports the service credentials logic as implemented by
     <varname>ImportCredential=</varname>/<varname>LoadCredential=</varname>/<varname>SetCredential=</varname>
-    (see <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>1</manvolnum></citerefentry> for
+    (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'>
index d398c5d361a86f5cb9a2c118e7e9b27955dbb97d..548ce24e4edce25a9a7bad393a83adec42e98ef9 100644 (file)
 
     <para><command>systemd-sysusers</command> supports the service credentials logic as implemented by
     <varname>ImportCredential=</varname>/<varname>LoadCredential=</varname>/<varname>SetCredential=</varname>
-    (see <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>1</manvolnum></citerefentry> for
+    (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'>
       <member><citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
       <member><citerefentry><refentrytitle>sysusers.d</refentrytitle><manvolnum>5</manvolnum></citerefentry></member>
       <member><ulink url="https://systemd.io/UIDS-GIDS">Users, Groups, UIDs and GIDs on systemd systems</ulink></member>
-      <member><citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
+      <member><citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry></member>
       <member><citerefentry project='man-pages'><refentrytitle>mkpasswd</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
     </simplelist></para>
   </refsect1>
index 6bf6694d2d7a2a9fa098f852afffee9b6b7abe78..c30a046f72689168a073278ba6dc9d223d6b1868 100644 (file)
 
     <para><command>systemd-tmpfiles</command> supports the service credentials logic as implemented by
     <varname>ImportCredential=</varname>/<varname>LoadCredential=</varname>/<varname>SetCredential=</varname>
-    (see <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>1</manvolnum></citerefentry> for
+    (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'>
index 1fa3abe53e3a290ece05069825e780662f567646..87cb9e4777bb49cb26251b1f89f9cc1892243bde 100644 (file)
@@ -58,7 +58,7 @@
 
     <para><command>systemd-vconsole-setup</command> supports the service credentials logic as implemented by
     <varname>ImportCredential=</varname>/<varname>LoadCredential=</varname>/<varname>SetCredential=</varname>
-    (see <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>1</manvolnum></citerefentry> for
+    (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'>
index 4d99cd88f6becd8a41dedf65191efb3604bed0b0..3e98e4ddba0f202fb23fbeb1ced39d47e01b7af0 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>
                 <literal>60-foo.link.wol.password</literal>), and if the credential not found, then
                 read from <literal>wol.password</literal>. See
                 <varname>ImportCredential=</varname>/<varname>LoadCredential=</varname>/<varname>SetCredential=</varname> in
-                <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>1</manvolnum></citerefentry>
+                <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry>
                 for details. The password in the credential, must be 6 bytes in hex format with each
                 byte separated by a colon (<literal>:</literal>) like an Ethernet MAC address, e.g.,
                 <literal>aa:bb:cc:dd:ee:ff</literal>.</para>
     </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 76f9f4d042c584f33ad563eb3d9f90ad4fdeff17..35c897af398efdcf94091db15e64bc7a9669fe6a 100644 (file)
       <varlistentry>
         <term><varname>RequiredForOnline=</varname></term>
         <listitem>
-          <para>Takes a boolean or a minimum operational state and an optional maximum operational
-          state. Please see
+          <para>Takes a boolean, a minimum operational state (e.g., <literal>carrier</literal>), or a range
+          of operational state separated with a colon (e.g., <literal>degraded:routable</literal>).
+          Please see
           <citerefentry><refentrytitle>networkctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>
           for possible operational states. When <literal>yes</literal>, the network is deemed required
           when determining whether the system is online (including when running
           minimum and maximum operational state required for the network interface to be considered
           online.</para>
 
+          <para>When <literal>yes</literal> is specified for a CAN device,
+          <command>systemd-networkd-wait-online</command> deems that the interface is online when its
+          operational state becomes <literal>carrier</literal>. For an interface with other type, e.g.
+          <literal>ether</literal>, the interface is deened online when its online state is
+          <literal>degraded</literal> or <literal>routable</literal>.</para>
+
           <para>Defaults to <literal>yes</literal> when <varname>ActivationPolicy=</varname> is not
           set, or set to <literal>up</literal>, <literal>always-up</literal>, or
           <literal>bound</literal>. Defaults to <literal>no</literal> when
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..c1c8e97f0c33ca59af06aa0e767e3d95379d2dfa 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>
         </listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><varname>ssh.listen</varname></term>
+        <listitem>
+          <para>May be used to configure SSH sockets the system shall be reachable on. See
+          <citerefentry><refentrytitle>systemd-ssh-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+          for details.</para>
+
+          <xi:include href="version-info.xml" xpointer="v256"/>
+        </listitem>
+      </varlistentry>
+
       <varlistentry>
         <term><varname>sysusers.extra</varname></term>
         <listitem>
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>
index 6cc6122a34072ec474fc4bdad1a67964ec8ecbba..b29d32f69a5b4f515cf40e672497ca7189bc72c1 100644 (file)
@@ -97,7 +97,7 @@
       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>Preceeding the use counters (if they are specified), an optional CPU architecture
+      <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
index 4ebde474a3058289c9a0cce51a2cf568bd6fc3fa..054ab2d13958b32f775e22685f919d76bd8bee02 100644 (file)
 
   <refnamediv>
     <refname>udev.conf</refname>
+    <refname>udev.conf.d</refname>
     <refpurpose>Configuration for device event managing daemon</refpurpose>
   </refnamediv>
 
   <refsynopsisdiv>
-    <para><filename>/etc/udev/udev.conf</filename></para>
+    <para>
+      <simplelist>
+        <member><filename>/etc/udev/udev.conf</filename></member>
+        <member><filename>/run/udev/udev.conf</filename></member>
+        <member><filename>/usr/lib/udev/udev.conf</filename></member>
+        <member><filename>/etc/udev/udev.conf.d/*.conf</filename></member>
+        <member><filename>/run/udev/udev.conf.d/*.conf</filename></member>
+        <member><filename>/usr/lib/udev/udev.conf.d/*.conf</filename></member>
+      </simplelist>
+    </para>
   </refsynopsisdiv>
 
   <refsect1>
     <title>Description</title>
 
+    <para>These files contain configuration options for
+      <citerefentry><refentrytitle>systemd-udevd</refentrytitle><manvolnum>8</manvolnum></citerefentry>.
+      The syntax of these files is very simple: a list
+      of assignments, one per line.
+      All empty lines or lines beginning with <literal>#</literal> are
+      ignored.
+    </para>
     <para>
-      <citerefentry><refentrytitle>systemd-udevd</refentrytitle><manvolnum>8</manvolnum></citerefentry>
-      expects its main configuration file at
-      <filename>/etc/udev/udev.conf</filename>. It consists of a set
-      of variables allowing the user to override default udev
-      values. All empty lines or lines beginning with '#' are
-      ignored. The following variables can be set:
+      The following options can be set:
     </para>
 
     <variablelist class='config-directives'>
           <option>err</option>, <option>info</option> and
           <option>debug</option>.</para>
 
+          <note>
+            <para>This option is also honored by
+                <citerefentry><refentrytitle>udevadm</refentrytitle><manvolnum>8</manvolnum></citerefentry>.
+            </para>
+          </note>
+
           <xi:include href="version-info.xml" xpointer="v216"/>
         </listitem>
       </varlistentry>
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 d2d255391d523f009976acbb287a4e0f71ae4fd1..53a3d966ff4b30eb98fe53b25d6424a6a5aa786a 100644 (file)
@@ -199,6 +199,11 @@ if pamconfdir == ''
         pamconfdir = prefixdir / 'lib/pam.d'
 endif
 
+sshconfdir = get_option('sshconfdir')
+if sshconfdir == ''
+        sshconfdir = sysconfdir / 'ssh/ssh_config.d'
+endif
+
 sshdconfdir = get_option('sshdconfdir')
 if sshdconfdir == ''
         sshdconfdir = sysconfdir / 'ssh/sshd_config.d'
@@ -235,6 +240,7 @@ conf.set_quoted('PREFIX_NOSLASH',                             prefixdir_noslash)
 conf.set_quoted('RANDOM_SEED',                                randomseeddir / 'random-seed')
 conf.set_quoted('RANDOM_SEED_DIR',                            randomseeddir)
 conf.set_quoted('RC_LOCAL_PATH',                              get_option('rc-local'))
+conf.set_quoted('SSHCONFDIR',                                 sshconfdir)
 conf.set_quoted('SSHDCONFDIR',                                sshdconfdir)
 conf.set_quoted('SYSCONF_DIR',                                sysconfdir)
 conf.set_quoted('SYSCTL_DIR',                                 sysctldir)
@@ -2206,6 +2212,7 @@ subdir('src/shutdown')
 subdir('src/sleep')
 subdir('src/socket-activate')
 subdir('src/socket-proxy')
+subdir('src/ssh-generator')
 subdir('src/stdio-bridge')
 subdir('src/sulogin-shell')
 subdir('src/sysctl')
@@ -2688,7 +2695,8 @@ summary({
         'SysV rc?.d directories' :          sysvrcnd_path,
         'PAM modules directory' :           pamlibdir,
         'PAM configuration directory' :     pamconfdir,
-        'ssh configuration directory' :     sshdconfdir,
+        'ssh server configuration directory' : sshdconfdir,
+        'ssh client configuration directory' : sshconfdir,
         'libcryptsetup plugins directory' : libcryptsetup_plugins_dir,
         'RPM macros directory' :            rpmmacrosdir,
         'modprobe.d directory' :            modprobedir,
index c677c7f4202f194e49a00d1cff39b206fdbdbfed..b74f949189635f0d802d892e56ffda79ca1ff1e5 100644 (file)
@@ -211,6 +211,8 @@ option('pamlibdir', type : 'string',
        description : 'directory for PAM modules')
 option('pamconfdir', type : 'string',
        description : 'directory for PAM configuration ["no" disables]')
+option('sshconfdir', type : 'string',
+       description : 'directory for SSH client configuration ["no" disables]')
 option('sshdconfdir', type : 'string',
        description : 'directory for SSH server configuration ["no" disables]')
 option('libcryptsetup-plugins-dir', type : 'string',
index b02c24cdd90af5bd01b538925af4bc6f34a5717b..9ec4faf1225bfdc575e20f48389fa57364e7d17a 100644 (file)
@@ -18,6 +18,7 @@ Environment=ASAN_OPTIONS=verify_asan_link_order=false
 @Incremental=yes
 @QemuMem=2G
 @RuntimeSize=8G
+ToolsTreePackages=virtiofsd
 KernelCommandLineExtra=systemd.crash_shell
                        systemd.log_level=debug
                        systemd.log_ratelimit_kmsg=0
index 6948f8eb7ce8eb65b6a1f4e2220b0997b26ae4db..9646bbefde048fa46fae109f0990e455028b5b1c 100644 (file)
@@ -3,6 +3,9 @@
 [Config]
 Dependencies=base
 
+[Output]
+@Format=directory
+
 [Content]
 Autologin=yes
 BaseTrees=../../mkosi.output/base
index 67d46432d40f81dd20c675b161f67bc72fc30280..871186d5ca9a4140742612652635c3f76c3a95dd 100644 (file)
@@ -18,6 +18,7 @@ Packages=
         libcap-ng-utils
         netcat
         openssh-server
+        openssh-clients
         p11-kit
         pam
         passwd
index 588f833c8f4a9b8c25ff1f63492c4b6c88ed01d4..348bdb2992c334eaa44c3aff68712f6353b8d1db 100644 (file)
@@ -18,6 +18,7 @@ Packages=
         libcap-ng-utils
         netcat-openbsd
         openssh-server
+        openssh-client
         passwd
         policykit-1
         procps
index 60a2b6dbfc5accba722e98cb9ccaf6473196c19c..71434b4560b50ae6615dfd74a53d6fa6f6acae30 100644 (file)
@@ -14,6 +14,7 @@ Packages=
         kernel-kvmsmall
         libcap-ng-utils
         openssh-server
+        openssh-clients
         python3
         python3-pefile
         python3-psutil
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 652254155c265c820ba0b3175e24a3cb7cd003c2..e6499a0d612aea4c15c792763d380b098617a433 100644 (file)
@@ -18,3 +18,7 @@ options bonding max_bonds=0
 # Do the same for dummy0.
 
 options dummy numdummies=0
+
+# Do the same for ifb0.
+
+options ifb numifbs=0
diff --git a/network/80-6rd-tunnel.link b/network/80-6rd-tunnel.link
new file mode 100644 (file)
index 0000000..22fc3d6
--- /dev/null
@@ -0,0 +1,24 @@
+# SPDX-License-Identifier: MIT-0
+#
+# This config file is installed as part of systemd.
+# It may be freely copied and edited (following the MIT No Attribution license).
+#
+# To make local modifications, one of the following methods may be used:
+# 1. add a drop-in file that extends this file by creating the
+#    /etc/systemd/network/80-6rd-tunnel.link.d/ directory and creating a
+#    new .conf file there.
+# 2. copy this file into /etc/systemd/network/ or one of the other paths checked
+#    by systemd-udevd and edit it there.
+# This file should not be edited in place, because it'll be overwritten on upgrades.
+
+# This .link file matches 6rd-* SIT devices and marks them as managed by
+# systemd-networkd.
+
+[Match]
+Kind=sit
+OriginalName=6rd-*
+
+[Link]
+NamePolicy=keep
+MACAddressPolicy=persistent
+Property=ID_NET_MANAGED_BY=io.systemd.Network
index 5374ec5b073a18a913f1b163c85801ab9bd930ad..96bf526f76585e47145444c676851ee473ce1a0b 100644 (file)
@@ -7,11 +7,11 @@
 # 1. add a drop-in file that extends this file by creating the
 #    /etc/systemd/network/80-6rd-tunnel.network.d/ directory and creating a
 #    new .conf file there.
-# 2. copy this file into /etc/systemd/network or one of the other paths checked
+# 2. copy this file into /etc/systemd/network/ or one of the other paths checked
 #    by systemd-networkd and edit it there.
 # This file should not be edited in place, because it'll be overwritten on upgrades.
 
-# This network file matches 6rd-* SIT devices which is automatically created by
+# This .network file matches 6rd-* SIT devices which is automatically created by
 # systemd-networkd when DHCPv4 6RD option is received.
 
 [Match]
diff --git a/network/80-container-vb.link b/network/80-container-vb.link
new file mode 100644 (file)
index 0000000..2bcea13
--- /dev/null
@@ -0,0 +1,24 @@
+# SPDX-License-Identifier: MIT-0
+#
+# This config file is installed as part of systemd.
+# It may be freely copied and edited (following the MIT No Attribution license).
+#
+# To make local modifications, one of the following methods may be used:
+# 1. add a drop-in file that extends this file by creating the
+#    /etc/systemd/network/80-container-vb.link.d/ directory and creating a
+#    new .conf file there.
+# 2. copy this file into /etc/systemd/network/ or one of the other paths checked
+#    by systemd-udevd and edit it there.
+# This file should not be edited in place, because it'll be overwritten on upgrades.
+
+# This .link file matches the host-side of the virtual Ethernet link created
+# by systemd-nspawn's --network-veth switch with --network-bridge= or
+# --network-zone= switch. See systemd-nspawn(1) for details.
+
+[Match]
+Kind=veth
+OriginalName=vb-*
+
+[Link]
+NamePolicy=keep
+Property=ID_NET_MANAGED_BY=io.systemd.Network
index 806f834e18834d7d1f50f66d78a4b0072092574a..6d5ea41795681bd5577cf29920159b43f1717876 100644 (file)
@@ -7,13 +7,13 @@
 # 1. add a drop-in file that extends this file by creating the
 #    /etc/systemd/network/80-container-vb.network.d/ directory and creating a
 #    new .conf file there.
-# 2. copy this file into /etc/systemd/network or one of the other paths checked
+# 2. copy this file into /etc/systemd/network/ or one of the other paths checked
 #    by systemd-networkd and edit it there.
 # This file should not be edited in place, because it'll be overwritten on upgrades.
 
-# This network file matches the host-side of the virtual Ethernet link
-# created by systemd-nspawn's --network-veth switch with --network-bridge or
-# --network-zone switch. See systemd-nspawn(1) for details.
+# This .network file matches the host-side of the virtual Ethernet link created
+# by systemd-nspawn's --network-veth switch with --network-bridge= or
+# --network-zone= switch. See systemd-nspawn(1) for details.
 
 [Match]
 Kind=veth
diff --git a/network/80-container-ve.link b/network/80-container-ve.link
new file mode 100644 (file)
index 0000000..6dafac6
--- /dev/null
@@ -0,0 +1,24 @@
+# SPDX-License-Identifier: MIT-0
+#
+# This config file is installed as part of systemd.
+# It may be freely copied and edited (following the MIT No Attribution license).
+#
+# To make local modifications, one of the following methods may be used:
+# 1. add a drop-in file that extends this file by creating the
+#    /etc/systemd/network/80-container-ve.link.d/ directory and creating a
+#    new .conf file there.
+# 2. copy this file into /etc/systemd/network/ or one of the other paths checked
+#    by systemd-udevd and edit it there.
+# This file should not be edited in place, because it'll be overwritten on upgrades.
+
+# This .link file matches the host-side of the virtual Ethernet link
+# created by systemd-nspawn's --network-veth switch. See systemd-nspawn(1) for
+# details.
+
+[Match]
+Kind=veth
+OriginalName=ve-*
+
+[Link]
+NamePolicy=keep
+Property=ID_NET_MANAGED_BY=io.systemd.Network
index 076213adc66668880898dc69d25fbf4b0c6b7a25..21a5eacc4e4e0c5170d7eae4dc0d42702c004b66 100644 (file)
@@ -7,11 +7,11 @@
 # 1. add a drop-in file that extends this file by creating the
 #    /etc/systemd/network/80-container-ve.network.d/ directory and creating a
 #    new .conf file there.
-# 2. copy this file into /etc/systemd/network or one of the other paths checked
+# 2. copy this file into /etc/systemd/network/ or one of the other paths checked
 #    by systemd-networkd and edit it there.
 # This file should not be edited in place, because it'll be overwritten on upgrades.
 
-# This network file matches the host-side of the virtual Ethernet link
+# This .network file matches the host-side of the virtual Ethernet link
 # created by systemd-nspawn's --network-veth switch. See systemd-nspawn(1) for
 # details.
 
diff --git a/network/80-container-vz.link b/network/80-container-vz.link
new file mode 100644 (file)
index 0000000..303e82a
--- /dev/null
@@ -0,0 +1,23 @@
+# SPDX-License-Identifier: MIT-0
+#
+# This config file is installed as part of systemd.
+# It may be freely copied and edited (following the MIT No Attribution license).
+#
+# To make local modifications, one of the following methods may be used:
+# 1. add a drop-in file that extends this file by creating the
+#    /etc/systemd/network/80-container-vz.link.d/ directory and creating a
+#    new .conf file there.
+# 2. copy this file into /etc/systemd/network/ or one of the other paths checked
+#    by systemd-udevd and edit it there.
+# This file should not be edited in place, because it'll be overwritten on upgrades.
+
+# This .link file matches the bridge interface created by systemd-nspawn's
+# --network-zone= switch. See systemd-nspawn(1) for details.
+
+[Match]
+Kind=bridge
+OriginalName=vz-*
+
+[Link]
+NamePolicy=keep
+Property=ID_NET_MANAGED_BY=io.systemd.Network
diff --git a/network/80-vm-vt.link b/network/80-vm-vt.link
new file mode 100644 (file)
index 0000000..e98e78e
--- /dev/null
@@ -0,0 +1,23 @@
+# SPDX-License-Identifier: MIT-0
+#
+# This config file is installed as part of systemd.
+# It may be freely copied and edited (following the MIT No Attribution license).
+#
+# To make local modifications, one of the following methods may be used:
+# 1. add a drop-in file that extends this file by creating the
+#    /etc/systemd/network/80-vm-vt.link.d/ directory and creating a
+#    new .conf file there.
+# 2. copy this file into /etc/systemd/network/ or one of the other paths checked
+#    by systemd-udevd and edit it there.
+# This file should not be edited in place, because it'll be overwritten on upgrades.
+
+# This .link file matches vt-* TUN/TAP devices on the host and marks them as
+# managed by systemd-networkd.
+
+[Match]
+Kind=tun
+OriginalName=vt-*
+
+[Link]
+NamePolicy=keep
+Property=ID_NET_MANAGED_BY=io.systemd.Network
index 2a472f4f516b3d6b94ffce85122c14a64d39a500..9df0bea76096d79971edd0bcba1b5a2e47657176 100644 (file)
@@ -3,11 +3,16 @@
 if conf.get('ENABLE_NETWORKD') == 1
         install_data(
                 '80-6rd-tunnel.network',
+                '80-6rd-tunnel.link',
                 '80-container-host0.network',
                 '80-container-vb.network',
+                '80-container-vb.link',
                 '80-container-ve.network',
+                '80-container-ve.link',
                 '80-container-vz.network',
+                '80-container-vz.link',
                 '80-vm-vt.network',
+                '80-vm-vt.link',
                 '80-wifi-adhoc.network',
                 install_dir : networkdir)
 
index 1af7cf8531f7859c59244412d677424d6d0df6c1..a53463451910272ff6a54c59e484b07131fc7514 100644 (file)
--- a/po/pa.po
+++ b/po/pa.po
@@ -1,12 +1,12 @@
 # SPDX-License-Identifier: LGPL-2.1-or-later
 #
 # A S Alam <amanpreet.alam@gmail.com>, 2020, 2021, 2023.
-# A S Alam <aalam@users.noreply.translate.fedoraproject.org>, 2023.
+# A S Alam <aalam@users.noreply.translate.fedoraproject.org>, 2023, 2024.
 msgid ""
 msgstr ""
 "Report-Msgid-Bugs-To: \n"
 "POT-Creation-Date: 2023-11-14 21:25+0000\n"
-"PO-Revision-Date: 2023-12-28 15:36+0000\n"
+"PO-Revision-Date: 2024-01-16 14:35+0000\n"
 "Last-Translator: A S Alam <aalam@users.noreply.translate.fedoraproject.org>\n"
 "Language-Team: Punjabi <https://translate.fedoraproject.org/projects/systemd/"
 "master/pa/>\n"
@@ -15,7 +15,7 @@ msgstr ""
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
 "Plural-Forms: nplurals=2; plural=n > 1;\n"
-"X-Generator: Weblate 5.3\n"
+"X-Generator: Weblate 5.3.1\n"
 
 #: src/core/org.freedesktop.systemd1.policy.in:22
 msgid "Send passphrase back to system"
@@ -155,7 +155,7 @@ msgstr "ਅਫ਼ਸੋਸ, ਰਿਕਵਰੀ ਕੁੰਜੀ ਫੇਰ ਭਰ
 #: src/home/pam_systemd_home.c:352
 #, c-format
 msgid "Security token of user %s not inserted."
-msgstr ""
+msgstr "%s ਵਰਤੋਂਕਾਰ ਲਈ ਸੁਰੱਖਿਆ ਟੋਕਨ ਨਹੀਂ ਦਿੱਤਾ ਗਿਆ।"
 
 #: src/home/pam_systemd_home.c:353 src/home/pam_systemd_home.c:356
 msgid "Try again with password: "
@@ -834,19 +834,19 @@ msgstr ""
 
 #: src/network/org.freedesktop.network1.policy:165
 msgid "Reload network settings"
-msgstr ""
+msgstr "ਨੈੱਟਵਰਕ ਸੈਟਿੰਗਾਂ ਨੂੰ ਮੁੜ-ਲੋਡ ਕਰੋ"
 
 #: src/network/org.freedesktop.network1.policy:166
 msgid "Authentication is required to reload network settings."
-msgstr ""
+msgstr "ਨੈੱਟਵਰਕ ਸੈਟਿੰਗਾਂ ਨੂੰ ਮੁੜ-ਲੋਡ ਕਰਨ ਲਈ ਪਰਮਾਣਕਿਤਾ ਚਾਹੀਦੀ ਹੈ।"
 
 #: src/network/org.freedesktop.network1.policy:176
 msgid "Reconfigure network interface"
-msgstr ""
+msgstr "ਨੈੱਟਵਰਕ ਇੰਟਰਫੇਸ ਦੀ ਮੁੜ-ਸੰਰਚਨਾ ਕਰੋ"
 
 #: src/network/org.freedesktop.network1.policy:177
 msgid "Authentication is required to reconfigure network interface."
-msgstr ""
+msgstr "ਨੈੱਟਵਰਕ ਇੰਟਰਫੇਸ ਦੀ ਮੁੜ-ਸੰਰਚਨਾ ਕਰਨ ਥਈ ਪਰਮਾਣਕਿਤਾ ਚਾਹੀਦੀ ਹੈ।"
 
 #: src/portable/org.freedesktop.portable1.policy:13
 msgid "Inspect a portable service image"
@@ -894,7 +894,7 @@ msgstr ""
 
 #: src/resolve/org.freedesktop.resolve1.policy:133
 msgid "Authentication is required to reset name resolution settings."
-msgstr ""
+msgstr "ਨਾਂ ਹੱਲ ਸੈਟਿੰਗਾਂ ਮੁੜ-ਸੈੱਟ ਕਰਨ ਲਈ ਪਰਮਾਣਕਿਤਾ ਚਾਹੀਦੀ ਹੈ।"
 
 #: src/timedate/org.freedesktop.timedate1.policy:22
 msgid "Set system time"
@@ -902,15 +902,15 @@ msgstr "ਸਿਸਟਮ ਸਮਾੰ ਸੈੱਟ ਕਰੋ"
 
 #: src/timedate/org.freedesktop.timedate1.policy:23
 msgid "Authentication is required to set the system time."
-msgstr ""
+msgstr "ਸਿਸਟਮ ਟਾਈਮ ਸੈੱਟ ਕਰਨ ਲਈ ਪਰਮਾਣਕਿਤਾ ਚਾਹੀਦੀ ਹੈ।"
 
 #: src/timedate/org.freedesktop.timedate1.policy:33
 msgid "Set system timezone"
-msgstr ""
+msgstr "ਸਿਸਟਮ ਸਮਾਂ-ਖੇਤਰ ਸੈੱਟ ਕਰੋ"
 
 #: src/timedate/org.freedesktop.timedate1.policy:34
 msgid "Authentication is required to set the system timezone."
-msgstr ""
+msgstr "ਸਿਸਟਮ ਸਮਾਂ-ਖੇਤਰ ਸੈੱਟ ਕਰਨ ਲਈ ਪਰਮਾਣਕਿਤਾ ਚਾਹੀਦੀ ਹੈ।"
 
 #: src/timedate/org.freedesktop.timedate1.policy:43
 msgid "Set RTC to local timezone or UTC"
@@ -930,19 +930,19 @@ msgstr ""
 
 #: src/core/dbus-unit.c:352
 msgid "Authentication is required to start '$(unit)'."
-msgstr ""
+msgstr "'$(unit)' ਨੂੰ ਸ਼ੁਰੂ ਕਰਨ ਲਈ ਪਰਮਾਣਕਿਤਾ ਚਾਹੀਦੀ ਹੈ।"
 
 #: src/core/dbus-unit.c:353
 msgid "Authentication is required to stop '$(unit)'."
-msgstr ""
+msgstr "'$(unit)' ਨੂੰ ਰੋਕਣ ਕਰਨ ਲਈ ਪਰਮਾਣਕਿਤਾ ਚਾਹੀਦੀ ਹੈ।"
 
 #: src/core/dbus-unit.c:354
 msgid "Authentication is required to reload '$(unit)'."
-msgstr ""
+msgstr "'$(unit)' ਨੂੰ ਮੁੜ-ਲੋਡ (reload) ਕਰਨ ਲਈ ਪਰਮਾਣਕਿਤਾ ਚਾਹੀਦੀ ਹੈ।"
 
 #: src/core/dbus-unit.c:355 src/core/dbus-unit.c:356
 msgid "Authentication is required to restart '$(unit)'."
-msgstr ""
+msgstr "'$(unit)' ਨੂੰ ਮੁੜ-ਸ਼ੁਰੂ (restart) ਕਰਨ ਲਈ ਪਰਮਾਣਕਿਤਾ ਚਾਹੀਦੀ ਹੈ।"
 
 #: src/core/dbus-unit.c:553
 msgid "Authentication is required to send a UNIX signal to the processes of '$(unit)'."
index 96b888fdffbff2e150bdcdfac82d9ac677a7f7c5..390c6e00343dbc87ba1b78284af97386981295c9 100644 (file)
@@ -59,6 +59,8 @@ KERNEL=="vd*", ATTRS{serial}=="?*", ENV{ID_SERIAL}="$attr{serial}", SYMLINK+="di
 
 # ATA
 KERNEL=="sd*[!0-9]|sr*", ENV{ID_SERIAL}!="?*", SUBSYSTEMS=="scsi", ATTRS{vendor}=="ATA", IMPORT{program}="ata_id --export $devnode"
+KERNEL=="sd*[!0-9]|sr*", ENV{ID_BUS}=="ata", ENV{ID_ATA_PERIPHERAL_DEVICE_TYPE}=="20", PROGRAM="scsi_id -u -g $devnode", \
+  SYMLINK+="disk/by-id/scsi-$result$env{.PART_SUFFIX}"
 
 # ATAPI devices (SPC-3 or later)
 KERNEL=="sd*[!0-9]|sr*", ENV{ID_SERIAL}!="?*", SUBSYSTEMS=="scsi", ATTRS{type}=="5", ATTRS{scsi_level}=="[6-9]*", IMPORT{program}="ata_id --export $devnode"
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 b307e428bebb4142f2d74db96fdcf48c5b49a077..e6c476742b38dd4c3f783e9928e8c702af7f9622 100644 (file)
@@ -42,9 +42,9 @@ _loginctl () {
 
     local -A OPTS=(
         [STANDALONE]='--all -a --help -h --no-pager --version
-                      --no-legend --no-ask-password -l --full --value'
+                      --no-legend --no-ask-password -l --full --value -j'
         [ARG]='--host -H --kill-whom --property -p --signal -s -M --machine
-                      -n --lines -o --output -P'
+                      -n --lines -o --output -P --json'
     )
 
     if __contains_word "$prev" ${OPTS[ARG]}; then
@@ -68,6 +68,9 @@ _loginctl () {
             --output|-o)
                 comps=$( loginctl --output=help 2>/dev/null )
                 ;;
+            --json)
+                comps=$( loginctl --json=help 2>/dev/null )
+                ;;
         esac
         COMPREPLY=( $(compgen -W '$comps' -- "$cur") )
         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 1c196391c80dfcbae2f24ca0ed6971566d08e689..74ea1016a9332f2f5778bb79e196f1f7da5eeef3 100644 (file)
@@ -28,12 +28,13 @@ _systemd_id128() {
     local i verb comps
     local cur=${COMP_WORDS[COMP_CWORD]} prev=${COMP_WORDS[COMP_CWORD-1]} words cword
     local -A OPTS=(
-        [STANDALONE]='-h --help --version -p --pretty'
-        [ARG]='-a --app-specific'
+        [STANDALONE]='-h --help --version -p --pretty --value -u --uuid --no-legend --no-pager -j'
+        [ARG]='-a --app-specific --json'
     )
 
     local -A VERBS=(
         [STANDALONE]='new machine-id boot-id invocation-id help'
+        [ARG]='show'
     )
 
     _init_completion || return
@@ -43,6 +44,9 @@ _systemd_id128() {
             --app-specific|-a)
                 comps=""
                 ;;
+            --json)
+                comps="short pretty off"
+                ;;
         esac
         COMPREPLY=( $(compgen -W '$comps' -- "$cur") )
         return 0
@@ -65,6 +69,12 @@ _systemd_id128() {
         comps=${VERBS[*]}
     elif __contains_word "$verb" ${VERBS[STANDALONE]}; then
         comps=''
+    elif __contains_word "$verb" ${VERBS[ARG]}; then
+        case $verb in
+            show)
+                comps="$(IFS='\n ' systemd-id128 show --no-legend)"
+                ;;
+        esac
     fi
 
     COMPREPLY=( $(compgen -W '$comps' -- "$cur") )
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..49b4c3b8cc16236899dfcaa3ba425c8a9e2072c2 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"
@@ -271,6 +272,7 @@ static int help(int argc, char *argv[], void *userdata) {
                "                             specified time\n"
                "     --profile=name|PATH     Include the specified profile in the\n"
                "                             security review of the unit(s)\n"
+               "     --unit=UNIT             Evaluate conditions and asserts of unit\n"
                "     --table                 Output plot's raw time data as a table\n"
                "  -h --help                  Show this help\n"
                "     --version               Show package version\n"
@@ -362,7 +364,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':
@@ -558,6 +560,10 @@ static int parse_argv(int argc, char *argv[]) {
                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
                                        "Option --offline= is only supported for security right now.");
 
+        if (arg_offline && optind >= argc - 1)
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+                                       "Option --offline= requires one or more units to perform a security review.");
+
         if (arg_json_format_flags != JSON_FORMAT_OFF && !STRPTR_IN_SET(argv[optind], "security", "inspect-elf", "plot", "fdstore", "pcrs", "architectures", "capability", "exit-status"))
                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
                                        "Option --json= is only supported for security, inspect-elf, plot, fdstore, pcrs, architectures, capability, exit-status right now.");
index 5fbfff0af41663d3d0cf69c5281ff27396a52461..05a6f211f7fc60a57a545056b34440c627b3f19e 100644 (file)
@@ -20,7 +20,7 @@ typedef void* (*mfree_func_t)(void *p);
  * proceeding and smashing the stack limits. Note that by default RLIMIT_STACK is 8M on Linux. */
 #define ALLOCA_MAX (4U*1024U*1024U)
 
-#define new(t, n) ((t*) malloc_multiply(sizeof(t), (n)))
+#define new(t, n) ((t*) malloc_multiply((n), sizeof(t)))
 
 #define new0(t, n) ((t*) calloc((n) ?: 1, sizeof(t)))
 
@@ -45,7 +45,7 @@ typedef void* (*mfree_func_t)(void *p);
                 (t*) alloca0((sizeof(t)*_n_));                          \
         })
 
-#define newdup(t, p, n) ((t*) memdup_multiply(p, sizeof(t), (n)))
+#define newdup(t, p, n) ((t*) memdup_multiply(p, (n), sizeof(t)))
 
 #define newdup_suffix0(t, p, n) ((t*) memdup_suffix0_multiply(p, sizeof(t), (n)))
 
@@ -112,7 +112,7 @@ static inline bool size_multiply_overflow(size_t size, size_t need) {
         return _unlikely_(need != 0 && size > (SIZE_MAX / need));
 }
 
-_malloc_ _alloc_(1, 2) static inline void *malloc_multiply(size_t size, size_t need) {
+_malloc_ _alloc_(1, 2) static inline void *malloc_multiply(size_t need, size_t size) {
         if (size_multiply_overflow(size, need))
                 return NULL;
 
@@ -128,7 +128,7 @@ _alloc_(2, 3) static inline void *reallocarray(void *p, size_t need, size_t size
 }
 #endif
 
-_alloc_(2, 3) static inline void *memdup_multiply(const void *p, size_t size, size_t need) {
+_alloc_(2, 3) static inline void *memdup_multiply(const void *p, size_t need, size_t size) {
         if (size_multiply_overflow(size, need))
                 return NULL;
 
index 23a128bfd136b02ffc3630c20f10ed63f3c7e4fc..38387cb2ff35fb2b5880a9ad1a1eeb86b301ae7f 100644 (file)
@@ -244,9 +244,9 @@ static bool env_match(const char *t, const char *pattern) {
                 return true;
 
         if (!strchr(pattern, '=')) {
-                size_t l = strlen(pattern);
+                t = startswith(t, pattern);
 
-                return strneq(t, pattern, l) && t[l] == '=';
+                return t && *t == '=';
         }
 
         return false;
@@ -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..8cfa5717dc48c461272946e0a7643898fecfd1a1 100644 (file)
@@ -8,16 +8,38 @@
 #include "alloc-util.h"
 #include "macro.h"
 
+/* An iovec pointing to a single NUL byte */
+#define IOVEC_NUL_BYTE (const struct iovec) {                   \
+                .iov_base = (void*) (const uint8_t[1]) { 0 },   \
+                .iov_len = 1,                                   \
+        }
+
 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 +57,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 3333cf18e7a6e88115bf7c8db898061774f5483e..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
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 4e3d59fc563d8b204602e7355abe30b19bb88a90..068054512bcb1cc756af3cc8673ae429720a851c 100644 (file)
@@ -167,19 +167,13 @@ static char** user_dirs(
                 return NULL;
 
         /* Now merge everything we found. */
-        if (strv_extend(&res, persistent_control) < 0)
-                return NULL;
-
-        if (strv_extend(&res, runtime_control) < 0)
-                return NULL;
-
-        if (strv_extend(&res, transient) < 0)
-                return NULL;
-
-        if (strv_extend(&res, generator_early) < 0)
-                return NULL;
-
-        if (strv_extend(&res, persistent_config) < 0)
+        if (strv_extend_many(
+                            &res,
+                            persistent_control,
+                            runtime_control,
+                            transient,
+                            generator_early,
+                            persistent_config) < 0)
                 return NULL;
 
         if (strv_extend_strv_concat(&res, config_dirs, "/systemd/user") < 0)
@@ -192,16 +186,12 @@ static char** user_dirs(
         if (strv_extend_strv(&res, (char**) user_config_unit_paths, false) < 0)
                 return NULL;
 
-        if (strv_extend(&res, runtime_config) < 0)
-                return NULL;
-
-        if (strv_extend(&res, global_runtime_config) < 0)
-                return NULL;
-
-        if (strv_extend(&res, generator) < 0)
-                return NULL;
-
-        if (strv_extend(&res, data_home) < 0)
+        if (strv_extend_many(
+                            &res,
+                            runtime_config,
+                            global_runtime_config,
+                            generator,
+                            data_home) < 0)
                 return NULL;
 
         if (strv_extend_strv_concat(&res, data_dirs, "/systemd/user") < 0)
index 4d5c01d2cffec0815a8553037f9157b110bf7977..d75d25af9975416896c0048dd0c1b7df14b5f8f0 100644 (file)
@@ -1024,7 +1024,7 @@ int getenv_for_pid(pid_t pid, const char *field, char **ret) {
         _cleanup_fclose_ FILE *f = NULL;
         char *value = NULL;
         const char *path;
-        size_t l, sum = 0;
+        size_t sum = 0;
         int r;
 
         assert(pid >= 0);
@@ -1059,9 +1059,9 @@ int getenv_for_pid(pid_t pid, const char *field, char **ret) {
         if (r < 0)
                 return r;
 
-        l = strlen(field);
         for (;;) {
                 _cleanup_free_ char *line = NULL;
+                const char *match;
 
                 if (sum > ENVIRONMENT_BLOCK_MAX) /* Give up searching eventually */
                         return -ENOBUFS;
@@ -1074,8 +1074,9 @@ int getenv_for_pid(pid_t pid, const char *field, char **ret) {
 
                 sum += r;
 
-                if (strneq(line, field, l) && line[l] == '=') {
-                        value = strdup(line + l + 1);
+                match = startswith(line, field);
+                if (match && *match == '=') {
+                        value = strdup(match + 1);
                         if (!value)
                                 return -ENOMEM;
 
@@ -1667,6 +1668,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) {
index 98133a2ecda7686e851dbeab76ced51bf75656e5..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;
 }
 
@@ -1656,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;
@@ -1681,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;
 
@@ -1692,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,
@@ -1709,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 032d73857ed7f43d790d3df9d070cac4148c7926..c784125ccb423450063f116ee7bb216d8e342c42 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_;
@@ -374,6 +374,14 @@ int socket_get_mtu(int fd, int af, size_t *ret);
 
 int connect_unix_path(int fd, int dir_fd, const char *path);
 
+static inline bool VSOCK_CID_IS_REGULAR(unsigned cid) {
+        /* 0, 1, 2, UINT32_MAX are special, refuse those */
+        return cid > 2 && cid < UINT32_MAX;
+}
+
+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. */
@@ -386,3 +394,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 cb736c36dd66311608211cebbc289e4c8303dd30..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,6 +110,13 @@ 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;
index 8b039ebd9843dd258ea8454c3d10d56c28a5d1f0..2aac588118c1902d19aa68d69bf79c2917655e16 100644 (file)
@@ -1509,24 +1509,20 @@ ssize_t strlevenshtein(const char *x, const char *y) {
 }
 
 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 occurence of "needle" in "haystack". */
+        /* 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 occurence, i.e. *after* the
+        /* Special case: for the empty string we return the very last possible occurrence, i.e. *after* the
          * last char, not before. */
-        if (l == 0)
+        if (*needle == 0)
                 return strchr(haystack, 0);
 
-        for (const char *p = haystack; *p; p++)
-                if (strneq(p, needle, l))
-                        f = p;
-
-        return (char*) f;
+        for (const char *p = strstr(haystack, needle), *q; p; p = q) {
+                q = strstr(p + 1, needle);
+                if (!q)
+                        return (char *) p;
+        }
+        return NULL;
 }
index 908e9e251398a15e5e95b01622dd003c7d4b0f6c..e32653818b0b417d797d1102c5ef14271343a873 100644 (file)
@@ -123,6 +123,22 @@ char** strv_copy_n(char * const *l, size_t m) {
         return TAKE_PTR(result);
 }
 
+int strv_copy_unless_empty(char * const *l, char ***ret) {
+        assert(ret);
+
+        if (strv_isempty(l)) {
+                *ret = NULL;
+                return 0;
+        }
+
+        char **copy = strv_copy(l);
+        if (!copy)
+                return -ENOMEM;
+
+        *ret = TAKE_PTR(copy);
+        return 1;
+}
+
 size_t strv_length(char * const *l) {
         size_t n = 0;
 
@@ -489,29 +505,31 @@ int strv_insert(char ***l, size_t position, char *value) {
         char **c;
         size_t n, m;
 
+        assert(l);
+
         if (!value)
                 return 0;
 
         n = strv_length(*l);
         position = MIN(position, n);
 
-        /* increase and check for overflow */
-        m = n + 2;
-        if (m < n)
+        /* check for overflow and increase*/
+        if (n > SIZE_MAX - 2)
                 return -ENOMEM;
+        m = n + 2;
 
-        c = new(char*, m);
+        c = reallocarray(*l, GREEDY_ALLOC_ROUND_UP(m), sizeof(char*));
         if (!c)
                 return -ENOMEM;
 
-        for (size_t i = 0; i < position; i++)
-                c[i] = (*l)[i];
+        if (n > position)
+                memmove(c + position + 1, c + position, (n - position) * sizeof(char*));
+
         c[position] = value;
-        for (size_t i = position; i < n; i++)
-                c[i+1] = (*l)[i];
-        c[n+1] = NULL;
+        c[n + 1] = NULL;
 
-        return free_and_replace(*l, c);
+        *l = c;
+        return 0;
 }
 
 int strv_consume_with_size(char ***l, size_t *n, char *value) {
@@ -572,39 +590,63 @@ int strv_extend_with_size(char ***l, size_t *n, const char *value) {
         return strv_consume_with_size(l, n, v);
 }
 
-int strv_extend_front(char ***l, const char *value) {
+int strv_extend_many_internal(char ***l, const char *value, ...) {
+        va_list ap;
         size_t n, m;
-        char *v, **c;
+        int r;
 
         assert(l);
 
-        /* Like strv_extend(), but prepends rather than appends the new entry */
+        m = n = strv_length(*l);
 
-        if (!value)
-                return 0;
+        r = 0;
+        va_start(ap, value);
+        for (const char *s = value; s != POINTER_MAX; s = va_arg(ap, const char*)) {
+                if (!s)
+                        continue;
 
-        n = strv_length(*l);
+                if (m > SIZE_MAX-1) { /* overflow */
+                        r = -ENOMEM;
+                        break;
+                }
+                m++;
+        }
+        va_end(ap);
 
-        /* Increase and overflow check. */
-        m = n + 2;
-        if (m < n)
+        if (r < 0)
+                return r;
+        if (m > SIZE_MAX-1)
                 return -ENOMEM;
 
-        v = strdup(value);
-        if (!v)
+        char **c = reallocarray(*l, GREEDY_ALLOC_ROUND_UP(m+1), sizeof(char*));
+        if (!c)
                 return -ENOMEM;
+        *l = c;
 
-        c = reallocarray(*l, m, sizeof(char*));
-        if (!c) {
-                free(v);
-                return -ENOMEM;
+        r = 0;
+        size_t i = n;
+        va_start(ap, value);
+        for (const char *s = value; s != POINTER_MAX; s = va_arg(ap, const char*)) {
+                if (!s)
+                        continue;
+
+                c[i] = strdup(s);
+                if (!c[i]) {
+                        r = -ENOMEM;
+                        break;
+                }
+                i++;
         }
+        va_end(ap);
 
-        memmove(c+1, c, n * sizeof(char*));
-        c[0] = v;
-        c[n+1] = NULL;
+        if (r < 0) {
+                /* rollback on error */
+                for (size_t j = n; j < i; j++)
+                        c[j] = mfree(c[j]);
+                return r;
+        }
 
-        *l = c;
+        c[i] = NULL;
         return 0;
 }
 
index f1a8bc49109540bfbbecf94d4ce9f52e997937b4..91337b9287086027e41ae56ecd7303c44ca95218 100644 (file)
@@ -38,6 +38,8 @@ char** strv_copy_n(char * const *l, size_t n);
 static inline char** strv_copy(char * const *l) {
         return strv_copy_n(l, SIZE_MAX);
 }
+int strv_copy_unless_empty(char * const *l, char ***ret);
+
 size_t strv_length(char * const *l) _pure_;
 
 int strv_extend_strv(char ***a, char * const *b, bool filter_duplicates);
@@ -53,8 +55,10 @@ static inline int strv_extend(char ***l, const char *value) {
         return strv_extend_with_size(l, NULL, value);
 }
 
+int strv_extend_many_internal(char ***l, const char *value, ...);
+#define strv_extend_many(l, ...) strv_extend_many_internal(l, __VA_ARGS__, POINTER_MAX)
+
 int strv_extendf(char ***l, const char *format, ...) _printf_(2,3);
-int strv_extend_front(char ***l, const char *value);
 
 int strv_push_with_size(char ***l, size_t *n, char *value);
 static inline int strv_push(char ***l, char *value) {
index f9014dc560bd4895fcbae0269af878a7de71574e..02123dc591351638846f30f04173b0857157f653 100644 (file)
@@ -1517,7 +1517,7 @@ int get_timezones(char ***ret) {
         /* Always include UTC */
         r = strv_extend(&zones, "UTC");
         if (r < 0)
-                return -ENOMEM;
+                return r;
 
         strv_sort(zones);
         strv_uniq(zones);
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 8aaffe8d0448d715977232f5fa72ee3c78a769f4..1cea96242e7cc2349838ef83b755a545ac37c631 100644 (file)
@@ -184,27 +184,28 @@ const char* default_root_shell(const char *root) {
 
 static int synthesize_user_creds(
                 const char **username,
-                uid_t *uid, gid_t *gid,
-                const char **home,
-                const char **shell,
+                uid_t *ret_uid, gid_t *ret_gid,
+                const char **ret_home,
+                const char **ret_shell,
                 UserCredsFlags flags) {
 
+        assert(username);
+        assert(*username);
+
         /* We enforce some special rules for uid=0 and uid=65534: in order to avoid NSS lookups for root we hardcode
          * their user record data. */
 
         if (STR_IN_SET(*username, "root", "0")) {
                 *username = "root";
 
-                if (uid)
-                        *uid = 0;
-                if (gid)
-                        *gid = 0;
-
-                if (home)
-                        *home = "/root";
-
-                if (shell)
-                        *shell = default_root_shell(NULL);
+                if (ret_uid)
+                        *ret_uid = 0;
+                if (ret_gid)
+                        *ret_gid = 0;
+                if (ret_home)
+                        *ret_home = "/root";
+                if (ret_shell)
+                        *ret_shell = default_root_shell(NULL);
 
                 return 0;
         }
@@ -213,16 +214,14 @@ static int synthesize_user_creds(
             synthesize_nobody()) {
                 *username = NOBODY_USER_NAME;
 
-                if (uid)
-                        *uid = UID_NOBODY;
-                if (gid)
-                        *gid = GID_NOBODY;
-
-                if (home)
-                        *home = FLAGS_SET(flags, USER_CREDS_CLEAN) ? NULL : "/";
-
-                if (shell)
-                        *shell = FLAGS_SET(flags, USER_CREDS_CLEAN) ? NULL : NOLOGIN;
+                if (ret_uid)
+                        *ret_uid = UID_NOBODY;
+                if (ret_gid)
+                        *ret_gid = GID_NOBODY;
+                if (ret_home)
+                        *ret_home = FLAGS_SET(flags, USER_CREDS_CLEAN) ? NULL : "/";
+                if (ret_shell)
+                        *ret_shell = FLAGS_SET(flags, USER_CREDS_CLEAN) ? NULL : NOLOGIN;
 
                 return 0;
         }
@@ -232,11 +231,12 @@ static int synthesize_user_creds(
 
 int get_user_creds(
                 const char **username,
-                uid_t *uid, gid_t *gid,
-                const char **home,
-                const char **shell,
+                uid_t *ret_uid, gid_t *ret_gid,
+                const char **ret_home,
+                const char **ret_shell,
                 UserCredsFlags flags) {
 
+        bool patch_username = false;
         uid_t u = UID_INVALID;
         struct passwd *p;
         int r;
@@ -245,7 +245,7 @@ int get_user_creds(
         assert(*username);
 
         if (!FLAGS_SET(flags, USER_CREDS_PREFER_NSS) ||
-            (!home && !shell)) {
+            (!ret_home && !ret_shell)) {
 
                 /* So here's the deal: normally, we'll try to synthesize all records we can synthesize, and override
                  * the user database with that. However, if the user specifies USER_CREDS_PREFER_NSS then the
@@ -256,7 +256,7 @@ int get_user_creds(
                  * of the relevant users, but changing the UID/GID mappings for them is something we explicitly don't
                  * support. */
 
-                r = synthesize_user_creds(username, uid, gid, home, shell, flags);
+                r = synthesize_user_creds(username, ret_uid, ret_gid, ret_home, ret_shell, flags);
                 if (r >= 0)
                         return 0;
                 if (r != -ENOMEDIUM) /* not a username we can synthesize */
@@ -271,15 +271,15 @@ int get_user_creds(
                  * instead of the first occurrence in the database. However if the uid was configured by a numeric uid,
                  * then let's pick the real username from /etc/passwd. */
                 if (p)
-                        *username = p->pw_name;
-                else if (FLAGS_SET(flags, USER_CREDS_ALLOW_MISSING) && !gid && !home && !shell) {
+                        patch_username = true;
+                else if (FLAGS_SET(flags, USER_CREDS_ALLOW_MISSING) && !ret_gid && !ret_home && !ret_shell) {
 
                         /* If the specified user is a numeric UID and it isn't in the user database, and the caller
                          * passed USER_CREDS_ALLOW_MISSING and was only interested in the UID, then just return that
                          * and don't complain. */
 
-                        if (uid)
-                                *uid = u;
+                        if (ret_uid)
+                                *ret_uid = u;
 
                         return 0;
                 }
@@ -293,65 +293,57 @@ int get_user_creds(
                 r = IN_SET(errno, 0, ENOENT) ? -ESRCH : -errno;
 
                 /* If the user requested that we only synthesize as fallback, do so now */
-                if (FLAGS_SET(flags, USER_CREDS_PREFER_NSS)) {
-                        if (synthesize_user_creds(username, uid, gid, home, shell, flags) >= 0)
+                if (FLAGS_SET(flags, USER_CREDS_PREFER_NSS))
+                        if (synthesize_user_creds(username, ret_uid, ret_gid, ret_home, ret_shell, flags) >= 0)
                                 return 0;
-                }
 
                 return r;
         }
 
-        if (uid) {
-                if (!uid_is_valid(p->pw_uid))
-                        return -EBADMSG;
+        if (ret_uid && !uid_is_valid(p->pw_uid))
+                return -EBADMSG;
 
-                *uid = p->pw_uid;
-        }
+        if (ret_gid && !gid_is_valid(p->pw_gid))
+                return -EBADMSG;
 
-        if (gid) {
-                if (!gid_is_valid(p->pw_gid))
-                        return -EBADMSG;
+        if (ret_uid)
+                *ret_uid = p->pw_uid;
 
-                *gid = p->pw_gid;
-        }
+        if (ret_gid)
+                *ret_gid = p->pw_gid;
 
-        if (home) {
-                if (FLAGS_SET(flags, USER_CREDS_CLEAN) &&
-                    (empty_or_root(p->pw_dir) ||
-                     !path_is_valid(p->pw_dir) ||
-                     !path_is_absolute(p->pw_dir)))
-                        *home = NULL; /* Note: we don't insist on normalized paths, since there are setups that have /./ in the path */
-                else
-                        *home = p->pw_dir;
-        }
+        if (ret_home)
+                /* Note: we don't insist on normalized paths, since there are setups that have /./ in the path */
+                *ret_home = (FLAGS_SET(flags, USER_CREDS_CLEAN) &&
+                             (empty_or_root(p->pw_dir) ||
+                              !path_is_valid(p->pw_dir) ||
+                              !path_is_absolute(p->pw_dir))) ? NULL : p->pw_dir;
 
-        if (shell) {
-                if (FLAGS_SET(flags, USER_CREDS_CLEAN) &&
-                    (isempty(p->pw_shell) ||
-                     !path_is_valid(p->pw_dir) ||
-                     !path_is_absolute(p->pw_shell) ||
-                     is_nologin_shell(p->pw_shell)))
-                        *shell = NULL;
-                else
-                        *shell = p->pw_shell;
-        }
+        if (ret_shell)
+                *ret_shell = (FLAGS_SET(flags, USER_CREDS_CLEAN) &&
+                              (isempty(p->pw_shell) ||
+                               !path_is_valid(p->pw_dir) ||
+                               !path_is_absolute(p->pw_shell) ||
+                               is_nologin_shell(p->pw_shell))) ? NULL : p->pw_shell;
+
+        if (patch_username)
+                *username = p->pw_name;
 
         return 0;
 }
 
-int get_group_creds(const char **groupname, gid_t *gid, UserCredsFlags flags) {
-        struct group *g;
-        gid_t id;
+static int synthesize_group_creds(
+                const char **groupname,
+                gid_t *ret_gid) {
 
         assert(groupname);
-
-        /* We enforce some special rules for gid=0: in order to avoid NSS lookups for root we hardcode its data. */
+        assert(*groupname);
 
         if (STR_IN_SET(*groupname, "root", "0")) {
                 *groupname = "root";
 
-                if (gid)
-                        *gid = 0;
+                if (ret_gid)
+                        *ret_gid = 0;
 
                 return 0;
         }
@@ -360,21 +352,41 @@ int get_group_creds(const char **groupname, gid_t *gid, UserCredsFlags flags) {
             synthesize_nobody()) {
                 *groupname = NOBODY_GROUP_NAME;
 
-                if (gid)
-                        *gid = GID_NOBODY;
+                if (ret_gid)
+                        *ret_gid = GID_NOBODY;
 
                 return 0;
         }
 
+        return -ENOMEDIUM;
+}
+
+int get_group_creds(const char **groupname, gid_t *ret_gid, UserCredsFlags flags) {
+        bool patch_groupname = false;
+        struct group *g;
+        gid_t id;
+        int r;
+
+        assert(groupname);
+        assert(*groupname);
+
+        if (!FLAGS_SET(flags, USER_CREDS_PREFER_NSS)) {
+                r = synthesize_group_creds(groupname, ret_gid);
+                if (r >= 0)
+                        return 0;
+                if (r != -ENOMEDIUM) /* not a groupname we can synthesize */
+                        return r;
+        }
+
         if (parse_gid(*groupname, &id) >= 0) {
                 errno = 0;
                 g = getgrgid(id);
 
                 if (g)
-                        *groupname = g->gr_name;
+                        patch_groupname = true;
                 else if (FLAGS_SET(flags, USER_CREDS_ALLOW_MISSING)) {
-                        if (gid)
-                                *gid = id;
+                        if (ret_gid)
+                                *ret_gid = id;
 
                         return 0;
                 }
@@ -383,18 +395,28 @@ int get_group_creds(const char **groupname, gid_t *gid, UserCredsFlags flags) {
                 g = getgrnam(*groupname);
         }
 
-        if (!g)
+        if (!g) {
                 /* getgrnam() may fail with ENOENT if /etc/group is missing.
                  * For us that is equivalent to the name not being defined. */
-                return IN_SET(errno, 0, ENOENT) ? -ESRCH : -errno;
+                r = IN_SET(errno, 0, ENOENT) ? -ESRCH : -errno;
+
+                if (FLAGS_SET(flags, USER_CREDS_PREFER_NSS))
+                        if (synthesize_group_creds(groupname, ret_gid) >= 0)
+                                return 0;
 
-        if (gid) {
+                return r;
+        }
+
+        if (ret_gid) {
                 if (!gid_is_valid(g->gr_gid))
                         return -EBADMSG;
 
-                *gid = g->gr_gid;
+                *ret_gid = g->gr_gid;
         }
 
+        if (patch_groupname)
+                *groupname = g->gr_name;
+
         return 0;
 }
 
index b3e254662eba843f73c3e809f472865731e6e6aa..b363c2b28efc59e717461c550d63379d28bb6532 100644 (file)
@@ -42,8 +42,8 @@ typedef enum UserCredsFlags {
         USER_CREDS_CLEAN         = 1 << 2,  /* try to clean up shell and home fields with invalid data */
 } UserCredsFlags;
 
-int get_user_creds(const char **username, uid_t *uid, gid_t *gid, const char **home, const char **shell, UserCredsFlags flags);
-int get_group_creds(const char **groupname, gid_t *gid, UserCredsFlags flags);
+int get_user_creds(const char **username, uid_t *ret_uid, gid_t *ret_gid, const char **ret_home, const char **ret_shell, UserCredsFlags flags);
+int get_group_creds(const char **groupname, gid_t *ret_gid, UserCredsFlags flags);
 
 char* uid_to_name(uid_t uid);
 char* gid_to_name(gid_t gid);
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 58470385df27025fa3ff51a005ae3f8819e1617e..a0a474537effe39f69ca36ded84b0d09ab072a59 100644 (file)
@@ -81,11 +81,13 @@ static int load_etc_machine_info(void) {
         return 0;
 }
 
-static int load_etc_kernel_install_conf(void) {
+static int load_kernel_install_conf_one(const char *dir) {
         _cleanup_free_ char *layout = NULL, *p = NULL;
         int r;
 
-        p = path_join(arg_root, etc_kernel(), "install.conf");
+        assert(dir);
+
+        p = path_join(arg_root, dir, "install.conf");
         if (!p)
                 return log_oom();
 
@@ -100,6 +102,23 @@ static int load_etc_kernel_install_conf(void) {
                 free_and_replace(arg_install_layout, layout);
         }
 
+        return 1;
+}
+
+static int load_kernel_install_conf(void) {
+        const char *conf_root;
+        int r;
+
+        conf_root = getenv("KERNEL_INSTALL_CONF_ROOT");
+        if (conf_root)
+                return load_kernel_install_conf_one(conf_root);
+
+        FOREACH_STRING(p, "/etc/kernel", "/usr/lib/kernel") {
+                r = load_kernel_install_conf_one(p);
+                if (r != 0)
+                        return r;
+        }
+
         return 0;
 }
 
@@ -120,7 +139,7 @@ static int settle_make_entry_directory(void) {
         if (r < 0)
                 return r;
 
-        r = load_etc_kernel_install_conf();
+        r = load_kernel_install_conf();
         if (r < 0)
                 return r;
 
@@ -557,7 +576,7 @@ static int install_entry_token(void) {
         if (!arg_make_entry_directory && arg_entry_token_type == BOOT_ENTRY_TOKEN_MACHINE_ID)
                 return 0;
 
-        p = path_join(arg_root, etc_kernel(), "entry-token");
+        p = path_join(arg_root, getenv("KERNEL_INSTALL_CONF_ROOT") ?: "/etc/kernel/", "entry-token");
         if (!p)
                 return log_oom();
 
index 3cab875fa0647ee2b43f31f49686225c5b04c400..448e868a4241cd4d6b0aa25fe4b1e23f95c40537 100644 (file)
@@ -119,7 +119,7 @@ int settle_entry_token(void) {
 
         r = boot_entry_token_ensure(
                         arg_root,
-                        etc_kernel(),
+                        getenv("KERNEL_INSTALL_CONF_ROOT"),
                         arg_machine_id,
                         /* machine_id_is_random = */ false,
                         &arg_entry_token_type,
index 147455e2411acbc1c7198186a6c3cee3c8325d87..6f2c1630de2f9e7619b27ce40ffcd83f46652dc7 100644 (file)
@@ -8,7 +8,3 @@ const char *get_efi_arch(void);
 int get_file_version(int fd, char **ret);
 
 int settle_entry_token(void);
-
-static inline const char* etc_kernel(void) {
-        return getenv("KERNEL_INSTALL_CONF_ROOT") ?: "/etc/kernel/";
-}
index f5b157305594c6d21d773a52ff95dab5feb1fa48..e66e2daf3043c59c777f75cff27b4c3d28438d59 100644 (file)
@@ -232,7 +232,7 @@ static EFI_STATUS find_device(const EFI_GUID *type, EFI_HANDLE *device, EFI_DEVI
                 }
 
                 /* Patch in the data we found */
-                *ret_device_path = device_path_replace_node(partition_path, part_node, (EFI_DEVICE_PATH *) &hd);
+                *ret_device_path = device_path_replace_node(partition_path, part_node, &hd.Header);
                 return EFI_SUCCESS;
         }
 
index 5c5071eabe5c7d948af5733700ae2da82e12cc57..dea112add0983ab8a37bb129f234bd5f11668345 100644 (file)
@@ -300,12 +300,11 @@ static int parse_argv(int argc, char *argv[]) {
         if (strv_isempty(arg_phase)) {
                 /* If no phases are specifically selected, pick everything from the beginning of the initrd
                  * to the beginning of shutdown. */
-                if (strv_extend_strv(&arg_phase,
-                                     STRV_MAKE("enter-initrd",
-                                               "enter-initrd:leave-initrd",
-                                               "enter-initrd:leave-initrd:sysinit",
-                                               "enter-initrd:leave-initrd:sysinit:ready"),
-                                     /* filter_duplicates= */ false) < 0)
+                if (strv_extend_many(&arg_phase,
+                                     "enter-initrd",
+                                     "enter-initrd:leave-initrd",
+                                     "enter-initrd:leave-initrd:sysinit",
+                                     "enter-initrd:leave-initrd:sysinit:ready") < 0)
                         return log_oom();
         } else {
                 strv_sort(arg_phase);
@@ -419,7 +418,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 +994,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 411f59d55de1dbc2de64fec7749d867c05e746cd..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;
@@ -4572,7 +4577,7 @@ static uint64_t unit_get_effective_limit_one(Unit *u, CGroupLimitType type) {
                                 assert_not_reached();
                 }
 
-        cc = unit_get_cgroup_context(u);
+        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. */
index 1d6938aa1812d4c055b9ef45c857c601f97942c3..c56979de01fd331564b2af6716b2ae3aa5111892 100644 (file)
@@ -320,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);
 
index 1830d697848bbc5e9da3e1c0932bbb4b1cb8aa11..602fec13143c35e43b83dd869d1ee2e2f5d05b76 100644 (file)
@@ -1949,7 +1949,7 @@ int bus_exec_context_set_transient_property(
 
                                 r = strv_extend_strv(&c->supplementary_groups, l, true);
                                 if (r < 0)
-                                        return -ENOMEM;
+                                        return r;
 
                                 joined = strv_join(c->supplementary_groups, " ");
                                 if (!joined)
@@ -3174,7 +3174,7 @@ int bus_exec_context_set_transient_property(
 
                                 r = strv_extend_strv(dirs, l, true);
                                 if (r < 0)
-                                        return -ENOMEM;
+                                        return r;
 
                                 unit_write_settingf(u, flags, name, "%s=%s", name, joined);
                         }
@@ -3201,7 +3201,7 @@ int bus_exec_context_set_transient_property(
                                 _cleanup_free_ char *joined = NULL;
                                 r = strv_extend_strv(&c->exec_search_path, l, true);
                                 if (r < 0)
-                                        return -ENOMEM;
+                                        return r;
                                 joined = strv_join(c->exec_search_path, ":");
                                 if (!joined)
                                         return log_oom();
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 3aeb85e45248e146f4bbc6a9ee0adba6647f20a9..7aa44b2393e445ec83d050473bad4c249e5fe8da 100644 (file)
@@ -2271,7 +2271,9 @@ static int bus_unit_set_transient_property(
                                 u->documentation = strv_free(u->documentation);
                                 unit_write_settingf(u, flags, name, "%s=", name);
                         } else {
-                                strv_extend_strv(&u->documentation, l, false);
+                                r = strv_extend_strv(&u->documentation, l, /* filter_duplicates= */ false);
+                                if (r < 0)
+                                        return r;
 
                                 STRV_FOREACH(p, l)
                                         unit_write_settingf(u, flags, name, "%s=%s", name, *p);
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..41c0fce13b5ebe8ae575c247f2fe73bb46be26ba 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"
@@ -243,9 +244,8 @@ static char **credential_search_path(const ExecParameters *params, CredentialSea
         }
 
         if (IN_SET(path, CREDENTIAL_SEARCH_PATH_TRUSTED, CREDENTIAL_SEARCH_PATH_ALL)) {
-                if (params->received_credentials_directory)
-                        if (strv_extend(&l, params->received_credentials_directory) < 0)
-                                return NULL;
+                if (strv_extend(&l, params->received_credentials_directory) < 0)
+                        return NULL;
 
                 if (strv_extend_strv(&l, CONF_PATHS_STRV("credstore"), /* filter_duplicates= */ true) < 0)
                         return NULL;
@@ -271,20 +271,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 +688,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 +702,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 f39b53280067157b1db4cee58458fd9e0b7af040..1ae766d93ede9e51f0705ba3bbd7fa5060a51376 100644 (file)
@@ -2811,11 +2811,10 @@ static int compile_symlinks(
          * absolute, when they are processed in namespace.c they will be made relative automatically, i.e.:
          * 'os-release -> .os-release-stage/os-release' is what will be created. */
         if (setup_os_release_symlink) {
-                r = strv_extend(&symlinks, "/run/host/.os-release-stage/os-release");
-                if (r < 0)
-                        return r;
-
-                r = strv_extend(&symlinks, "/run/host/os-release");
+                r = strv_extend_many(
+                                &symlinks,
+                                "/run/host/.os-release-stage/os-release",
+                                "/run/host/os-release");
                 if (r < 0)
                         return r;
         }
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 e71763763c47d78ea8d5ec75f80a39836483b29f..5c10aabc7ef3c0731f06f9aaf0ac4ea359a06cd2 100644 (file)
@@ -382,10 +382,6 @@ int exec_spawn(Unit *unit,
         if (r < 0)
                 return log_unit_error_errno(unit, r, "Failed to load environment files: %m");
 
-        /* Fork with up-to-date SELinux label database, so the child inherits the up-to-date db
-           and, until the next SELinux policy changes, we save further reloads in future children. */
-        mac_selinux_maybe_reload();
-
         /* We won't know the real executable path until we create the mount namespace in the child, but we
            want to log from the parent, so we use the possibly inaccurate path here. */
         log_command_line(unit, "About to execute", command->path, command->argv);
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 e9d567a97b8fe03cc23d28ba8f2ca850e792a441..0d91faee6f214610880096ada7ce6db9063d9333 100644 (file)
@@ -494,8 +494,9 @@ int manager_deserialize(Manager *m, FILE *f, FDSet *fds) {
                         (void) exec_shared_runtime_deserialize_one(m, val, fds);
                 else if ((val = startswith(l, "subscribed="))) {
 
-                        if (strv_extend(&m->deserialized_subscribed, val) < 0)
-                                return -ENOMEM;
+                        r = strv_extend(&m->deserialized_subscribed, val);
+                        if (r < 0)
+                                return r;
                 } else if ((val = startswith(l, "varlink-server-socket-address="))) {
                         if (!m->varlink_server && MANAGER_IS_SYSTEM(m)) {
                                 r = manager_varlink_init(m);
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 f6b5683467a13bac47c6aadee67ea3c247334657..8bfdd56116b0170394b499d455d80d4f1fca5438 100644 (file)
@@ -994,11 +994,7 @@ static int activation_details_path_append_pair(ActivationDetails *details, char
         if (isempty(p->trigger_path_filename))
                 return 0;
 
-        r = strv_extend(strv, "trigger_path");
-        if (r < 0)
-                return r;
-
-        r = strv_extend(strv, p->trigger_path_filename);
+        r = strv_extend_many(strv, "trigger_path", p->trigger_path_filename);
         if (r < 0)
                 return r;
 
index 9a26bc174424ace2d2249b8b3f0f008f197709d7..a4c9f80e04de194ab0b6e676f76a9ea0267883fd 100644 (file)
@@ -3098,7 +3098,7 @@ int service_deserialize_exec_command(
                 case STATE_EXEC_COMMAND_ARGS:
                         r = strv_extend(&argv, arg);
                         if (r < 0)
-                                return -ENOMEM;
+                                return r;
                         break;
                 default:
                         assert_not_reached();
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 011261a7fcaa92840785d1a7089ab7d2a9c8efc2..49b45b21406bf4739c0922930b0ad47744da817e 100644 (file)
@@ -6530,16 +6530,12 @@ int activation_details_append_pair(ActivationDetails *details, char ***strv) {
                 return 0;
 
         if (!isempty(details->trigger_unit_name)) {
-                r = strv_extend(strv, "trigger_unit");
-                if (r < 0)
-                        return r;
-
-                r = strv_extend(strv, details->trigger_unit_name);
+                r = strv_extend_many(strv, "trigger_unit", details->trigger_unit_name);
                 if (r < 0)
                         return r;
         }
 
-        if (ACTIVATION_DETAILS_VTABLE(details)->append_env) {
+        if (ACTIVATION_DETAILS_VTABLE(details)->append_pair) {
                 r = ACTIVATION_DETAILS_VTABLE(details)->append_pair(details, strv);
                 if (r < 0)
                         return r;
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..90b2fe4a7a5ec3128c196bf3842e018314b8418f 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);
 
@@ -1240,7 +1242,7 @@ static int run_debug(int argc, char **argv, void *userdata) {
         if (r < 0)
                 return r;
 
-        r = strv_extend_strv(&debugger_call, STRV_MAKE(exe, "-c", path), false);
+        r = strv_extend_many(&debugger_call, exe, "-c", path);
         if (r < 0)
                 return log_oom();
 
@@ -1249,14 +1251,14 @@ static int run_debug(int argc, char **argv, void *userdata) {
                         const char *sysroot_cmd;
                         sysroot_cmd = strjoina("set sysroot ", arg_root);
 
-                        r = strv_extend_strv(&debugger_call, STRV_MAKE("-iex", sysroot_cmd), false);
+                        r = strv_extend_many(&debugger_call, "-iex", sysroot_cmd);
                         if (r < 0)
                                 return log_oom();
                 } else if (streq(arg_debugger, "lldb")) {
                         const char *sysroot_cmd;
                         sysroot_cmd = strjoina("platform select --sysroot ", arg_root, " host");
 
-                        r = strv_extend_strv(&debugger_call, STRV_MAKE("-O", sysroot_cmd), false);
+                        r = strv_extend_many(&debugger_call, "-O", sysroot_cmd);
                         if (r < 0)
                                 return log_oom();
                 }
index 5586baff9a49c882318fa668828e6194fb0462c8..bbc705c0069b5c9833e5847c9ed172d66d99563a 100644 (file)
@@ -421,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);
@@ -448,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;
@@ -462,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)
@@ -503,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;
 
@@ -544,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;
@@ -558,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)
@@ -592,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;
 
@@ -606,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;
 
@@ -614,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;
 }
@@ -843,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);
 
@@ -997,8 +996,6 @@ static int vl_method_encrypt(Varlink *link, JsonVariant *parameters, VarlinkMeth
 
         assert(link);
 
-        json_variant_sensitive(parameters);
-
         r = varlink_dispatch(link, parameters, dispatch_table, &p);
         if (r != 0)
                 return r;
@@ -1032,8 +1029,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;
 
@@ -1079,9 +1077,6 @@ static int vl_method_decrypt(Varlink *link, JsonVariant *parameters, VarlinkMeth
 
         assert(link);
 
-        /* Let's also mark the (theoretically encrypted) input as sensitive, in case the NULL encryption scheme was used. */
-        json_variant_sensitive(parameters);
-
         r = varlink_dispatch(link, parameters, dispatch_table, &p);
         if (r != 0)
                 return r;
@@ -1106,8 +1101,9 @@ static int vl_method_decrypt(Varlink *link, JsonVariant *parameters, VarlinkMeth
                         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)
@@ -1143,7 +1139,7 @@ static int run(int argc, char *argv[]) {
 
                 /* Invocation as Varlink service */
 
-                r = varlink_server_new(&varlink_server, VARLINK_SERVER_ACCOUNT_UID|VARLINK_SERVER_INHERIT_USERDATA);
+                r = varlink_server_new(&varlink_server, VARLINK_SERVER_ACCOUNT_UID|VARLINK_SERVER_INHERIT_USERDATA|VARLINK_SERVER_INPUT_SENSITIVE);
                 if (r < 0)
                         return log_error_errno(r, "Failed to allocate Varlink server: %m");
 
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 687b908f060c97f65e108544c322430fa6e69d8c..e1fdc3f5f02448099185f2bd19da9a8fd38c6de1 100644 (file)
@@ -488,7 +488,7 @@ static int parse_argv(int argc, char *argv[]) {
                                         if (n > INT_MAX)
                                                 return log_error_errno(SYNTHETIC_ERRNO(ERANGE), "Slot index out of range: %u", n);
 
-                                        a = reallocarray(arg_wipe_slots, sizeof(int), arg_n_wipe_slots + 1);
+                                        a = reallocarray(arg_wipe_slots, arg_n_wipe_slots + 1, sizeof(int));
                                         if (!a)
                                                 return log_oom();
 
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 10af26888e5bc2bca15205868a537252516bb196..2b080257fed7363fff77e02c8a3f12a19a17ad69 100644 (file)
@@ -46,7 +46,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"
 #include "vpick.h"
 
@@ -423,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))
@@ -441,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 {
@@ -1709,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);
index 90e31c98efa41074618b51a17bd77d9058e4d1ef..fa2c54af31d6a992bef9e20c226043dfee1a1401 100644 (file)
@@ -26,7 +26,7 @@ static int environment_dirs(char ***ret) {
         if (r < 0)
                 return r;
 
-        r = strv_extend_front(&dirs, c);
+        r = strv_consume_prepend(&dirs, TAKE_PTR(c));
         if (r < 0)
                 return r;
 
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 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 787dc773ac9f9563016eb7f6e9243acd0a1a7b01..00fc7f114aceb2fc8652e0e13d582564afed03fe 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"
@@ -402,11 +402,9 @@ static void home_maybe_stop_retry_deactivate(Home *h, HomeState state) {
         /* Free the deactivation retry event source if we won't need it anymore. Specifically, we'll free the
          * event source whenever the home directory is already deactivated (and we thus where successful) or
          * if we start executing an operation that indicates that the home directory is going to be used or
-         * operated on again. Also, if the home is referenced again stop the timer */
+         * operated on again. Also, if the home is referenced again stop the timer. */
 
-        if (HOME_STATE_MAY_RETRY_DEACTIVATE(state) &&
-            !h->ref_event_source_dont_suspend &&
-            !h->ref_event_source_please_suspend)
+        if (HOME_STATE_MAY_RETRY_DEACTIVATE(state) && !home_is_referenced(h))
                 return;
 
         h->retry_deactivate_event_source = sd_event_source_disable_unref(h->retry_deactivate_event_source);
@@ -454,7 +452,7 @@ static void home_start_retry_deactivate(Home *h) {
                 return;
 
         /* If the home directory is being used now don't start the timer */
-        if (h->ref_event_source_dont_suspend || h->ref_event_source_please_suspend)
+        if (home_is_referenced(h))
                 return;
 
         r = sd_event_add_time_relative(
@@ -2616,7 +2614,7 @@ static int on_home_ref_eof(sd_event_source *s, int fd, uint32_t revents, void *u
         if (h->ref_event_source_dont_suspend == s)
                 h->ref_event_source_dont_suspend = sd_event_source_disable_unref(h->ref_event_source_dont_suspend);
 
-        if (h->ref_event_source_dont_suspend || h->ref_event_source_please_suspend)
+        if (home_is_referenced(h))
                 return 0;
 
         log_info("Got notification that all sessions of user %s ended, deactivating automatically.", h->user_name);
@@ -2744,6 +2742,19 @@ static int home_dispatch_acquire(Home *h, Operation *o) {
         return 1;
 }
 
+bool home_is_referenced(Home *h) {
+        assert(h);
+
+        return h->ref_event_source_dont_suspend || h->ref_event_source_please_suspend;
+}
+
+bool home_shall_suspend(Home *h) {
+        assert(h);
+
+        /* Suspend if there's at least one client referencing this home directory that wants a suspend and none who does not. */
+        return h->ref_event_source_please_suspend && !h->ref_event_source_dont_suspend;
+}
+
 static int home_dispatch_release(Home *h, Operation *o) {
         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
         int r;
@@ -2752,7 +2763,7 @@ static int home_dispatch_release(Home *h, Operation *o) {
         assert(o);
         assert(o->type == OPERATION_RELEASE);
 
-        if (h->ref_event_source_dont_suspend || h->ref_event_source_please_suspend)
+        if (home_is_referenced(h))
                 /* If there's now a reference again, then let's abort the release attempt */
                 r = sd_bus_error_setf(&error, BUS_ERROR_HOME_BUSY, "Home %s is currently referenced.", h->user_name);
         else {
@@ -2889,7 +2900,7 @@ static int home_dispatch_pipe_eof(Home *h, Operation *o) {
         assert(o);
         assert(o->type == OPERATION_PIPE_EOF);
 
-        if (h->ref_event_source_please_suspend || h->ref_event_source_dont_suspend)
+        if (home_is_referenced(h))
                 return 1; /* Hmm, there's a reference again, let's cancel this */
 
         switch (home_get_state(h)) {
index 0f314aad93c9fcf572bb361d5072f99835fe078c..1226c0c6ba4d6105aea5eb1ce318b90f5d73b7fd 100644 (file)
@@ -197,6 +197,8 @@ int home_unregister(Home *h, sd_bus_error *error);
 int home_lock(Home *h, sd_bus_error *error);
 int home_unlock(Home *h, UserRecord *secret, sd_bus_error *error);
 
+bool home_is_referenced(Home *h);
+bool home_shall_suspend(Home *h);
 HomeState home_get_state(Home *h);
 
 int home_get_disk_status(Home *h, uint64_t *ret_disk_size,uint64_t *ret_disk_usage, uint64_t *ret_disk_free, uint64_t *ret_disk_ceiling, uint64_t *ret_disk_floor, statfs_f_type_t *ret_fstype, mode_t *ret_access_mode);
index dd17857a6ba8556938d28e9b2d7976265c38effe..5395ae62c1b7fc70f6d2bcecfaea200a1ca1a26e 100644 (file)
@@ -595,10 +595,7 @@ static int method_lock_all_homes(sd_bus_message *message, void *userdata, sd_bus
 
         HASHMAP_FOREACH(h, m->homes_by_name) {
 
-                /* Automatically suspend all homes that have at least one client referencing it that asked
-                 * for "please suspend", and no client that asked for "please do not suspend". */
-                if (h->ref_event_source_dont_suspend ||
-                    !h->ref_event_source_please_suspend)
+                if (!home_shall_suspend(h))
                         continue;
 
                 if (!o) {
index 94b2ea5181a16b25e3872b8836728801142f209d..b1d0c5112031ef7ba419ba984ddb2ad046c1b92a 100644 (file)
@@ -998,7 +998,7 @@ static int manager_bind_varlink(Manager *m) {
         assert(m);
         assert(!m->varlink_server);
 
-        r = varlink_server_new(&m->varlink_server, VARLINK_SERVER_ACCOUNT_UID|VARLINK_SERVER_INHERIT_USERDATA);
+        r = varlink_server_new(&m->varlink_server, VARLINK_SERVER_ACCOUNT_UID|VARLINK_SERVER_INHERIT_USERDATA|VARLINK_SERVER_INPUT_SENSITIVE);
         if (r < 0)
                 return log_error_errno(r, "Failed to allocate varlink server object: %m");
 
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 4cd5be8353b590106b191512d0c2d9534177e7ce..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;
 }
@@ -283,13 +288,13 @@ static int handle_generic_user_record_error(
         /* 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) {
 
@@ -999,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 f0e643822a3bc6c2e080eea9bd8c48ad15171385..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;
 
@@ -92,6 +99,9 @@ static void context_destroy(Context *c) {
 
         context_reset(c, UINT64_MAX);
         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 d726ab7051c9dfcdc46def2125b3666ad0c6b28d..fa86cf666bf7b6750ba174f85992d6b2aa3eacc9 100644 (file)
@@ -5,18 +5,22 @@
 
 #include "alloc-util.h"
 #include "build.h"
+#include "format-table.h"
 #include "gpt.h"
 #include "id128-print.h"
 #include "main-func.h"
+#include "parse-argument.h"
 #include "pretty-print.h"
 #include "strv.h"
-#include "format-table.h"
 #include "terminal-util.h"
 #include "verbs.h"
 
 static Id128PrettyPrintMode arg_mode = ID128_PRINT_ID128;
 static sd_id128_t arg_app = {};
 static bool arg_value = false;
+static PagerFlags arg_pager_flags = 0;
+static bool arg_legend = true;
+static JsonFormatFlags arg_json_format_flags = JSON_FORMAT_OFF;
 
 static int verb_new(int argc, char **argv, void *userdata) {
         return id128_print_new(arg_mode);
@@ -150,9 +154,9 @@ static int verb_show(int argc, char **argv, void *userdata) {
                 }
 
         if (table) {
-                r = table_print(table, NULL);
+                r = table_print_with_pager(table, arg_json_format_flags, arg_pager_flags, arg_legend);
                 if (r < 0)
-                        return table_log_print_error(r);
+                        return r;
         }
 
         return 0;
@@ -177,6 +181,12 @@ static int help(void) {
                "  help                    Show this help\n"
                "\nOptions:\n"
                "  -h --help               Show this help\n"
+               "     --no-pager           Do not pipe output into a pager\n"
+               "     --no-legend          Do not show the headers and footers\n"
+               "     --json=FORMAT        Output inspection data in JSON (takes one of\n"
+               "                          pretty, short, off)\n"
+               "  -j                      Equivalent to --json=pretty (on TTY) or\n"
+               "                          --json=short (otherwise)\n"
                "  -p --pretty             Generate samples of program code\n"
                "  -P --value              Only print the value\n"
                "  -a --app-specific=ID    Generate app-specific IDs\n"
@@ -197,11 +207,17 @@ static int verb_help(int argc, char **argv, void *userdata) {
 static int parse_argv(int argc, char *argv[]) {
         enum {
                 ARG_VERSION = 0x100,
+                ARG_NO_PAGER,
+                ARG_NO_LEGEND,
+                ARG_JSON,
         };
 
         static const struct option options[] = {
                 { "help",         no_argument,       NULL, 'h'              },
                 { "version",      no_argument,       NULL, ARG_VERSION      },
+                { "no-pager",     no_argument,       NULL, ARG_NO_PAGER     },
+                { "no-legend",    no_argument,       NULL, ARG_NO_LEGEND    },
+                { "json",         required_argument, NULL, ARG_JSON         },
                 { "pretty",       no_argument,       NULL, 'p'              },
                 { "value",        no_argument,       NULL, 'P'              },
                 { "app-specific", required_argument, NULL, 'a'              },
@@ -214,7 +230,7 @@ static int parse_argv(int argc, char *argv[]) {
         assert(argc >= 0);
         assert(argv);
 
-        while ((c = getopt_long(argc, argv, "hpa:uP", options, NULL)) >= 0)
+        while ((c = getopt_long(argc, argv, "hpa:uPj", options, NULL)) >= 0)
                 switch (c) {
 
                 case 'h':
@@ -223,6 +239,24 @@ static int parse_argv(int argc, char *argv[]) {
                 case ARG_VERSION:
                         return version();
 
+                case ARG_NO_PAGER:
+                        arg_pager_flags |= PAGER_DISABLE;
+                        break;
+
+                case ARG_NO_LEGEND:
+                        arg_legend = false;
+                        break;
+
+                case 'j':
+                        arg_json_format_flags = JSON_FORMAT_PRETTY_AUTO|JSON_FORMAT_COLOR_AUTO;
+                        break;
+
+                case ARG_JSON:
+                        r = parse_json_argument(optarg, &arg_json_format_flags);
+                        if (r <= 0)
+                                return r;
+
+                        break;
                 case 'p':
                         arg_mode = ID128_PRINT_PRETTY;
                         arg_value = false;
index 8bc8a328666765c40fd31630669ec2f133cce42d..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"
@@ -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..2523d43944959bd62e65cc5ce6b5b0c7ca5804cd 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 {
@@ -132,9 +133,10 @@ static int context_copy(const Context *source, Context *ret) {
 
         assert(source);
         assert(ret);
+        assert(source->rfd >= 0 || source->rfd == AT_FDCWD);
 
         _cleanup_(context_done) Context copy = (Context) {
-                .rfd = -EBADF,
+                .rfd = AT_FDCWD,
                 .action = source->action,
                 .machine_id = source->machine_id,
                 .machine_id_is_random = source->machine_id_is_random,
@@ -143,9 +145,11 @@ static int context_copy(const Context *source, Context *ret) {
                 .entry_token_type = source->entry_token_type,
         };
 
-        copy.rfd = fd_reopen(source->rfd, O_CLOEXEC|O_DIRECTORY|O_PATH);
-        if (copy.rfd < 0)
-                return copy.rfd;
+        if (source->rfd >= 0) {
+                copy.rfd = fd_reopen(source->rfd, O_CLOEXEC|O_DIRECTORY|O_PATH);
+                if (copy.rfd < 0)
+                        return copy.rfd;
+        }
 
         r = strdup_or_null(source->layout_other, &copy.layout_other);
         if (r < 0)
@@ -168,9 +172,9 @@ static int context_copy(const Context *source, Context *ret) {
         r = strdup_or_null(source->kernel, &copy.kernel);
         if (r < 0)
                 return r;
-        copy.initrds = strv_copy(source->initrds);
-        if (!copy.initrds)
-                return -ENOMEM;
+        r = strv_copy_unless_empty(source->initrds, &copy.initrds);
+        if (r < 0)
+                return r;
         r = strdup_or_null(source->initrd_generator, &copy.initrd_generator);
         if (r < 0)
                 return r;
@@ -180,15 +184,15 @@ static int context_copy(const Context *source, Context *ret) {
         r = strdup_or_null(source->staging_area, &copy.staging_area);
         if (r < 0)
                 return r;
-        copy.plugins = strv_copy(source->plugins);
-        if (!copy.plugins)
-                return -ENOMEM;
-        copy.argv = strv_copy(source->argv);
-        if (!copy.argv)
-                return -ENOMEM;
-        copy.envp = strv_copy(source->envp);
-        if (!copy.envp)
-                return -ENOMEM;
+        r = strv_copy_unless_empty(source->plugins, &copy.plugins);
+        if (r < 0)
+                return r;
+        r = strv_copy_unless_empty(source->argv, &copy.argv);
+        if (r < 0)
+                return r;
+        r = strv_copy_unless_empty(source->envp, &copy.envp);
+        if (r < 0)
+                return r;
 
         *ret = copy;
         copy = CONTEXT_NULL;
@@ -427,21 +431,6 @@ static int context_load_environment(Context *c) {
         return 0;
 }
 
-static int context_ensure_conf_root(Context *c) {
-        int r;
-
-        assert(c);
-
-        if (c->conf_root)
-                return 0;
-
-        r = chaseat(c->rfd, "/etc/kernel", CHASE_AT_RESOLVE_IN_ROOT, &c->conf_root, /* ret_fd = */ NULL);
-        if (r < 0)
-                log_debug_errno(r, "Failed to chase /etc/kernel, ignoring: %m");
-
-        return 0;
-}
-
 static int context_load_install_conf_one(Context *c, const char *path) {
         _cleanup_fclose_ FILE *f = NULL;
         _cleanup_free_ char
@@ -494,8 +483,8 @@ static int context_load_install_conf(Context *c) {
                         return r;
         }
 
-        STRV_FOREACH(p, STRV_MAKE("/etc/kernel", "/usr/lib/kernel")) {
-                r = context_load_install_conf_one(c, *p);
+        FOREACH_STRING(p, "/etc/kernel", "/usr/lib/kernel") {
+                r = context_load_install_conf_one(c, p);
                 if (r != 0)
                         return r;
         }
@@ -729,10 +718,6 @@ static int context_init(Context *c) {
         if (r < 0)
                 return r;
 
-        r = context_ensure_conf_root(c);
-        if (r < 0)
-                return r;
-
         r = context_load_install_conf(c);
         if (r < 0)
                 return r;
@@ -993,11 +978,10 @@ static int context_build_arguments(Context *c) {
                         return log_oom();
 
         } else if (c->action == ACTION_INSPECT) {
-                r = strv_extend(&a, c->kernel ?: "[KERNEL_IMAGE]");
-                if (r < 0)
-                        return log_oom();
-
-                r = strv_extend(&a, "[INITRD...]");
+                r = strv_extend_many(
+                                &a,
+                                c->kernel ?: "[KERNEL_IMAGE]",
+                                "[INITRD...]");
                 if (r < 0)
                         return log_oom();
         }
@@ -1181,7 +1165,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,14 +1204,17 @@ 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;
 
         c->action = ACTION_ADD;
 
-        fd = open("/usr/lib/modules", O_DIRECTORY|O_RDONLY|O_CLOEXEC);
+        fd = chase_and_openat(c->rfd, "/usr/lib/modules", CHASE_AT_RESOLVE_IN_ROOT, O_DIRECTORY|O_RDONLY|O_CLOEXEC, NULL);
         if (fd < 0)
-                return log_error_errno(fd, "Failed to open /usr/lib/modules/: %m");
+                return log_error_errno(fd, "Failed to open %s/usr/lib/modules/: %m", strempty(arg_root));
 
         _cleanup_free_ DirectoryEntries *de = NULL;
         r = readdir_all(fd, RECURSE_DIR_SORT|RECURSE_DIR_IGNORE_DOT, &de);
@@ -1235,15 +1222,10 @@ static int verb_add_all(int argc, char *argv[], void *userdata) {
                 return log_error_errno(r, "Failed to numerate /usr/lib/modules/ contents: %m");
 
         FOREACH_ARRAY(d, de->entries, de->n_entries) {
-
-                _cleanup_free_ char *j = path_join("/usr/lib/modules/", (*d)->d_name);
-                if (!j)
-                        return log_oom();
-
                 r = dirent_ensure_type(fd, *d);
                 if (r < 0) {
                         if (r != -ENOENT) /* don't log if just gone by now */
-                                log_debug_errno(r, "Failed to check if '%s' is a directory, ignoring: %m", j);
+                                log_debug_errno(r, "Failed to check if '%s/usr/lib/modules/%s' is a directory, ignoring: %m", strempty(arg_root), (*d)->d_name);
                         continue;
                 }
 
@@ -1256,7 +1238,7 @@ static int verb_add_all(int argc, char *argv[], void *userdata) {
 
                 if (faccessat(fd, fn, F_OK, AT_SYMLINK_NOFOLLOW) < 0) {
                         if (errno != ENOENT)
-                                log_debug_errno(errno, "Failed to check if '/usr/lib/modules/%s/vmlinuz' exists, ignoring: %m", (*d)->d_name);
+                                log_debug_errno(errno, "Failed to check if '%s/usr/lib/modules/%s/vmlinuz' exists, ignoring: %m", strempty(arg_root), (*d)->d_name);
 
                         log_notice("Not adding version '%s', because kernel image not found.", (*d)->d_name);
                         continue;
@@ -1268,6 +1250,8 @@ static int verb_add_all(int argc, char *argv[], void *userdata) {
                 if (r < 0)
                         return log_error_errno(r, "Failed to copy execution context: %m");
 
+                /* do_add() will look up the path in the correct root directory so we don't need to prefix it
+                 * with arg_root here. */
                 _cleanup_free_ char *full = path_join("/usr/lib/modules/", fn);
                 if (!full)
                         return log_oom();
@@ -1283,7 +1267,7 @@ static int verb_add_all(int argc, char *argv[], void *userdata) {
         }
 
         if (n > 0)
-                log_info("Installed %zu kernels.", n);
+                log_debug("Installed %zu kernel(s).", n);
         else if (ret == 0)
                 ret = log_error_errno(SYNTHETIC_ERRNO(ENOENT), "No kernels to install found.");
 
@@ -1308,7 +1292,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. "
@@ -1446,12 +1430,13 @@ static int verb_inspect(int argc, char *argv[], void *userdata) {
 }
 
 static int verb_list(int argc, char *argv[], void *userdata) {
+        Context *c = ASSERT_PTR(userdata);
         _cleanup_close_ int fd = -EBADF;
         int r;
 
-        fd = open("/usr/lib/modules", O_DIRECTORY|O_RDONLY|O_CLOEXEC);
+        fd = chase_and_openat(c->rfd, "/usr/lib/modules", CHASE_AT_RESOLVE_IN_ROOT, O_DIRECTORY|O_RDONLY|O_CLOEXEC, NULL);
         if (fd < 0)
-                return log_error_errno(fd, "Failed to open /usr/lib/modules/: %m");
+                return log_error_errno(fd, "Failed to open %s/usr/lib/modules/: %m", strempty(arg_root));
 
         _cleanup_free_ DirectoryEntries *de = NULL;
         r = readdir_all(fd, RECURSE_DIR_SORT|RECURSE_DIR_IGNORE_DOT, &de);
@@ -1467,7 +1452,6 @@ static int verb_list(int argc, char *argv[], void *userdata) {
         table_set_align_percent(table, table_get_cell(table, 0, 1), 100);
 
         FOREACH_ARRAY(d, de->entries, de->n_entries) {
-
                 _cleanup_free_ char *j = path_join("/usr/lib/modules/", (*d)->d_name);
                 if (!j)
                         return log_oom();
@@ -1475,7 +1459,7 @@ static int verb_list(int argc, char *argv[], void *userdata) {
                 r = dirent_ensure_type(fd, *d);
                 if (r < 0) {
                         if (r != -ENOENT) /* don't log if just gone by now */
-                                log_debug_errno(r, "Failed to check if '%s' is a directory, ignoring: %m", j);
+                                log_debug_errno(r, "Failed to check if '%s/%s' is a directory, ignoring: %m", strempty(arg_root), j);
                         continue;
                 }
 
@@ -1489,7 +1473,7 @@ static int verb_list(int argc, char *argv[], void *userdata) {
                 bool exists;
                 if (faccessat(fd, fn, F_OK, AT_SYMLINK_NOFOLLOW) < 0) {
                         if (errno != ENOENT)
-                                log_debug_errno(errno, "Failed to check if '/usr/lib/modules/%s/vmlinuz' exists, ignoring: %m", (*d)->d_name);
+                                log_debug_errno(errno, "Failed to check if '%s/usr/lib/modules/%s/vmlinuz' exists, ignoring: %m", strempty(arg_root), (*d)->d_name);
 
                         exists = false;
                 } else
index 72f13de24e8b36a0b3982c228b0ce2369026c02b..655f17b2e9531c653ef812657a17e11edd8f83c2 100644 (file)
@@ -4,6 +4,7 @@
 #include "sd-dhcp-client-id.h"
 
 #include "dhcp-duid-internal.h"
+#include "json.h"
 #include "macro.h"
 #include "siphash24.h"
 #include "sparse-endian.h"
@@ -55,3 +56,5 @@ static inline bool client_id_data_size_is_valid(size_t size) {
 
 void client_id_hash_func(const sd_dhcp_client_id *client_id, struct siphash *state);
 int client_id_compare_func(const sd_dhcp_client_id *a, const sd_dhcp_client_id *b);
+
+int json_dispatch_client_id(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata);
index 6feec25adf08ea891a79ebb5b666f582f00ceb6e..cab04f05e5f6212faa95f5a7196c6bfbd643bd06 100644 (file)
@@ -2,6 +2,7 @@
 
 #include "alloc-util.h"
 #include "dhcp-client-id-internal.h"
+#include "iovec-util.h"
 #include "unaligned.h"
 #include "utf8.h"
 
@@ -51,7 +52,9 @@ int sd_dhcp_client_id_set(
 
         assert_return(client_id, -EINVAL);
         assert_return(data, -EINVAL);
-        assert_return(client_id_data_size_is_valid(data_size), -EINVAL);
+
+        if (!client_id_data_size_is_valid(data_size))
+                return -EINVAL;
 
         client_id->id.type = type;
         memcpy(client_id->id.data, data, data_size);
@@ -67,10 +70,12 @@ int sd_dhcp_client_id_set_raw(
 
         assert_return(client_id, -EINVAL);
         assert_return(data, -EINVAL);
-        assert_return(client_id_size_is_valid(data_size), -EINVAL);
 
         /* Unlike sd_dhcp_client_id_set(), this takes whole client ID including its type. */
 
+        if (!client_id_size_is_valid(data_size))
+                return -EINVAL;
+
         memcpy(client_id->raw, data, data_size);
 
         client_id->size = data_size;
@@ -135,6 +140,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;
@@ -148,7 +155,6 @@ int sd_dhcp_client_id_to_string_from_raw(const void *data, size_t data_size, cha
         int r;
 
         assert_return(data, -EINVAL);
-        assert_return(client_id_size_is_valid(data_size), -EINVAL);
         assert_return(ret, -EINVAL);
 
         r = sd_dhcp_client_id_set_raw(&client_id, data, data_size);
@@ -172,3 +178,19 @@ int client_id_compare_func(const sd_dhcp_client_id *a, const sd_dhcp_client_id *
 
         return memcmp_nn(a->raw, a->size, b->raw, b->size);
 }
+
+int json_dispatch_client_id(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
+        sd_dhcp_client_id *client_id = ASSERT_PTR(userdata);
+        _cleanup_(iovec_done) struct iovec iov = {};
+        int r;
+
+        r = json_dispatch_byte_array_iovec(name, variant, flags, &iov);
+        if (r < 0)
+                return r;
+
+        r = sd_dhcp_client_id_set_raw(client_id, iov.iov_base, iov.iov_len);
+        if (r < 0)
+                return json_log(variant, flags, r, "Failed to set DHCP client ID from JSON field '%s': %m", strna(name));
+
+        return 0;
+}
index dc1c17035d1ff01e20fb94b34737b3aba9b6570c..395f7ce4d045ba9d8dcdd3c148185158b9e967bd 100644 (file)
@@ -84,8 +84,10 @@ struct sd_dhcp_client {
         usec_t t1_time;
         usec_t t2_time;
         usec_t expire_time;
-        uint64_t attempt;
-        uint64_t max_attempts;
+        uint64_t discover_attempt;
+        uint64_t request_attempt;
+        uint64_t max_discover_attempts;
+        uint64_t max_request_attempts;
         OrderedHashmap *extra_options;
         OrderedHashmap *vendor_options;
         sd_event_source *timeout_t1;
@@ -149,6 +151,7 @@ static int client_receive_message_udp(
                 uint32_t revents,
                 void *userdata);
 static void client_stop(sd_dhcp_client *client, int error);
+static int client_restart(sd_dhcp_client *client);
 
 int dhcp_client_set_state_callback(
                 sd_dhcp_client *client,
@@ -529,7 +532,7 @@ int sd_dhcp_client_set_max_attempts(sd_dhcp_client *client, uint64_t max_attempt
         assert_return(client, -EINVAL);
         assert_return(!sd_dhcp_client_is_running(client), -EBUSY);
 
-        client->max_attempts = max_attempts;
+        client->max_discover_attempts = max_attempts;
 
         return 0;
 }
@@ -654,7 +657,8 @@ static int client_initialize(sd_dhcp_client *client) {
         (void) event_source_disable(client->timeout_expire);
         (void) event_source_disable(client->timeout_ipv6_only_mode);
 
-        client->attempt = 0;
+        client->discover_attempt = 0;
+        client->request_attempt = 0;
 
         client_set_state(client, DHCP_STATE_STOPPED);
         client->xid = 0;
@@ -1188,13 +1192,19 @@ static int client_timeout_resend(
         case DHCP_STATE_INIT:
         case DHCP_STATE_INIT_REBOOT:
         case DHCP_STATE_SELECTING:
+                if (client->discover_attempt >= client->max_discover_attempts)
+                        goto error;
+
+                client->discover_attempt++;
+                next_timeout = client_compute_request_timeout(time_now, client->discover_attempt);
+                break;
         case DHCP_STATE_REQUESTING:
         case DHCP_STATE_BOUND:
-                if (client->attempt >= client->max_attempts)
+                if (client->request_attempt >= client->max_request_attempts)
                         goto error;
 
-                client->attempt++;
-                next_timeout = client_compute_request_timeout(time_now, client->attempt);
+                client->request_attempt++;
+                next_timeout = client_compute_request_timeout(time_now, client->request_attempt);
                 break;
 
         case DHCP_STATE_STOPPED:
@@ -1218,14 +1228,14 @@ static int client_timeout_resend(
                 r = client_send_discover(client);
                 if (r >= 0) {
                         client_set_state(client, DHCP_STATE_SELECTING);
-                        client->attempt = 0;
-                } else if (client->attempt >= client->max_attempts)
+                        client->discover_attempt = 0;
+                } else if (client->discover_attempt >= client->max_discover_attempts)
                         goto error;
                 break;
 
         case DHCP_STATE_SELECTING:
                 r = client_send_discover(client);
-                if (r < 0 && client->attempt >= client->max_attempts)
+                if (r < 0 && client->discover_attempt >= client->max_discover_attempts)
                         goto error;
                 break;
 
@@ -1234,7 +1244,7 @@ static int client_timeout_resend(
         case DHCP_STATE_RENEWING:
         case DHCP_STATE_REBINDING:
                 r = client_send_request(client);
-                if (r < 0 && client->attempt >= client->max_attempts)
+                if (r < 0 && client->request_attempt >= client->max_request_attempts)
                          goto error;
 
                 if (client->state == DHCP_STATE_INIT_REBOOT)
@@ -1251,12 +1261,20 @@ static int client_timeout_resend(
                 goto error;
         }
 
-        if (client->attempt >= TRANSIENT_FAILURE_ATTEMPTS)
+        if (client->discover_attempt >= TRANSIENT_FAILURE_ATTEMPTS)
                 client_notify(client, SD_DHCP_CLIENT_EVENT_TRANSIENT_FAILURE);
 
         return 0;
 
 error:
+        /* Avoid REQUEST infinite loop. Per RFC 2131 section 3.1.5: if the client receives
+           neither a DHCPACK or a DHCPNAK message after employing the retransmission algorithm,
+           the client reverts to INIT state and restarts the initialization process */
+        if (client->request_attempt >= client->max_request_attempts) {
+                log_dhcp_client(client, "Max REQUEST attempts reached. Restarting...");
+                client_restart(client);
+                return 0;
+        }
         client_stop(client, r);
 
         /* Errors were dealt with when stopping the client, don't spill
@@ -1389,7 +1407,8 @@ static int client_timeout_t2(sd_event_source *s, uint64_t usec, void *userdata)
         client->fd = safe_close(client->fd);
 
         client_set_state(client, DHCP_STATE_REBINDING);
-        client->attempt = 0;
+        client->discover_attempt = 0;
+        client->request_attempt = 0;
 
         r = dhcp_network_bind_raw_socket(client->ifindex, &client->link, client->xid,
                                          &client->hw_addr, &client->bcast_addr,
@@ -1412,7 +1431,8 @@ static int client_timeout_t1(sd_event_source *s, uint64_t usec, void *userdata)
                 client_set_state(client, DHCP_STATE_RENEWING);
         else if (client->state != DHCP_STATE_INIT)
                 client_set_state(client, DHCP_STATE_INIT_REBOOT);
-        client->attempt = 0;
+        client->discover_attempt = 0;
+        client->request_attempt = 0;
 
         return client_initialize_time_events(client);
 }
@@ -1550,7 +1570,8 @@ static int client_enter_requesting_now(sd_dhcp_client *client) {
         assert(client);
 
         client_set_state(client, DHCP_STATE_REQUESTING);
-        client->attempt = 0;
+        client->discover_attempt = 0;
+        client->request_attempt = 0;
 
         return event_reset_time(client->event, &client->timeout_resend,
                                 CLOCK_BOOTTIME, 0, 0,
@@ -1777,7 +1798,8 @@ static int client_enter_bound_now(sd_dhcp_client *client, int notify_event) {
                 notify_event = SD_DHCP_CLIENT_EVENT_IP_ACQUIRE;
 
         client_set_state(client, DHCP_STATE_BOUND);
-        client->attempt = 0;
+        client->discover_attempt = 0;
+        client->request_attempt = 0;
 
         client->last_addr = client->lease->address;
 
@@ -2107,7 +2129,8 @@ int sd_dhcp_client_send_renew(sd_dhcp_client *client) {
         assert(client->lease);
 
         client->start_delay = 0;
-        client->attempt = 1;
+        client->discover_attempt = 1;
+        client->request_attempt = 1;
         client_set_state(client, DHCP_STATE_RENEWING);
 
         return client_initialize_time_events(client);
@@ -2360,7 +2383,8 @@ int sd_dhcp_client_new(sd_dhcp_client **ret, int anonymize) {
                 .mtu = DHCP_MIN_PACKET_SIZE,
                 .port = DHCP_PORT_CLIENT,
                 .anonymize = !!anonymize,
-                .max_attempts = UINT64_MAX,
+                .max_discover_attempts = UINT64_MAX,
+                .max_request_attempts = 5,
                 .ip_service_type = -1,
         };
         /* NOTE: this could be moved to a function. */
index 297deb8a5d60e38a01715f8559587dd8b3403bbb..4782ec62d7af28d02f25041aaaa688fbd716ee41 100644 (file)
@@ -71,7 +71,9 @@ int sd_dhcp_duid_set(
 
         assert_return(duid, -EINVAL);
         assert_return(data, -EINVAL);
-        assert_return(duid_data_size_is_valid(data_size), -EINVAL);
+
+        if (!duid_data_size_is_valid(data_size))
+                return -EINVAL;
 
         unaligned_write_be16(&duid->duid.type, duid_type);
         memcpy(duid->duid.data, data, data_size);
@@ -87,10 +89,12 @@ int sd_dhcp_duid_set_raw(
 
         assert_return(duid, -EINVAL);
         assert_return(data, -EINVAL);
-        assert_return(duid_size_is_valid(data_size), -EINVAL);
 
         /* Unlike sd_dhcp_duid_set(), this takes whole DUID including its type. */
 
+        if (!duid_size_is_valid(data_size))
+                return -EINVAL;
+
         memcpy(duid->raw, data, data_size);
 
         duid->size = data_size;
@@ -209,9 +213,11 @@ int dhcp_duid_to_string_internal(uint16_t type, const void *data, size_t data_si
         const char *t;
 
         assert(data);
-        assert(duid_data_size_is_valid(data_size));
         assert(ret);
 
+        if (!duid_data_size_is_valid(data_size))
+                return -EINVAL;
+
         x = hexmem(data, data_size);
         if (!x)
                 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 16880a80b61ea325c5816b61d1849f205379fc60..db1d0464ae8c07ec85a1bab035279e90237f4c82 100644 (file)
@@ -136,7 +136,7 @@ sd_dhcp_server_lease* dhcp_server_get_static_lease(sd_dhcp_server *server, const
 
         static_lease = hashmap_get(server->static_leases_by_client_id, &req->client_id);
         if (static_lease)
-                return static_lease;
+                goto verify;
 
         /* when no lease is found based on the client id fall back to chaddr */
         if (!client_id_data_size_is_valid(req->message->hlen))
@@ -145,7 +145,20 @@ sd_dhcp_server_lease* dhcp_server_get_static_lease(sd_dhcp_server *server, const
         if (sd_dhcp_client_id_set(&client_id, /* type = */ 1, req->message->chaddr, req->message->hlen) < 0)
                 return NULL;
 
-        return hashmap_get(server->static_leases_by_client_id, &client_id);
+        static_lease = hashmap_get(server->static_leases_by_client_id, &client_id);
+        if (!static_lease)
+                return NULL;
+
+verify:
+        /* Check if the address is in the same subnet. */
+        if ((static_lease->address & server->netmask) != server->subnet)
+                return NULL;
+
+        /* Check if the address is different from the server address. */
+        if (static_lease->address == server->address)
+                return NULL;
+
+        return static_lease;
 }
 
 int sd_dhcp_server_set_static_lease(
index b1c35064ce8c24fa50c4df209a762c5bc8b4686f..d59bf430d7ace6f9e174bbb0ec8438200b60997e 100644 (file)
@@ -87,13 +87,6 @@ int sd_dhcp_server_configure_pool(
                 server->address = address->s_addr;
                 server->netmask = netmask;
                 server->subnet = address->s_addr & netmask;
-
-                /* Drop any leases associated with the old address range */
-                hashmap_clear(server->bound_leases_by_address);
-                hashmap_clear(server->bound_leases_by_client_id);
-
-                if (server->callback)
-                        server->callback(server, SD_DHCP_SERVER_EVENT_LEASE_CHANGED, server->callback_userdata);
         }
 
         return 0;
@@ -1050,10 +1043,19 @@ int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message, siz
                         return 0;
 
                 /* for now pick a random free address from the pool */
-                if (static_lease)
+                if (static_lease) {
+                        if (existing_lease != hashmap_get(server->bound_leases_by_address, UINT32_TO_PTR(static_lease->address)))
+                                /* The address is already assigned to another host. Refusing. */
+                                return 0;
+
+                        /* Found a matching static lease. */
                         address = static_lease->address;
-                else if (existing_lease)
+
+                } else if (existing_lease && address_is_in_pool(server, existing_lease->address))
+
+                        /* If we previously assigned an address to the host, then reuse it. */
                         address = existing_lease->address;
+
                 else {
                         struct siphash state;
                         uint64_t hash;
@@ -1151,30 +1153,24 @@ int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message, siz
                 /* Silently ignore Rapid Commit option in REQUEST message. */
                 req->rapid_commit = false;
 
-                /* disallow our own address */
-                if (address == server->address)
-                        return 0;
-
                 if (static_lease) {
-                        /* Found a static lease for the client ID. */
-
                         if (static_lease->address != address)
-                                /* The client requested an address which is different from the static lease. Refuse. */
+                                /* The client requested an address which is different from the static lease. Refusing. */
+                                return server_send_nak_or_ignore(server, init_reboot, req);
+
+                        if (existing_lease != hashmap_get(server->bound_leases_by_address, UINT32_TO_PTR(address)))
+                                /* The requested address is already assigned to another host. Refusing. */
                                 return server_send_nak_or_ignore(server, init_reboot, req);
 
+                        /* Found a static lease for the client ID. */
                         return server_ack_request(server, req, address);
                 }
 
-                if (address_is_in_pool(server, address)) {
+                if (address_is_in_pool(server, address))
                         /* The requested address is in the pool. */
-
-                        if (existing_lease && existing_lease->address != address)
-                                /* We previously assigned an address, but the client requested another one. Refuse. */
-                                return server_send_nak_or_ignore(server, init_reboot, req);
-
                         return server_ack_request(server, req, address);
-                }
 
+                /* Refuse otherwise. */
                 return server_send_nak_or_ignore(server, init_reboot, req);
         }
 
index 9c343f8368d77217052c7071ecbd6ac632d4bec3..a52f788968d9e9b1f5b1ace86e72f968825d41ae 100644 (file)
@@ -184,7 +184,7 @@ static void test_message_handler(void) {
         assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test), NULL) == 0);
         test.option_server_id.address = htobe32(INADDR_LOOPBACK);
         test.option_requested_ip.address = htobe32(INADDR_LOOPBACK + 4);
-        assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test), NULL) == 0);
+        assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test), NULL) == DHCP_ACK);
         test.option_requested_ip.address = htobe32(INADDR_LOOPBACK + 3);
         assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test), NULL) == DHCP_ACK);
 
@@ -200,7 +200,7 @@ static void test_message_handler(void) {
         assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test), NULL) == DHCP_ACK);
 
         test.option_requested_ip.address = htobe32(INADDR_LOOPBACK + 30);
-        assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test), NULL) == 0);
+        assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test), NULL) == DHCP_ACK);
 
         /* request address reserved for static lease (unmatching client ID) */
         test.option_client_id.id[6] = 'H';
index 5ac90223ffd88874e2fe1f8da42ef0e71f381758..e3d1eb8f33b7752bcddde0130e92564082fa5fa5 100644 (file)
@@ -1288,7 +1288,7 @@ static int message_push_fd(sd_bus_message *m, int fd) {
         if (copy < 0)
                 return -errno;
 
-        f = reallocarray(m->fds, sizeof(int), m->n_fds + 1);
+        f = reallocarray(m->fds, m->n_fds + 1, sizeof(int));
         if (!f) {
                 m->poisoned = true;
                 safe_close(copy);
index 718709f0b24bd9bd7bc91406ff5ea90ea3481290..8fe8854350c98159427d4c9ba0857d5175375e5a 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;
 
@@ -735,12 +735,12 @@ static int bus_socket_inotify_setup(sd_bus *b) {
         assert(b->sockaddr.sa.sa_family == AF_UNIX);
         assert(b->sockaddr.un.sun_path[0] != 0);
 
-        /* Sets up an inotify fd in case watch_bind is enabled: wait until the configured AF_UNIX file system socket
-         * appears before connecting to it. The implemented is pretty simplistic: we just subscribe to relevant changes
-         * to all prefix components of the path, and every time we get an event for that we try to reconnect again,
-         * without actually caring what precisely the event we got told us. If we still can't connect we re-subscribe
-         * to all relevant changes of anything in the path, so that our watches include any possibly newly created path
-         * components. */
+        /* Sets up an inotify fd in case watch_bind is enabled: wait until the configured AF_UNIX file system
+         * socket appears before connecting to it. The implemented is pretty simplistic: we just subscribe to
+         * relevant changes to all components of the path, and every time we get an event for that we try to
+         * reconnect again, without actually caring what precisely the event we got told us. If we still
+         * can't connect we re-subscribe to all relevant changes of anything in the path, so that our watches
+         * include any possibly newly created path components. */
 
         if (b->inotify_fd < 0) {
                 b->inotify_fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC);
@@ -759,17 +759,17 @@ static int bus_socket_inotify_setup(sd_bus *b) {
         if (r < 0)
                 goto fail;
 
-        /* Watch all parent directories, and don't mind any prefix that doesn't exist yet. For the innermost directory
-         * that exists we want to know when files are created or moved into it. For all parents of it we just care if
-         * they are removed or renamed. */
+        /* Watch all components of the path, and don't mind any prefix that doesn't exist yet. For the
+         * innermost directory that exists we want to know when files are created or moved into it. For all
+         * parents of it we just care if they are removed or renamed. */
 
         if (!GREEDY_REALLOC(new_watches, n + 1)) {
                 r = -ENOMEM;
                 goto fail;
         }
 
-        /* Start with the top-level directory, which is a bit simpler than the rest, since it can't be a symlink, and
-         * always exists */
+        /* Start with the top-level directory, which is a bit simpler than the rest, since it can't be a
+         * symlink, and always exists */
         wd = inotify_add_watch(b->inotify_fd, "/", IN_CREATE|IN_MOVED_TO);
         if (wd < 0) {
                 r = log_debug_errno(errno, "Failed to add inotify watch on /: %m");
index f036a49c644b50d1ea482c953c53f00e1c168523..46a367cbd7a1b3e97cccda6f74438eaa7856463b 100644 (file)
@@ -640,7 +640,7 @@ int bus_start_running(sd_bus *bus) {
 
 static int parse_address_key(const char **p, const char *key, char **value) {
         _cleanup_free_ char *r = NULL;
-        size_t l, n = 0;
+        size_t n = 0;
         const char *a;
 
         assert(p);
@@ -648,17 +648,14 @@ static int parse_address_key(const char **p, const char *key, char **value) {
         assert(value);
 
         if (key) {
-                l = strlen(key);
-                if (strncmp(*p, key, l) != 0)
-                        return 0;
-
-                if ((*p)[l] != '=')
+                a = startswith(*p, key);
+                if (!a || *a != '=')
                         return 0;
 
                 if (*value)
                         return -EINVAL;
 
-                a = *p + l + 1;
+                a++;
         } else
                 a = *p;
 
@@ -1486,9 +1483,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;
 
@@ -4115,13 +4118,13 @@ _public_ int sd_bus_path_decode_many(const char *path, const char *path_template
 
         for (template_pos = path_template; *template_pos; ) {
                 const char *sep;
-                size_t length;
+                size_t length, path_length;
                 char *label;
 
                 /* verify everything until the next '%' matches verbatim */
                 sep = strchrnul(template_pos, '%');
                 length = sep - template_pos;
-                if (strncmp(path_pos, template_pos, length))
+                if (!strneq(path_pos, template_pos, length))
                         return 0;
 
                 path_pos += length;
@@ -4142,8 +4145,8 @@ _public_ int sd_bus_path_decode_many(const char *path, const char *path_template
 
                 /* verify the suffixes match */
                 sep = strchrnul(path_pos, '/');
-                if (sep - path_pos < (ssize_t)length ||
-                    strncmp(sep - length, template_pos, length))
+                path_length = sep - path_pos;
+                if (length > path_length || !strneq(sep - length, template_pos, length))
                         return 0;
 
                 template_pos += length; /* skip over matched label */
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 3e88d349e1689cc37bea398734cc7c0b152fe5b0..c72fccc229e9d30ba1c21074055e117553b2a695 100644 (file)
@@ -1081,7 +1081,7 @@ _public_ int sd_get_uids(uid_t **users) {
                                 uid_t *t;
 
                                 n = MAX(16, 2*r);
-                                t = reallocarray(l, sizeof(uid_t), n);
+                                t = reallocarray(l, n, sizeof(uid_t));
                                 if (!t)
                                         return -ENOMEM;
 
index f84097e16de294b79c0144f26340f328ca93c5fd..fb11c7e02bb2816877c409ed56b7d7c328ff01f2 100644 (file)
@@ -56,6 +56,10 @@ static bool rtnl_message_type_is_mdb(uint16_t type) {
         return IN_SET(type, RTM_NEWMDB, RTM_DELMDB, RTM_GETMDB);
 }
 
+static bool rtnl_message_type_is_nsid(uint16_t type) {
+        return IN_SET(type, RTM_NEWNSID, RTM_DELNSID, RTM_GETNSID);
+}
+
 int sd_rtnl_message_route_set_dst_prefixlen(sd_netlink_message *m, unsigned char prefixlen) {
         struct rtmsg *rtm;
 
@@ -1216,3 +1220,24 @@ int sd_rtnl_message_new_mdb(
 
         return 0;
 }
+
+int sd_rtnl_message_new_nsid(
+                sd_netlink *rtnl,
+                sd_netlink_message **ret,
+                uint16_t nlmsg_type) {
+
+        struct rtgenmsg *rt;
+        int r;
+
+        assert_return(rtnl_message_type_is_nsid(nlmsg_type), -EINVAL);
+        assert_return(ret, -EINVAL);
+
+        r = message_new(rtnl, ret, nlmsg_type);
+        if (r < 0)
+                return r;
+
+        rt = NLMSG_DATA((*ret)->hdr);
+        rt->rtgen_family = AF_UNSPEC;
+
+        return 0;
+}
index abe316b64bdc021a995628b0fcdbbece8e4f729d..49d000d47e3354c43d1cbf84fbef4991d8fa9322 100644 (file)
@@ -779,32 +779,6 @@ int sd_netlink_message_read_data(sd_netlink_message *m, uint16_t attr_type, size
 
         assert_return(m, -EINVAL);
 
-        r = netlink_message_read_internal(m, attr_type, &attr_data, NULL);
-        if (r < 0)
-                return r;
-
-        if (ret_data) {
-                void *data;
-
-                data = memdup(attr_data, r);
-                if (!data)
-                        return -ENOMEM;
-
-                *ret_data = data;
-        }
-
-        if (ret_size)
-                *ret_size = r;
-
-        return r;
-}
-
-int sd_netlink_message_read_data_suffix0(sd_netlink_message *m, uint16_t attr_type, size_t *ret_size, void **ret_data) {
-        void *attr_data;
-        int r;
-
-        assert_return(m, -EINVAL);
-
         r = netlink_message_read_internal(m, attr_type, &attr_data, NULL);
         if (r < 0)
                 return r;
index 0153456d9beb9fa87e4482941a39dd2f3f155e3c..681b3086d08f79706275649042f2b1cd88e8fa1d 100644 (file)
@@ -17,6 +17,7 @@
 #include <linux/if_tunnel.h>
 #include <linux/ip.h>
 #include <linux/l2tp.h>
+#include <linux/net_namespace.h>
 #include <linux/netlink.h>
 #include <linux/nexthop.h>
 #include <linux/nl80211.h>
@@ -1185,6 +1186,13 @@ static const NLAPolicy rtnl_mdb_policies[] = {
 
 DEFINE_POLICY_SET(rtnl_mdb);
 
+static const NLAPolicy rtnl_nsid_policies[] = {
+        [NETNSA_FD]         = BUILD_POLICY(S32),
+        [NETNSA_NSID]       = BUILD_POLICY(U32),
+};
+
+DEFINE_POLICY_SET(rtnl_nsid);
+
 static const NLAPolicy rtnl_policies[] = {
         [RTM_NEWLINK]      = BUILD_POLICY_NESTED_WITH_SIZE(rtnl_link, sizeof(struct ifinfomsg)),
         [RTM_DELLINK]      = BUILD_POLICY_NESTED_WITH_SIZE(rtnl_link, sizeof(struct ifinfomsg)),
@@ -1220,6 +1228,9 @@ static const NLAPolicy rtnl_policies[] = {
         [RTM_NEWMDB]       = BUILD_POLICY_NESTED_WITH_SIZE(rtnl_mdb, sizeof(struct br_port_msg)),
         [RTM_DELMDB]       = BUILD_POLICY_NESTED_WITH_SIZE(rtnl_mdb, sizeof(struct br_port_msg)),
         [RTM_GETMDB]       = BUILD_POLICY_NESTED_WITH_SIZE(rtnl_mdb, sizeof(struct br_port_msg)),
+        [RTM_NEWNSID]      = BUILD_POLICY_NESTED_WITH_SIZE(rtnl_nsid, sizeof(struct rtgenmsg)),
+        [RTM_DELNSID]      = BUILD_POLICY_NESTED_WITH_SIZE(rtnl_nsid, sizeof(struct rtgenmsg)),
+        [RTM_GETNSID]      = BUILD_POLICY_NESTED_WITH_SIZE(rtnl_nsid, sizeof(struct rtgenmsg)),
 };
 
 DEFINE_POLICY_SET(rtnl);
index 636af1a2d59503cf95fdf2a5a7e8ec7dfa8e9360..832159a649822d2f1cc4fd4b19d396b0ce717085 100644 (file)
@@ -376,7 +376,7 @@ int rtnl_resolve_link_alternative_name(sd_netlink **rtnl, const char *name, char
         assert(ifindex > 0);
 
         if (ret) {
-                r = sd_netlink_message_read_string_strdup(message, IFLA_IFNAME, ret);
+                r = sd_netlink_message_read_string_strdup(reply, IFLA_IFNAME, ret);
                 if (r < 0)
                         return r;
         }
index 4c2d3173fbebb843fe71cd22c3e2d94b15d9dd9d..13aedc4dbec8dcf9c97fde96bdfe5d2a567820ec 100644 (file)
@@ -677,6 +677,10 @@ TEST(rtnl_set_link_name) {
         assert_se(!strv_contains(alternative_names, "testlongalternativename"));
         assert_se(strv_contains(alternative_names, "test-additional-name"));
         assert_se(!strv_contains(alternative_names, "test-shortname"));
+
+        _cleanup_free_ char *resolved = NULL;
+        assert_se(rtnl_resolve_link_alternative_name(&rtnl, "test-additional-name", &resolved) == ifindex);
+        assert_se(streq_ptr(resolved, "test-shortname"));
 }
 
 DEFINE_TEST_MAIN(LOG_DEBUG);
index 2059567ef894f031c33437ef3f4cd685ed31a37a..25c6e44a7791cdd95eb1a1b5fbba10bb86881470 100644 (file)
@@ -90,49 +90,48 @@ static const char *const link_online_state_table[_LINK_ONLINE_STATE_MAX] = {
 
 DEFINE_STRING_TABLE_LOOKUP(link_online_state, LinkOnlineState);
 
-int parse_operational_state_range(const char *str, LinkOperationalStateRange *out) {
-        LinkOperationalStateRange range = { _LINK_OPERSTATE_INVALID, _LINK_OPERSTATE_INVALID };
-        _cleanup_free_ const char *min = NULL;
+int parse_operational_state_range(const char *s, LinkOperationalStateRange *ret) {
+        LinkOperationalStateRange range = LINK_OPERSTATE_RANGE_INVALID;
+        _cleanup_free_ char *buf = NULL;
         const char *p;
 
-        assert(str);
-        assert(out);
-
-        p = strchr(str, ':');
-        if (p) {
-                min = strndup(str, p - str);
+        assert(s);
+        assert(ret);
 
-                if (!isempty(p + 1)) {
-                        range.max = link_operstate_from_string(p + 1);
-                        if (range.max < 0)
-                                return -EINVAL;
-                }
-        } else
-                min = strdup(str);
+        /* allowed formats: "min", "min:", "min:max", ":max" */
 
-        if (!min)
-                return -ENOMEM;
+        if (isempty(s) || streq(s, ":"))
+                return -EINVAL;
 
-        if (!isempty(min)) {
-                range.min = link_operstate_from_string(min);
-                if (range.min < 0)
+        p = strchr(s, ':');
+        if (!p || isempty(p + 1))
+                range.max = LINK_OPERSTATE_ROUTABLE;
+        else {
+                range.max = link_operstate_from_string(p + 1);
+                if (range.max < 0)
                         return -EINVAL;
         }
 
-        /* Fail on empty strings. */
-        if (range.min == _LINK_OPERSTATE_INVALID && range.max == _LINK_OPERSTATE_INVALID)
-                return -EINVAL;
+        if (p) {
+                buf = strndup(s, p - s);
+                if (!buf)
+                        return -ENOMEM;
 
-        if (range.min == _LINK_OPERSTATE_INVALID)
+                s = buf;
+        }
+
+        if (isempty(s))
                 range.min = LINK_OPERSTATE_MISSING;
-        if (range.max == _LINK_OPERSTATE_INVALID)
-                range.max = LINK_OPERSTATE_ROUTABLE;
+        else {
+                range.min = link_operstate_from_string(s);
+                if (range.min < 0)
+                        return -EINVAL;
+        }
 
-        if (range.min > range.max)
+        if (!operational_state_range_is_valid(&range))
                 return -EINVAL;
 
-        *out = range;
-
+        *ret = range;
         return 0;
 }
 
index c47e271a768fd29ab04438b0dc98fc173f150cb1..6fc6015902e878bcaf632298a93b2e16347f7a09 100644 (file)
@@ -79,8 +79,30 @@ typedef struct LinkOperationalStateRange {
         LinkOperationalState max;
 } LinkOperationalStateRange;
 
-#define LINK_OPERSTATE_RANGE_DEFAULT (LinkOperationalStateRange) { LINK_OPERSTATE_DEGRADED, \
-                                                                   LINK_OPERSTATE_ROUTABLE }
-
-int parse_operational_state_range(const char *str, LinkOperationalStateRange *out);
+#define LINK_OPERSTATE_RANGE_DEFAULT            \
+        (const LinkOperationalStateRange) {     \
+                .min = LINK_OPERSTATE_DEGRADED, \
+                .max = LINK_OPERSTATE_ROUTABLE, \
+        }
+
+#define LINK_OPERSTATE_RANGE_INVALID            \
+        (const LinkOperationalStateRange) {     \
+                .min = _LINK_OPERSTATE_INVALID, \
+                .max = _LINK_OPERSTATE_INVALID, \
+        }
+
+int parse_operational_state_range(const char *s, LinkOperationalStateRange *ret);
 int network_link_get_operational_state(int ifindex, LinkOperationalState *ret);
+
+static inline bool operational_state_is_valid(LinkOperationalState s) {
+        return s >= 0 && s < _LINK_OPERSTATE_MAX;
+}
+static inline bool operational_state_range_is_valid(const LinkOperationalStateRange *range) {
+        return range &&
+                operational_state_is_valid(range->min) &&
+                operational_state_is_valid(range->max) &&
+                range->min <= range->max;
+}
+static inline bool operational_state_is_in_range(LinkOperationalState s, const LinkOperationalStateRange *range) {
+        return range && range->min <= s && s <= range->max;
+}
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 9b76a63990859d008d21915cb479d654ba1f1e56..b9c34cfc0f71a3a4f64ae167cd78714d3c762970 100644 (file)
@@ -44,6 +44,7 @@ static BusPrintPropertyFlags arg_print_flags = 0;
 static bool arg_full = false;
 static PagerFlags arg_pager_flags = 0;
 static bool arg_legend = true;
+static JsonFormatFlags arg_json_format_flags = JSON_FORMAT_OFF;
 static const char *arg_kill_whom = NULL;
 static int arg_signal = SIGTERM;
 static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
@@ -113,80 +114,115 @@ static OutputFlags get_output_flags(void) {
                 colors_enabled() * OUTPUT_COLOR;
 }
 
-static int show_table(Table *table, const char *word) {
+static int list_table_print(Table *table, const char *type) {
         int r;
 
         assert(table);
-        assert(word);
+        assert(type);
 
-        if (!table_isempty(table) || OUTPUT_MODE_IS_JSON(arg_output)) {
-                r = table_set_sort(table, (size_t) 0);
-                if (r < 0)
-                        return table_log_sort_error(r);
+        r = table_set_sort(table, (size_t) 0);
+        if (r < 0)
+                return table_log_sort_error(r);
 
-                table_set_header(table, arg_legend);
+        r = table_print_with_pager(table, arg_json_format_flags, arg_pager_flags, arg_legend);
+        if (r < 0)
+                return r;
 
-                if (OUTPUT_MODE_IS_JSON(arg_output))
-                        r = table_print_json(table, NULL, output_mode_to_json_format_flags(arg_output) | JSON_FORMAT_COLOR_AUTO);
+        if (arg_legend) {
+                if (table_isempty(table))
+                        printf("No %s.\n", type);
                 else
-                        r = table_print(table, NULL);
-                if (r < 0)
-                        return table_log_print_error(r);
+                        printf("\n%zu %s listed.\n", table_get_rows(table) - 1, type);
         }
 
-        if (arg_legend) {
-                if (table_isempty(table))
-                        printf("No %s.\n", word);
+        return 0;
+}
+
+static int list_sessions_table_add(Table *table, sd_bus_message *reply) {
+        int r;
+
+        assert(table);
+        assert(reply);
+
+        r = sd_bus_message_enter_container(reply, 'a', "(sussussbto)");
+        if (r < 0)
+                return bus_log_parse_error(r);
+
+        for (;;) {
+                const char *session_id, *user, *seat, *class, *tty;
+                uint32_t uid, leader_pid;
+                int idle;
+                uint64_t idle_timestamp_monotonic;
+
+                r = sd_bus_message_read(reply, "(sussussbto)",
+                                        &session_id,
+                                        &uid,
+                                        &user,
+                                        &seat,
+                                        &leader_pid,
+                                        &class,
+                                        &tty,
+                                        &idle,
+                                        &idle_timestamp_monotonic,
+                                        /* object = */ NULL);
+                if (r < 0)
+                        return bus_log_parse_error(r);
+                if (r == 0)
+                        break;
+
+                r = table_add_many(table,
+                                   TABLE_STRING, session_id,
+                                   TABLE_UID, (uid_t) uid,
+                                   TABLE_STRING, user,
+                                   TABLE_STRING, empty_to_null(seat),
+                                   TABLE_PID, (pid_t) leader_pid,
+                                   TABLE_STRING, class,
+                                   TABLE_STRING, empty_to_null(tty),
+                                   TABLE_BOOLEAN, idle);
+                if (r < 0)
+                        return table_log_add_error(r);
+
+                if (idle)
+                        r = table_add_cell(table, NULL, TABLE_TIMESTAMP_RELATIVE_MONOTONIC, &idle_timestamp_monotonic);
                 else
-                        printf("\n%zu %s listed.\n", table_get_rows(table) - 1, word);
+                        r = table_add_cell(table, NULL, TABLE_EMPTY, NULL);
+                if (r < 0)
+                        return table_log_add_error(r);
         }
 
+        r = sd_bus_message_exit_container(reply);
+        if (r < 0)
+                return bus_log_parse_error(r);
+
         return 0;
 }
 
-static int list_sessions(int argc, char *argv[], void *userdata) {
+static int list_sessions_table_add_fallback(Table *table, sd_bus_message *reply, sd_bus *bus) {
 
         static const struct bus_properties_map map[] = {
+                { "Leader",                 "u", NULL, offsetof(SessionStatusInfo, leader)                        },
+                { "Class",                  "s", NULL, offsetof(SessionStatusInfo, class)                         },
+                { "TTY",                    "s", NULL, offsetof(SessionStatusInfo, tty)                           },
                 { "IdleHint",               "b", NULL, offsetof(SessionStatusInfo, idle_hint)                     },
                 { "IdleSinceHintMonotonic", "t", NULL, offsetof(SessionStatusInfo, idle_hint_timestamp.monotonic) },
-                { "State",                  "s", NULL, offsetof(SessionStatusInfo, state)                         },
-                { "TTY",                    "s", NULL, offsetof(SessionStatusInfo, tty)                           },
                 {},
         };
 
-        _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
-        _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
-        _cleanup_(table_unrefp) Table *table = NULL;
-        sd_bus *bus = ASSERT_PTR(userdata);
         int r;
 
-        assert(argv);
-
-        pager_open(arg_pager_flags);
-
-        r = bus_call_method(bus, bus_login_mgr, "ListSessions", &error, &reply, NULL);
-        if (r < 0)
-                return log_error_errno(r, "Failed to list sessions: %s", bus_error_message(&error, r));
+        assert(table);
+        assert(reply);
+        assert(bus);
 
         r = sd_bus_message_enter_container(reply, 'a', "(susso)");
         if (r < 0)
                 return bus_log_parse_error(r);
 
-        table = table_new("session", "uid", "user", "seat", "tty", "state", "idle", "since");
-        if (!table)
-                return log_oom();
-
-        /* Right-align the first two fields (since they are numeric) */
-        (void) table_set_align_percent(table, TABLE_HEADER_CELL(0), 100);
-        (void) table_set_align_percent(table, TABLE_HEADER_CELL(1), 100);
-
-        (void) table_set_ersatz_string(table, TABLE_ERSATZ_DASH);
-
         for (;;) {
                 _cleanup_(sd_bus_error_free) sd_bus_error e = SD_BUS_ERROR_NULL;
+                _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
                 const char *id, *user, *seat, *object;
                 uint32_t uid;
-                _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
                 SessionStatusInfo i = {};
 
                 r = sd_bus_message_read(reply, "(susso)", &id, &uid, &user, &seat, &object);
@@ -209,8 +245,9 @@ static int list_sessions(int argc, char *argv[], void *userdata) {
                                    TABLE_UID, (uid_t) uid,
                                    TABLE_STRING, user,
                                    TABLE_STRING, empty_to_null(seat),
+                                   TABLE_PID, i.leader,
+                                   TABLE_STRING, i.class,
                                    TABLE_STRING, empty_to_null(i.tty),
-                                   TABLE_STRING, i.state,
                                    TABLE_BOOLEAN, i.idle_hint);
                 if (r < 0)
                         return table_log_add_error(r);
@@ -227,7 +264,49 @@ static int list_sessions(int argc, char *argv[], void *userdata) {
         if (r < 0)
                 return bus_log_parse_error(r);
 
-        return show_table(table, "sessions");
+        return 0;
+}
+
+static int list_sessions(int argc, char *argv[], void *userdata) {
+        sd_bus *bus = ASSERT_PTR(userdata);
+        _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+        _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
+        _cleanup_(table_unrefp) Table *table = NULL;
+        bool use_ex = true;
+        int r;
+
+        assert(argv);
+
+        r = bus_call_method(bus, bus_login_mgr, "ListSessionsEx", &error, &reply, NULL);
+        if (r < 0) {
+                if (sd_bus_error_has_name(&error, SD_BUS_ERROR_UNKNOWN_METHOD)) {
+                        sd_bus_error_free(&error);
+
+                        use_ex = false;
+                        r = bus_call_method(bus, bus_login_mgr, "ListSessions", &error, &reply, NULL);
+                }
+                if (r < 0)
+                        return log_error_errno(r, "Failed to list sessions: %s", bus_error_message(&error, r));
+        }
+
+        table = table_new("session", "uid", "user", "seat", "leader", "class", "tty", "idle", "since");
+        if (!table)
+                return log_oom();
+
+        /* Right-align the first two fields (since they are numeric) */
+        (void) table_set_align_percent(table, TABLE_HEADER_CELL(0), 100);
+        (void) table_set_align_percent(table, TABLE_HEADER_CELL(1), 100);
+
+        (void) table_set_ersatz_string(table, TABLE_ERSATZ_DASH);
+
+        if (use_ex)
+                r = list_sessions_table_add(table, reply);
+        else
+                r = list_sessions_table_add_fallback(table, reply, bus);
+        if (r < 0)
+                return r;
+
+        return list_table_print(table, "sessions");
 }
 
 static int list_users(int argc, char *argv[], void *userdata) {
@@ -246,8 +325,6 @@ static int list_users(int argc, char *argv[], void *userdata) {
 
         assert(argv);
 
-        pager_open(arg_pager_flags);
-
         r = bus_call_method(bus, bus_login_mgr, "ListUsers", &error, &reply, NULL);
         if (r < 0)
                 return log_error_errno(r, "Failed to list users: %s", bus_error_message(&error, r));
@@ -305,7 +382,7 @@ static int list_users(int argc, char *argv[], void *userdata) {
         if (r < 0)
                 return bus_log_parse_error(r);
 
-        return show_table(table, "users");
+        return list_table_print(table, "users");
 }
 
 static int list_seats(int argc, char *argv[], void *userdata) {
@@ -317,8 +394,6 @@ static int list_seats(int argc, char *argv[], void *userdata) {
 
         assert(argv);
 
-        pager_open(arg_pager_flags);
-
         r = bus_call_method(bus, bus_login_mgr, "ListSeats", &error, &reply, NULL);
         if (r < 0)
                 return log_error_errno(r, "Failed to list seats: %s", bus_error_message(&error, r));
@@ -351,7 +426,7 @@ static int list_seats(int argc, char *argv[], void *userdata) {
         if (r < 0)
                 return bus_log_parse_error(r);
 
-        return show_table(table, "seats");
+        return list_table_print(table, "seats");
 }
 
 static int show_unit_cgroup(
@@ -1464,6 +1539,7 @@ static int parse_argv(int argc, char *argv[]) {
                 ARG_VALUE,
                 ARG_NO_PAGER,
                 ARG_NO_LEGEND,
+                ARG_JSON,
                 ARG_KILL_WHOM,
                 ARG_NO_ASK_PASSWORD,
         };
@@ -1477,6 +1553,7 @@ static int parse_argv(int argc, char *argv[]) {
                 { "full",            no_argument,       NULL, 'l'                 },
                 { "no-pager",        no_argument,       NULL, ARG_NO_PAGER        },
                 { "no-legend",       no_argument,       NULL, ARG_NO_LEGEND       },
+                { "json",            required_argument, NULL, ARG_JSON            },
                 { "kill-whom",       required_argument, NULL, ARG_KILL_WHOM       },
                 { "signal",          required_argument, NULL, 's'                 },
                 { "host",            required_argument, NULL, 'H'                 },
@@ -1492,7 +1569,7 @@ static int parse_argv(int argc, char *argv[]) {
         assert(argc >= 0);
         assert(argv);
 
-        while ((c = getopt_long(argc, argv, "hp:P:als:H:M:n:o:", options, NULL)) >= 0)
+        while ((c = getopt_long(argc, argv, "hp:P:als:H:M:n:o:j", options, NULL)) >= 0)
 
                 switch (c) {
 
@@ -1546,7 +1623,19 @@ static int parse_argv(int argc, char *argv[]) {
                         if (arg_output < 0)
                                 return log_error_errno(arg_output, "Unknown output '%s'.", optarg);
 
-                        if (OUTPUT_MODE_IS_JSON(arg_output))
+                        break;
+
+                case 'j':
+                        arg_json_format_flags = JSON_FORMAT_PRETTY_AUTO|JSON_FORMAT_COLOR_AUTO;
+                        arg_legend = false;
+                        break;
+
+                case ARG_JSON:
+                        r = parse_json_argument(optarg, &arg_json_format_flags);
+                        if (r <= 0)
+                                return r;
+
+                        if (!FLAGS_SET(arg_json_format_flags, JSON_FORMAT_OFF))
                                 arg_legend = false;
 
                         break;
index acef32679a1adce19aa990b23565460f17ba6274..c86856806b591cd2df6e7485a52ba0356a142b36 100644 (file)
@@ -6,6 +6,7 @@
 
 #include "alloc-util.h"
 #include "bus-error.h"
+#include "bus-unit-util.h"
 #include "bus-util.h"
 #include "conf-parser.h"
 #include "format-util.h"
@@ -133,6 +134,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,
@@ -161,15 +167,24 @@ int handle_action_get_enabled_sleep_actions(HandleActionSleepMask mask, char ***
         return 0;
 }
 
-HandleAction handle_action_sleep_select(HandleActionSleepMask mask) {
+HandleAction handle_action_sleep_select(Manager *m) {
+        assert(m);
 
         FOREACH_ARRAY(i, sleep_actions, ELEMENTSOF(sleep_actions)) {
-                HandleActionSleepMask a = 1U << *i;
+                HandleActionSleepMask action_mask = 1U << *i;
+                const HandleActionData *a;
+                _cleanup_free_ char *load_state = NULL;
+
+                if (!FLAGS_SET(m->handle_action_sleep_mask, action_mask))
+                        continue;
+
+                a = ASSERT_PTR(handle_action_lookup(*i));
 
-                if (!FLAGS_SET(mask, a))
+                if (sleep_supported(a->sleep_operation) <= 0)
                         continue;
 
-                if (sleep_supported(ASSERT_PTR(handle_action_lookup(*i))->sleep_operation) > 0)
+                (void) unit_load_state(m->bus, a->target, &load_state);
+                if (streq_ptr(load_state, "loaded"))
                         return *i;
         }
 
@@ -258,7 +273,7 @@ static int handle_action_sleep_execute(
         if (handle == HANDLE_SLEEP) {
                 HandleAction a;
 
-                a = handle_action_sleep_select(m->handle_action_sleep_mask);
+                a = handle_action_sleep_select(m);
                 if (a < 0)
                         return log_warning_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
                                                  "None of the configured sleep operations are supported, ignoring.");
@@ -266,18 +281,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 5ee86486ec3c652e1499d7f0304d74c4e4cd57ae..c78c18c5aad635c16198a5a64cdb817bed760a58 100644 (file)
@@ -70,7 +70,7 @@ struct HandleActionData {
 };
 
 int handle_action_get_enabled_sleep_actions(HandleActionSleepMask mask, char ***ret);
-HandleAction handle_action_sleep_select(HandleActionSleepMask mask);
+HandleAction handle_action_sleep_select(Manager *m);
 
 int manager_handle_action(
                 Manager *m,
index 7f95fa7a4f709e80e2bbfc955afc6db864315375..14835aedc157c7a1a74202429de590f7978c9989 100644 (file)
@@ -11,6 +11,7 @@
 #include "async.h"
 #include "fd-util.h"
 #include "logind-button.h"
+#include "logind-dbus.h"
 #include "missing_input.h"
 #include "string-util.h"
 
@@ -343,6 +344,7 @@ static int button_dispatch(sd_event_source *s, int fd, uint32_t revents, void *u
                         b->lid_closed = true;
                         button_lid_switch_handle_action(b->manager, true);
                         button_install_check_event_source(b);
+                        manager_send_changed(b->manager, "LidClosed", NULL);
 
                 } else if (ev.code == SW_DOCK) {
                         log_struct(LOG_INFO,
@@ -361,6 +363,7 @@ static int button_dispatch(sd_event_source *s, int fd, uint32_t revents, void *u
 
                         b->lid_closed = false;
                         b->check_event_source = sd_event_source_unref(b->check_event_source);
+                        manager_send_changed(b->manager, "LidClosed", NULL);
 
                 } else if (ev.code == SW_DOCK) {
                         log_struct(LOG_INFO,
@@ -514,6 +517,7 @@ int button_check_switches(Button *b) {
 
         b->lid_closed = bitset_get(switches, SW_LID);
         b->docked = bitset_get(switches, SW_DOCK);
+        manager_send_changed(b->manager, "LidClosed", NULL);
 
         if (b->lid_closed)
                 button_install_check_event_source(b);
index 26133ee6efceece88d6b716ff8b09ebb5aa814bf..582cbcbea84709873e5572fd60a22225cb6c4256 100644 (file)
@@ -413,6 +413,9 @@ int manager_get_idle_hint(Manager *m, dual_timestamp *t) {
                 dual_timestamp k;
                 int ih;
 
+                if (!SESSION_CLASS_CAN_IDLE(s->class))
+                        continue;
+
                 ih = session_get_idle_hint(s, &k);
                 if (ih < 0)
                         return ih;
index b310a7cee8638ce27b074abc2aa34dfc3e9a8123..2f722d2491063cb2a01c2e1c43cc1c38c53614ad 100644 (file)
@@ -591,6 +591,60 @@ static int method_list_sessions(sd_bus_message *message, void *userdata, sd_bus_
         return sd_bus_send(NULL, reply, NULL);
 }
 
+static int method_list_sessions_ex(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+        Manager *m = ASSERT_PTR(userdata);
+        _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
+        int r;
+
+        assert(message);
+
+        r = sd_bus_message_new_method_return(message, &reply);
+        if (r < 0)
+                return r;
+
+        r = sd_bus_message_open_container(reply, 'a', "(sussussbto)");
+        if (r < 0)
+                return r;
+
+        Session *s;
+        HASHMAP_FOREACH(s, m->sessions) {
+                _cleanup_free_ char *path = NULL;
+                dual_timestamp idle_ts;
+                bool idle;
+
+                assert(s->user);
+
+                path = session_bus_path(s);
+                if (!path)
+                        return -ENOMEM;
+
+                r = session_get_idle_hint(s, &idle_ts);
+                if (r < 0)
+                        return r;
+                idle = r > 0;
+
+                r = sd_bus_message_append(reply, "(sussussbto)",
+                                          s->id,
+                                          (uint32_t) s->user->user_record->uid,
+                                          s->user->user_record->user_name,
+                                          s->seat ? s->seat->id : "",
+                                          (uint32_t) s->leader.pid,
+                                          session_class_to_string(s->class),
+                                          s->tty,
+                                          idle,
+                                          idle_ts.monotonic,
+                                          path);
+                if (r < 0)
+                        return r;
+        }
+
+        r = sd_bus_message_close_container(reply);
+        if (r < 0)
+                return r;
+
+        return sd_bus_send(NULL, reply, NULL);
+}
+
 static int method_list_users(sd_bus_message *message, void *userdata, sd_bus_error *error) {
         _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
         Manager *m = ASSERT_PTR(userdata);
@@ -865,25 +919,19 @@ static int create_session(
                         c = SESSION_USER;
         }
 
-        /* Check if we are already in a logind session. Or if we are in user@.service
-         * which is a special PAM session that avoids creating a logind session. */
-        r = manager_get_user_by_pid(m, leader.pid, NULL);
+        /* Check if we are already in a logind session, and if so refuse. */
+        r = manager_get_session_by_pidref(m, &leader, /* ret_session= */ NULL);
         if (r < 0)
                 return r;
         if (r > 0)
                 return sd_bus_error_setf(error, BUS_ERROR_SESSION_BUSY,
                                          "Already running in a session or user slice");
 
-        /*
-         * Old gdm and lightdm start the user-session on the same VT as
-         * the greeter session. But they destroy the greeter session
-         * after the user-session and want the user-session to take
-         * over the VT. We need to support this for
-         * backwards-compatibility, so make sure we allow new sessions
-         * on a VT that a greeter is running on. Furthermore, to allow
-         * re-logins, we have to allow a greeter to take over a used VT for
-         * the exact same reasons.
-         */
+        /* Old gdm and lightdm start the user-session on the same VT as the greeter session. But they destroy
+         * the greeter session after the user-session and want the user-session to take over the VT. We need
+         * to support this for backwards-compatibility, so make sure we allow new sessions on a VT that a
+         * greeter is running on. Furthermore, to allow re-logins, we have to allow a greeter to take over a
+         * used VT for the exact same reasons. */
         if (c != SESSION_GREETER &&
             vtnr > 0 &&
             vtnr < MALLOC_ELEMENTSOF(m->seat0->positions) &&
@@ -943,9 +991,17 @@ static int create_session(
                 goto fail;
 
         session->original_type = session->type = t;
-        session->class = c;
         session->remote = remote;
         session->vtnr = vtnr;
+        session->class = c;
+
+        /* Once the first session that is of a pinning class shows up we'll change the GC mode for the user
+         * from USER_GC_BY_ANY to USER_GC_BY_PIN, so that the user goes away once the last pinning session
+         * goes away. Background: we want that user@.service – when started manually – remains around (which
+         * itself is a non-pinning session), but gets stopped when the last pinning session goes away. */
+
+        if (SESSION_CLASS_PIN_USER(c))
+                user->gc_mode = USER_GC_BY_PIN;
 
         if (!isempty(tty)) {
                 session->tty = strdup(tty);
@@ -1017,8 +1073,14 @@ static int create_session(
 
         session->create_message = sd_bus_message_ref(message);
 
-        /* Now, let's wait until the slice unit and stuff got created. We send the reply back from
-         * session_send_create_reply(). */
+        /* Now call into session_send_create_reply(), which will reply to this method call for us. Or it
+         * won't – in case we just spawned a session scope and/or user service manager, and they aren't ready
+         * yet. We'll call session_create_reply() again once the session scope or the user service manager is
+         * ready, where the function will check again if a reply is then ready to be sent, and then do so if
+         * all is complete - or wait again. */
+        r = session_send_create_reply(session, /* error= */ NULL);
+        if (r < 0)
+                return r;
 
         return 1;
 
@@ -1740,6 +1802,7 @@ static int execute_shutdown_or_sleep(
         int r;
 
         assert(m);
+        assert(!m->action_job);
         assert(a);
 
         if (a->inhibit_what == INHIBIT_SHUTDOWN)
@@ -1759,9 +1822,11 @@ static int execute_shutdown_or_sleep(
         if (r < 0)
                 goto error;
 
-        r = free_and_strdup(&m->action_job, p);
-        if (r < 0)
+        m->action_job = strdup(p);
+        if (!m->action_job) {
+                r = -ENOMEM;
                 goto error;
+        }
 
         m->delayed_action = a;
 
@@ -2065,7 +2130,7 @@ static int method_do_shutdown_or_sleep(
         if (action == HANDLE_SLEEP) {
                 HandleAction selected;
 
-                selected = handle_action_sleep_select(m->handle_action_sleep_mask);
+                selected = handle_action_sleep_select(m);
                 if (selected < 0)
                         return sd_bus_error_set(error, BUS_ERROR_SLEEP_VERB_NOT_SUPPORTED,
                                                 "None of the configured sleep operations are supported");
@@ -2117,6 +2182,12 @@ static int method_do_shutdown_or_sleep(
         if (r != 0)
                 return r;
 
+        if (m->delayed_action)
+                return sd_bus_error_setf(error, BUS_ERROR_OPERATION_IN_PROGRESS,
+                                         "Action %s already in progress, refusing requested %s operation.",
+                                         handle_action_to_string(m->delayed_action->handle),
+                                         handle_action_to_string(a->handle));
+
         /* reset case we're shorting a scheduled shutdown */
         m->unlink_nologin = false;
         reset_scheduled_shutdown(m);
@@ -2393,8 +2464,9 @@ static int manager_scheduled_shutdown_handler(
 
         /* Don't allow multiple jobs being executed at the same time */
         if (m->delayed_action) {
-                r = -EALREADY;
-                log_error("Scheduled shutdown to %s failed: shutdown or sleep operation already in progress", a->target);
+                r = log_error_errno(SYNTHETIC_ERRNO(EALREADY),
+                                    "Scheduled shutdown to %s failed: shutdown or sleep operation already in progress.",
+                                    a->target);
                 goto error;
         }
 
@@ -2565,7 +2637,7 @@ static int method_can_shutdown_or_sleep(
                 sd_bus_error *error) {
 
         _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
-        bool multiple_sessions, challenge, blocked;
+        bool multiple_sessions, challenge, blocked, check_unit_state = true;
         const HandleActionData *a;
         const char *result = NULL;
         uid_t uid;
@@ -2578,10 +2650,12 @@ static int method_can_shutdown_or_sleep(
         if (action == HANDLE_SLEEP) {
                 HandleAction selected;
 
-                selected = handle_action_sleep_select(m->handle_action_sleep_mask);
+                selected = handle_action_sleep_select(m);
                 if (selected < 0)
                         return sd_bus_reply_method_return(message, "s", "na");
 
+                check_unit_state = false; /* Already handled by handle_action_sleep_select */
+
                 assert_se(a = handle_action_lookup(selected));
 
         } else if (HANDLE_ACTION_IS_SLEEP(action)) {
@@ -2615,7 +2689,7 @@ static int method_can_shutdown_or_sleep(
         multiple_sessions = r > 0;
         blocked = manager_is_inhibited(m, a->inhibit_what, INHIBIT_BLOCK, NULL, false, true, uid, NULL);
 
-        if (a->target) {
+        if (check_unit_state && a->target) {
                 _cleanup_free_ char *load_state = NULL;
 
                 r = unit_load_state(m->bus, a->target, &load_state);
@@ -3576,7 +3650,7 @@ static const sd_bus_vtable manager_vtable[] = {
         SD_BUS_PROPERTY("PreparingForSleep", "b", property_get_preparing, 0, 0),
         SD_BUS_PROPERTY("ScheduledShutdown", "(st)", property_get_scheduled_shutdown, 0, 0),
         SD_BUS_PROPERTY("Docked", "b", property_get_docked, 0, 0),
-        SD_BUS_PROPERTY("LidClosed", "b", property_get_lid_closed, 0, 0),
+        SD_BUS_PROPERTY("LidClosed", "b", property_get_lid_closed, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
         SD_BUS_PROPERTY("OnExternalPower", "b", property_get_on_external_power, 0, 0),
         SD_BUS_PROPERTY("RemoveIPC", "b", bus_property_get_bool, offsetof(Manager, remove_ipc), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("RuntimeDirectorySize", "t", NULL, offsetof(Manager, runtime_dir_size), SD_BUS_VTABLE_PROPERTY_CONST),
@@ -3618,6 +3692,11 @@ static const sd_bus_vtable manager_vtable[] = {
                                 SD_BUS_RESULT("a(susso)", sessions),
                                 method_list_sessions,
                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_ARGS("ListSessionsEx",
+                                SD_BUS_NO_ARGS,
+                                SD_BUS_RESULT("a(sussussbto)", sessions),
+                                method_list_sessions_ex,
+                                SD_BUS_VTABLE_UNPRIVILEGED),
         SD_BUS_METHOD_WITH_ARGS("ListUsers",
                                 SD_BUS_NO_ARGS,
                                 SD_BUS_RESULT("a(uso)", users),
index 15f04c4a66aaeedd08511cc9bd732e9f5579d0a9..7217b8147642c188d959c77d826bff94cf3f22e5 100644 (file)
@@ -216,7 +216,9 @@ int bus_session_method_lock(sd_bus_message *message, void *userdata, sd_bus_erro
         if (r == 0)
                 return 1; /* Will call us back */
 
-        r = session_send_lock(s, strstr(sd_bus_message_get_member(message), "Lock"));
+        r = session_send_lock(s, /* lock= */ strstr(sd_bus_message_get_member(message), "Lock"));
+        if (r == -ENOTTY)
+                return sd_bus_error_set(error, SD_BUS_ERROR_NOT_SUPPORTED, "Session does not support lock screen.");
         if (r < 0)
                 return r;
 
@@ -248,7 +250,7 @@ static int method_set_idle_hint(sd_bus_message *message, void *userdata, sd_bus_
 
         r = session_set_idle_hint(s, b);
         if (r == -ENOTTY)
-                return sd_bus_error_set(error, SD_BUS_ERROR_NOT_SUPPORTED, "Idle hint control is not supported on non-graphical sessions.");
+                return sd_bus_error_set(error, SD_BUS_ERROR_NOT_SUPPORTED, "Idle hint control is not supported on non-graphical and non-user sessions.");
         if (r < 0)
                 return r;
 
@@ -278,7 +280,11 @@ static int method_set_locked_hint(sd_bus_message *message, void *userdata, sd_bu
         if (uid != 0 && uid != s->user->user_record->uid)
                 return sd_bus_error_set(error, SD_BUS_ERROR_ACCESS_DENIED, "Only owner of session may set locked hint");
 
-        session_set_locked_hint(s, b);
+        r = session_set_locked_hint(s, b);
+        if (r == -ENOTTY)
+                return sd_bus_error_set(error, SD_BUS_ERROR_NOT_SUPPORTED, "Session does not support lock screen.");
+        if (r < 0)
+                return r;
 
         return sd_bus_reply_method_return(message, NULL);
 }
@@ -387,6 +393,9 @@ static int method_set_type(sd_bus_message *message, void *userdata, sd_bus_error
                 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
                                          "Invalid session type '%s'", t);
 
+        if (!SESSION_CLASS_CAN_CHANGE_TYPE(s->class))
+                return sd_bus_error_set(error, SD_BUS_ERROR_NOT_SUPPORTED, "Session class doesn't support changing type.");
+
         if (!session_is_controller(s, sd_bus_message_get_sender(message)))
                 return sd_bus_error_set(error, BUS_ERROR_NOT_IN_CONTROL, "You must be in control of this session to set type");
 
@@ -470,6 +479,9 @@ static int method_take_device(sd_bus_message *message, void *userdata, sd_bus_er
         if (!DEVICE_MAJOR_VALID(major) || !DEVICE_MINOR_VALID(minor))
                 return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Device major/minor is not valid.");
 
+        if (!SESSION_CLASS_CAN_TAKE_DEVICE(s->class))
+                return sd_bus_error_set(error, SD_BUS_ERROR_NOT_SUPPORTED, "Session class doesn't support taking device control.");
+
         if (!session_is_controller(s, sd_bus_message_get_sender(message)))
                 return sd_bus_error_set(error, BUS_ERROR_NOT_IN_CONTROL, "You are not in control of this session");
 
@@ -765,6 +777,9 @@ int session_send_lock(Session *s, bool lock) {
 
         assert(s);
 
+        if (!SESSION_CLASS_CAN_LOCK(s->class))
+                return -ENOTTY;
+
         p = session_bus_path(s);
         if (!p)
                 return -ENOMEM;
@@ -786,6 +801,9 @@ int session_send_lock_all(Manager *m, bool lock) {
         HASHMAP_FOREACH(session, m->sessions) {
                 int k;
 
+                if (!SESSION_CLASS_CAN_LOCK(session->class))
+                        continue;
+
                 k = session_send_lock(session, lock);
                 if (k < 0)
                         r = k;
@@ -800,7 +818,7 @@ static bool session_ready(Session *s) {
         /* Returns true when the session is ready, i.e. all jobs we enqueued for it are done (regardless if successful or not) */
 
         return !s->scope_job &&
-                !s->user->service_job;
+                (!SESSION_CLASS_WANTS_SERVICE_MANAGER(s->class) || !s->user->service_job);
 }
 
 int session_send_create_reply(Session *s, sd_bus_error *error) {
index 979e506897e8adfce2eeee418334ddc0821355ea..69dc52ad66ce0d025c081cd5270d38abc1bc265e 100644 (file)
@@ -38,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)
@@ -722,8 +722,11 @@ static int session_start_scope(Session *s, sd_bus_message *properties, sd_bus_er
         assert(s);
         assert(s->user);
 
+        if (!SESSION_CLASS_WANTS_SCOPE(s->class))
+                return 0;
+
         if (!s->scope) {
-                _cleanup_strv_free_ char **after = NULL;
+                _cleanup_strv_free_ char **wants = NULL, **after = NULL;
                 _cleanup_free_ char *scope = NULL;
                 const char *description;
 
@@ -735,6 +738,12 @@ static int session_start_scope(Session *s, sd_bus_message *properties, sd_bus_er
 
                 description = strjoina("Session ", s->id, " of User ", s->user->user_record->user_name);
 
+                /* These two have StopWhenUnneeded= set, hence add a dep towards them */
+                wants = strv_new(s->user->runtime_dir_service,
+                                 SESSION_CLASS_WANTS_SERVICE_MANAGER(s->class) ? s->user->service : STRV_IGNORE);
+                if (!wants)
+                        return log_oom();
+
                 /* We usually want to order session scopes after systemd-user-sessions.service since the
                  * latter unit is used as login session barrier for unprivileged users. However the barrier
                  * doesn't apply for root as sysadmin should always be able to log in (and without waiting
@@ -743,7 +752,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();
@@ -754,9 +763,7 @@ static int session_start_scope(Session *s, sd_bus_message *properties, sd_bus_er
                                 &s->leader,
                                 s->user->slice,
                                 description,
-                                /* These two have StopWhenUnneeded= set, hence add a dep towards them */
-                                STRV_MAKE(s->user->runtime_dir_service,
-                                          s->user->service),
+                                wants,
                                 after,
                                 user_record_home_directory(s->user->user_record),
                                 properties,
@@ -810,7 +817,7 @@ static int session_setup_stop_on_idle_timer(Session *s) {
 
         assert(s);
 
-        if (s->manager->stop_idle_session_usec == USEC_INFINITY || IN_SET(s->class, SESSION_GREETER, SESSION_LOCK_SCREEN))
+        if (s->manager->stop_idle_session_usec == USEC_INFINITY || !SESSION_CLASS_CAN_STOP_ON_IDLE(s->class))
                 return 0;
 
         r = sd_event_add_time_relative(
@@ -1148,7 +1155,9 @@ found_atime:
 int session_set_idle_hint(Session *s, bool b) {
         assert(s);
 
-        if (!SESSION_TYPE_IS_GRAPHICAL(s->type))
+        if (!SESSION_CLASS_CAN_IDLE(s->class)) /* Only some session classes know the idle concept at all */
+                return -ENOTTY;
+        if (!SESSION_TYPE_IS_GRAPHICAL(s->type)) /* And only graphical session types can set the field explicitly */
                 return -ENOTTY;
 
         if (s->idle_hint == b)
@@ -1174,15 +1183,20 @@ int session_get_locked_hint(Session *s) {
         return s->locked_hint;
 }
 
-void session_set_locked_hint(Session *s, bool b) {
+int session_set_locked_hint(Session *s, bool b) {
         assert(s);
 
+        if (!SESSION_CLASS_CAN_LOCK(s->class))
+                return -ENOTTY;
+
         if (s->locked_hint == b)
-                return;
+                return 0;
 
         s->locked_hint = b;
+        (void) session_save(s);
+        (void) session_send_changed(s, "LockedHint", NULL);
 
-        session_send_changed(s, "LockedHint", NULL);
+        return 1;
 }
 
 void session_set_type(Session *s, SessionType t) {
@@ -1288,11 +1302,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);
-        }
+        s->fifo_path = unlink_and_free(s->fifo_path);
 }
 
 bool session_may_gc(Session *s, bool drop_not_started) {
@@ -1636,10 +1646,14 @@ static const char* const session_type_table[_SESSION_TYPE_MAX] = {
 DEFINE_STRING_TABLE_LOOKUP(session_type, SessionType);
 
 static const char* const session_class_table[_SESSION_CLASS_MAX] = {
-        [SESSION_USER]        = "user",
-        [SESSION_GREETER]     = "greeter",
-        [SESSION_LOCK_SCREEN] = "lock-screen",
-        [SESSION_BACKGROUND]  = "background",
+        [SESSION_USER]              = "user",
+        [SESSION_USER_EARLY]        = "user-early",
+        [SESSION_GREETER]           = "greeter",
+        [SESSION_LOCK_SCREEN]       = "lock-screen",
+        [SESSION_BACKGROUND]        = "background",
+        [SESSION_BACKGROUND_LIGHT]  = "background-light",
+        [SESSION_MANAGER]           = "manager",
+        [SESSION_MANAGER_EARLY]     = "manager-early",
 };
 
 DEFINE_STRING_TABLE_LOOKUP(session_class, SessionClass);
index 95d49bb224188b17d7b19fe8b232b5b6108cc819..6a0fb5430359f4fbb8895c66f7f786a4cf84d3d2 100644 (file)
@@ -20,14 +20,49 @@ 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_BACKGROUND_LIGHT,   /* Like SESSION_BACKGROUND, but without the service manager */
+        SESSION_MANAGER,            /* The service manager */
+        SESSION_MANAGER_EARLY,      /* The service manager for root (which is allowed to run before systemd-user-sessions.service) */
         _SESSION_CLASS_MAX,
         _SESSION_CLASS_INVALID = -EINVAL,
 } SessionClass;
 
+/* Whether we shall allow sessions of this class to run before 'systemd-user-sessions.service'. It's
+ * generally set for root sessions, but no one else. */
+#define SESSION_CLASS_IS_EARLY(class) IN_SET((class), SESSION_USER_EARLY, SESSION_MANAGER_EARLY)
+
+/* Which session classes want their own scope units? (all of them, except the manager, which comes in its own service unit already */
+#define SESSION_CLASS_WANTS_SCOPE(class) IN_SET((class), SESSION_USER, SESSION_USER_EARLY, SESSION_GREETER, SESSION_LOCK_SCREEN, SESSION_BACKGROUND, SESSION_BACKGROUND_LIGHT)
+
+/* Which session classes want their own per-user service manager? */
+#define SESSION_CLASS_WANTS_SERVICE_MANAGER(class) IN_SET((class), SESSION_USER, SESSION_USER_EARLY, SESSION_GREETER, SESSION_LOCK_SCREEN, SESSION_BACKGROUND)
+
+/* Which session classes can pin our user tracking? */
+#define SESSION_CLASS_PIN_USER(class) (!IN_SET((class), SESSION_MANAGER, SESSION_MANAGER_EARLY))
+
+/* Which session classes decide whether system is idle? (should only cover sessions that have input, and are not idle screens themselves)*/
+#define SESSION_CLASS_CAN_IDLE(class) (IN_SET((class), SESSION_USER, SESSION_USER_EARLY, SESSION_GREETER))
+
+/* Which session classes have a lock screen concept? */
+#define SESSION_CLASS_CAN_LOCK(class) (IN_SET((class), SESSION_USER, SESSION_USER_EARLY))
+
+/* Which sessions are candidates to become "display" sessions */
+#define SESSION_CLASS_CAN_DISPLAY(class) (IN_SET((class), SESSION_USER, SESSION_USER_EARLY, SESSION_GREETER))
+
+/* Which sessions classes should be subject to stop-in-idle */
+#define SESSION_CLASS_CAN_STOP_ON_IDLE(class) (IN_SET((class), SESSION_USER, SESSION_USER_EARLY))
+
+/* Which session classes can take control of devices */
+#define SESSION_CLASS_CAN_TAKE_DEVICE(class) (IN_SET((class), SESSION_USER, SESSION_USER_EARLY, SESSION_GREETER, SESSION_LOCK_SCREEN))
+
+/* Which session classes allow changing session types */
+#define SESSION_CLASS_CAN_CHANGE_TYPE(class) (IN_SET((class), SESSION_USER, SESSION_USER_EARLY, SESSION_GREETER, SESSION_LOCK_SCREEN))
+
 typedef enum SessionType {
         SESSION_UNSPECIFIED,
         SESSION_TTY,
@@ -141,7 +176,7 @@ bool session_is_active(Session *s);
 int session_get_idle_hint(Session *s, dual_timestamp *t);
 int session_set_idle_hint(Session *s, bool b);
 int session_get_locked_hint(Session *s);
-void session_set_locked_hint(Session *s, bool b);
+int session_set_locked_hint(Session *s, bool b);
 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);
index 4e6b20eb11e85f0522c06c6a9244f616fdfc4479..e6e57ad79ee41ac5c529706b1c5e04c176ad0061 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"
 
@@ -63,6 +63,7 @@ int user_new(User **ret,
                 .manager = m,
                 .user_record = user_record_ref(ur),
                 .last_session_timestamp = USEC_INFINITY,
+                .gc_mode = USER_GC_BY_ANY,
         };
 
         if (asprintf(&u->state_file, "/run/systemd/users/" UID_FMT, ur->uid) < 0)
@@ -162,10 +163,12 @@ static int user_save_internal(User *u) {
                 "# This is private data. Do not parse.\n"
                 "NAME=%s\n"
                 "STATE=%s\n"         /* friendly user-facing state */
-                "STOPPING=%s\n",     /* low-level state */
+                "STOPPING=%s\n"      /* low-level state */
+                "GC_MODE=%s\n",
                 u->user_record->user_name,
                 user_state_to_string(user_get_state(u)),
-                yes_no(u->stopping));
+                yes_no(u->stopping),
+                user_gc_mode_to_string(u->gc_mode));
 
         /* LEGACY: no-one reads RUNTIME= anymore, drop it at some point */
         if (u->runtime_path)
@@ -302,7 +305,7 @@ int user_save(User *u) {
 }
 
 int user_load(User *u) {
-        _cleanup_free_ char *realtime = NULL, *monotonic = NULL, *stopping = NULL, *last_session_timestamp = NULL;
+        _cleanup_free_ char *realtime = NULL, *monotonic = NULL, *stopping = NULL, *last_session_timestamp = NULL, *gc_mode = NULL;
         int r;
 
         assert(u);
@@ -312,7 +315,8 @@ int user_load(User *u) {
                            "STOPPING",               &stopping,
                            "REALTIME",               &realtime,
                            "MONOTONIC",              &monotonic,
-                           "LAST_SESSION_TIMESTAMP", &last_session_timestamp);
+                           "LAST_SESSION_TIMESTAMP", &last_session_timestamp,
+                           "GC_MODE",                &gc_mode);
         if (r == -ENOENT)
                 return 0;
         if (r < 0)
@@ -333,10 +337,24 @@ int user_load(User *u) {
         if (last_session_timestamp)
                 (void) deserialize_usec(last_session_timestamp, &u->last_session_timestamp);
 
+        u->gc_mode = user_gc_mode_from_string(gc_mode);
+        if (u->gc_mode < 0)
+                u->gc_mode = USER_GC_BY_PIN;
+
         return 0;
 }
 
-static void user_start_service(User *u) {
+static bool user_wants_service_manager(User *u) {
+        assert(u);
+
+        LIST_FOREACH(sessions_by_user, s, u->sessions)
+                if (SESSION_CLASS_WANTS_SERVICE_MANAGER(s->class))
+                        return true;
+
+        return false;
+}
+
+void user_start_service_manager(User *u) {
         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
         int r;
 
@@ -346,6 +364,12 @@ static void user_start_service(User *u) {
          * start the per-user slice or the systemd-runtime-dir@.service instance, as those are pulled in both by
          * user@.service and the session scopes as dependencies. */
 
+        if (u->stopping) /* Don't try to start this if the user is going down */
+                return;
+
+        if (!user_wants_service_manager(u)) /* Only start user service manager if there's at least one session which wants it */
+                return;
+
         u->service_job = mfree(u->service_job);
 
         r = manager_start_unit(u->manager, u->service, &error, &u->service_job);
@@ -448,7 +472,7 @@ int user_start(User *u) {
         u->stopping = false;
 
         if (!u->started)
-                log_debug("Starting services for new user %s.", u->user_record->user_name);
+                log_debug("Tracking new user %s.", u->user_record->user_name);
 
         /* Save the user data so far, because pam_systemd will read the XDG_RUNTIME_DIR out of it while starting up
          * systemd --user.  We need to do user_save_internal() because we have not "officially" started yet. */
@@ -458,7 +482,7 @@ int user_start(User *u) {
         (void) user_update_slice(u);
 
         /* Start user@UID.service */
-        user_start_service(u);
+        user_start_service_manager(u);
 
         if (!u->started) {
                 if (!dual_timestamp_is_set(&u->timestamp))
@@ -575,6 +599,9 @@ int user_get_idle_hint(User *u, dual_timestamp *t) {
                 dual_timestamp k;
                 int ih;
 
+                if (!SESSION_CLASS_CAN_IDLE(s->class))
+                        continue;
+
                 ih = session_get_idle_hint(s, &k);
                 if (ih < 0)
                         return ih;
@@ -651,6 +678,29 @@ static usec_t user_get_stop_delay(User *u) {
         return u->manager->user_stop_delay;
 }
 
+static bool user_pinned_by_sessions(User *u) {
+        assert(u);
+
+        /* Returns true if at least one session exists that shall keep the user tracking alive. That
+         * generally means one session that isn't the service manager still exists. */
+
+        switch (u->gc_mode) {
+
+        case USER_GC_BY_ANY:
+                return u->sessions;
+
+        case USER_GC_BY_PIN:
+                LIST_FOREACH(sessions_by_user, i, u->sessions)
+                        if (SESSION_CLASS_PIN_USER(i->class))
+                                return true;
+
+                return false;
+
+        default:
+                assert_not_reached();
+        }
+}
+
 bool user_may_gc(User *u, bool drop_not_started) {
         int r;
 
@@ -659,7 +709,7 @@ bool user_may_gc(User *u, bool drop_not_started) {
         if (drop_not_started && !u->started)
                 return true;
 
-        if (u->sessions)
+        if (user_pinned_by_sessions(u))
                 return false;
 
         if (u->last_session_timestamp != USEC_INFINITY) {
@@ -718,22 +768,26 @@ UserState user_get_state(User *u) {
         if (!u->started || u->service_job)
                 return USER_OPENING;
 
-        if (u->sessions) {
-                bool all_closing = true;
+        bool any = false, all_closing = true;
+        LIST_FOREACH(sessions_by_user, i, u->sessions) {
+                SessionState state;
 
-                LIST_FOREACH(sessions_by_user, i, u->sessions) {
-                        SessionState state;
+                /* Ignore sessions that don't pin the user, i.e. are not supposed to have an effect on user state */
+                if (!SESSION_CLASS_PIN_USER(i->class))
+                        continue;
 
-                        state = session_get_state(i);
-                        if (state == SESSION_ACTIVE)
-                                return USER_ACTIVE;
-                        if (state != SESSION_CLOSING)
-                                all_closing = false;
-                }
+                state = session_get_state(i);
+                if (state == SESSION_ACTIVE)
+                        return USER_ACTIVE;
+                if (state != SESSION_CLOSING)
+                        all_closing = false;
 
-                return all_closing ? USER_CLOSING : USER_ONLINE;
+                any = true;
         }
 
+        if (any)
+                return all_closing ? USER_CLOSING : USER_ONLINE;
+
         if (user_check_linger_file(u) > 0 && user_unit_active(u))
                 return USER_LINGERING;
 
@@ -750,7 +804,7 @@ static bool elect_display_filter(Session *s) {
         /* Return true if the session is a candidate for the user’s ‘primary session’ or ‘display’. */
         assert(s);
 
-        return IN_SET(s->class, SESSION_USER, SESSION_GREETER) && s->started && !s->stopping;
+        return SESSION_CLASS_CAN_DISPLAY(s->class) && s->started && !s->stopping;
 }
 
 static int elect_display_compare(Session *s1, Session *s2) {
@@ -782,6 +836,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);
 
@@ -825,7 +882,7 @@ void user_update_last_session_timer(User *u) {
 
         assert(u);
 
-        if (u->sessions) {
+        if (user_pinned_by_sessions(u)) {
                 /* There are sessions, turn off the timer */
                 u->last_session_timestamp = USEC_INFINITY;
                 u->timer_event_source = sd_event_source_unref(u->timer_event_source);
@@ -873,6 +930,13 @@ static const char* const user_state_table[_USER_STATE_MAX] = {
 
 DEFINE_STRING_TABLE_LOOKUP(user_state, UserState);
 
+static const char* const user_gc_mode_table[_USER_GC_MODE_MAX] = {
+        [USER_GC_BY_PIN] = "pin",
+        [USER_GC_BY_ANY] = "any",
+};
+
+DEFINE_STRING_TABLE_LOOKUP(user_gc_mode, UserGCMode);
+
 int config_parse_tmpfs_size(
                 const char* unit,
                 const char *filename,
index 21b9f8f348ec5d73b1eecbfd534ef78e783e1a70..9bda5dde4218139daf25bbbb690426392f380a76 100644 (file)
@@ -19,6 +19,13 @@ typedef enum UserState {
         _USER_STATE_INVALID = -EINVAL,
 } UserState;
 
+typedef enum UserGCMode {
+        USER_GC_BY_ANY,         /* any session pins this user */
+        USER_GC_BY_PIN,         /* only sessions with an explicitly pinning class pin this user */
+        _USER_GC_MODE_MAX,
+        _USER_GC_MODE_INVALID = -EINVAL,
+} UserGCMode;
+
 struct User {
         Manager *manager;
 
@@ -41,6 +48,7 @@ struct User {
         /* Set up when the last session of the user logs out */
         sd_event_source *timer_event_source;
 
+        UserGCMode gc_mode;
         bool in_gc_queue:1;
 
         bool started:1;       /* Whenever the user being started, has been started or is being stopped again. */
@@ -57,6 +65,7 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(User *, user_free);
 
 bool user_may_gc(User *u, bool drop_not_started);
 void user_add_to_gc_queue(User *u);
+void user_start_service_manager(User *u);
 int user_start(User *u);
 int user_stop(User *u, bool force);
 int user_finalize(User *u);
@@ -72,4 +81,7 @@ void user_update_last_session_timer(User *u);
 const char* user_state_to_string(UserState s) _const_;
 UserState user_state_from_string(const char *s) _pure_;
 
+const char* user_gc_mode_to_string(UserGCMode m) _const_;
+UserGCMode user_gc_mode_from_string(const char *s) _pure_;
+
 CONFIG_PARSER_PROTOTYPE(config_parse_compat_user_tasks_max);
index 030f65da088a32411daa2962dd0bf62dfc738ddb..7b6467d63a040175b9f50bf5c62b6ed5ceda44bc 100644 (file)
@@ -438,6 +438,13 @@ static int deliver_session_leader_fd_consume(Session *s, const char *fdname, int
         assert(fdname);
         assert(fd >= 0);
 
+        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;
+        }
+
         if (!s->leader_fd_saved)
                 log_warning("Got leader pidfd for session '%s', but not recorded in session state, proceeding anyway.",
                             s->id);
@@ -450,13 +457,9 @@ static int deliver_session_leader_fd_consume(Session *s, const char *fdname, int
                         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);
-
-                close_and_notify_warn(fd, fdname);
-                return r;
+                goto fail_close;
         }
 
-        assert(pid_is_valid(s->deserialized_pid));
-
         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);
@@ -466,11 +469,15 @@ static int deliver_session_leader_fd_consume(Session *s, const char *fdname, int
                 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;
+        dev_t devno = 0; /* Explicit initialization to appease gcc */
         Session *s;
         int r;
 
index 8ba094bcff18ce8e505b740582464c27ebeb9c32..e49496fce840be9deb22f9dbf3942b510b6d7ca8 100644 (file)
                        send_interface="org.freedesktop.login1.Manager"
                        send_member="ListSessions"/>
 
+                <allow send_destination="org.freedesktop.login1"
+                       send_interface="org.freedesktop.login1.Manager"
+                       send_member="ListSessionsEx"/>
+
                 <allow send_destination="org.freedesktop.login1"
                        send_interface="org.freedesktop.login1.Manager"
                        send_member="ListUsers"/>
                        send_interface="org.freedesktop.login1.Manager"
                        send_member="SuspendThenHibernateWithFlags"/>
 
+                <allow send_destination="org.freedesktop.login1"
+                       send_interface="org.freedesktop.login1.Manager"
+                       send_member="Sleep"/>
+
                 <allow send_destination="org.freedesktop.login1"
                        send_interface="org.freedesktop.login1.Manager"
                        send_member="CanPowerOff"/>
                        send_interface="org.freedesktop.login1.Manager"
                        send_member="CanSuspendThenHibernate"/>
 
+                <allow send_destination="org.freedesktop.login1"
+                       send_interface="org.freedesktop.login1.Manager"
+                       send_member="CanSleep"/>
+
                 <allow send_destination="org.freedesktop.login1"
                        send_interface="org.freedesktop.login1.Manager"
                        send_member="ScheduleShutdown"/>
index 20ec5530d9a0ffabcbd6028fdc859009f8a912b1..d7814a7275dfb4f4ee96325036eb30ce04dc440b 100644 (file)
@@ -936,30 +936,21 @@ _public_ PAM_EXTERN int pam_sm_open_session(
         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. */
-
-        if (streq_ptr(service, "systemd-user")) {
-                char rt[STRLEN("/run/user/") + DECIMAL_STR_MAX(uid_t)];
-
-                xsprintf(rt, "/run/user/"UID_FMT, ur->uid);
-                r = configure_runtime_directory(handle, ur, rt);
-                if (r != PAM_SUCCESS)
-                        return r;
-
-                goto success;
-        }
-
-        /* Otherwise, we ask logind to create a session for us */
-
         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);
 
-        if (tty && strchr(tty, ':')) {
+        if (streq_ptr(service, "systemd-user")) {
+                /* If we detect that we are running in the "systemd-user" PAM stack, then let's patch the class to
+                 * 'manager' if not set, simply for robustness reasons. */
+                type = "unspecified";
+                class = IN_SET(user_record_disposition(ur), USER_INTRINSIC, USER_SYSTEM, USER_DYNAMIC) ?
+                        "manager-early" : "manager";
+                tty = NULL;
+
+        } else 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))
@@ -1011,7 +1002,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 4e830a4bc23220c65301c91dffde8907e5256c91..108fe0327b5ff0c95a0f5da6afb5473f2f4884e3 100644 (file)
@@ -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..4a3ccd6a99239648e39123cda8c17752448b86b6 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"
 
@@ -25,7 +28,13 @@ static int network_save(Network *network, const char *dest_dir) {
 
         assert(network);
 
-        r = generator_open_unit_file_full(dest_dir, NULL, NULL, &f, &temp_path);
+        r = generator_open_unit_file_full(
+                        dest_dir,
+                        /* source= */ NULL,
+                        /* name= */ NULL,
+                        &f,
+                        /* ret_final_path= */ NULL,
+                        &temp_path);
         if (r < 0)
                 return r;
 
@@ -53,7 +62,13 @@ static int netdev_save(NetDev *netdev, const char *dest_dir) {
 
         assert(netdev);
 
-        r = generator_open_unit_file_full(dest_dir, NULL, NULL, &f, &temp_path);
+        r = generator_open_unit_file_full(
+                        dest_dir,
+                        /* source= */ NULL,
+                        /* name= */ NULL,
+                        &f,
+                        /* ret_final_path= */ NULL,
+                        &temp_path);
         if (r < 0)
                 return r;
 
@@ -78,7 +93,13 @@ static int link_save(Link *link, const char *dest_dir) {
 
         assert(link);
 
-        r = generator_open_unit_file_full(dest_dir, NULL, NULL, &f, &temp_path);
+        r = generator_open_unit_file_full(
+                        dest_dir,
+                        /* source= */ NULL,
+                        /* name= */ NULL,
+                        &f,
+                        /* ret_final_path= */ NULL,
+                        &temp_path);
         if (r < 0)
                 return r;
 
@@ -122,6 +143,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 +265,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 +303,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 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..0195dce1bf04c6654623661621e869dcb4b541ca 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) {
@@ -1109,20 +1196,33 @@ static int wireguard_verify(NetDev *netdev, const char *filename) {
                         if (r < 0)
                                 return log_oom();
 
+                        /* For route_section_verify() below. */
+                        r = config_section_new(peer->section->filename, peer->section->line, &route->section);
+                        if (r < 0)
+                                return log_oom();
+
+                        route->source = NETWORK_CONFIG_SOURCE_STATIC;
                         route->family = ipmask->family;
                         route->dst = ipmask->ip;
                         route->dst_prefixlen = ipmask->cidr;
-                        route->scope = RT_SCOPE_UNIVERSE;
                         route->protocol = RTPROT_STATIC;
+                        route->protocol_set = true;
                         route->table = peer->route_table_set ? peer->route_table : w->route_table;
+                        route->table_set = true;
                         route->priority = peer->route_priority_set ? peer->route_priority : w->route_priority;
-                        if (route->priority == 0 && route->family == AF_INET6)
-                                route->priority = IP6_RT_PRIO_USER;
-                        route->source = NETWORK_CONFIG_SOURCE_STATIC;
+                        route->priority_set = true;
 
-                        r = set_ensure_consume(&w->routes, &route_hash_ops, TAKE_PTR(route));
+                        if (route_section_verify(route) < 0)
+                                continue;
+
+                        r = set_ensure_put(&w->routes, &route_hash_ops, route);
                         if (r < 0)
                                 return log_oom();
+                        if (r == 0)
+                                continue;
+
+                        route->wireguard = w;
+                        TAKE_PTR(route);
                 }
         }
 
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 f0924c93228f8b716768c7a2440dbf503e3a4065..fc6a708f87d879785ac2bbdf05db5c2d2b721b9c 100644 (file)
@@ -133,14 +133,40 @@ void link_get_address_states(
                 *ret_all = address_state_from_scope(MIN(ipv4_scope, ipv6_scope));
 }
 
+static void address_hash_func(const Address *a, struct siphash *state);
+static int address_compare_func(const Address *a1, const Address *a2);
+static void address_detach(Address *address);
+
+DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(
+        address_hash_ops_detach,
+        Address,
+        address_hash_func,
+        address_compare_func,
+        address_detach);
+
+DEFINE_HASH_OPS(
+        address_hash_ops,
+        Address,
+        address_hash_func,
+        address_compare_func);
+
+DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR(
+        address_section_hash_ops,
+        ConfigSection,
+        config_section_hash_func,
+        config_section_compare_func,
+        Address,
+        address_detach);
+
 int address_new(Address **ret) {
-        _cleanup_(address_freep) Address *address = NULL;
+        _cleanup_(address_unrefp) Address *address = NULL;
 
         address = new(Address, 1);
         if (!address)
                 return -ENOMEM;
 
         *address = (Address) {
+                .n_ref = 1,
                 .family = AF_UNSPEC,
                 .scope = RT_SCOPE_UNIVERSE,
                 .lifetime_valid_usec = USEC_INFINITY,
@@ -155,7 +181,7 @@ int address_new(Address **ret) {
 
 int address_new_static(Network *network, const char *filename, unsigned section_line, Address **ret) {
         _cleanup_(config_section_freep) ConfigSection *n = NULL;
-        _cleanup_(address_freep) Address *address = NULL;
+        _cleanup_(address_unrefp) Address *address = NULL;
         int r;
 
         assert(network);
@@ -186,7 +212,7 @@ int address_new_static(Network *network, const char *filename, unsigned section_
         /* This will be adjusted in address_section_verify(). */
         address->duplicate_address_detection = _ADDRESS_FAMILY_INVALID;
 
-        r = ordered_hashmap_ensure_put(&network->addresses_by_section, &config_section_hash_ops, address->section, address);
+        r = ordered_hashmap_ensure_put(&network->addresses_by_section, &address_section_hash_ops, address->section, address);
         if (r < 0)
                 return r;
 
@@ -194,9 +220,9 @@ int address_new_static(Network *network, const char *filename, unsigned section_
         return 0;
 }
 
-Address *address_free(Address *address) {
-        if (!address)
-                return NULL;
+static Address* address_detach_impl(Address *address) {
+        assert(address);
+        assert(!address->link || !address->network);
 
         if (address->network) {
                 assert(address->section);
@@ -204,11 +230,33 @@ Address *address_free(Address *address) {
 
                 if (address->network->dhcp_server_address == address)
                         address->network->dhcp_server_address = NULL;
+
+                address->network = NULL;
+                return address;
         }
 
-        if (address->link)
+        if (address->link) {
                 set_remove(address->link->addresses, address);
 
+                address->link = NULL;
+                return address;
+        }
+
+        return NULL;
+}
+
+static void address_detach(Address *address) {
+        assert(address);
+
+        address_unref(address_detach_impl(address));
+}
+
+static Address* address_free(Address *address) {
+        if (!address)
+                return NULL;
+
+        address_detach_impl(address);
+
         config_section_free(address->section);
         free(address->label);
         free(address->netlabel);
@@ -216,6 +264,8 @@ Address *address_free(Address *address) {
         return mfree(address);
 }
 
+DEFINE_TRIVIAL_REF_UNREF_FUNC(Address, address, address_free);
+
 static bool address_lifetime_is_valid(const Address *a) {
         assert(a);
 
@@ -466,19 +516,6 @@ static int address_compare_func(const Address *a1, const Address *a2) {
         }
 }
 
-DEFINE_HASH_OPS(
-        address_hash_ops,
-        Address,
-        address_hash_func,
-        address_compare_func);
-
-DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(
-        address_hash_ops_free,
-        Address,
-        address_hash_func,
-        address_compare_func,
-        address_free);
-
 static bool address_can_update(const Address *la, const Address *na) {
         assert(la);
         assert(la->link);
@@ -557,7 +594,7 @@ static bool address_can_update(const Address *la, const Address *na) {
 }
 
 int address_dup(const Address *src, Address **ret) {
-        _cleanup_(address_freep) Address *dest = NULL;
+        _cleanup_(address_unrefp) Address *dest = NULL;
         int r;
 
         assert(src);
@@ -567,7 +604,8 @@ int address_dup(const Address *src, Address **ret) {
         if (!dest)
                 return -ENOMEM;
 
-        /* clear all pointers */
+        /* clear the reference counter and all pointers */
+        dest->n_ref = 1;
         dest->network = NULL;
         dest->section = NULL;
         dest->link = NULL;
@@ -708,19 +746,21 @@ static void address_modify_nft_set(Address *address, bool add) {
         }
 }
 
-static int address_add(Link *link, Address *address) {
+static int address_attach(Link *link, Address *address) {
         int r;
 
         assert(link);
         assert(address);
+        assert(!address->link);
 
-        r = set_ensure_put(&link->addresses, &address_hash_ops_free, address);
+        r = set_ensure_put(&link->addresses, &address_hash_ops_detach, address);
         if (r < 0)
                 return r;
         if (r == 0)
                 return -EEXIST;
 
         address->link = link;
+        address_ref(address);
         return 0;
 }
 
@@ -781,7 +821,7 @@ static int address_drop(Address *address) {
 
         ipv4acd_detach(link, address);
 
-        address_free(address);
+        address_detach(address);
 
         link_update_operstate(link, /* also_update_master = */ true);
         link_check_ready(link);
@@ -910,7 +950,7 @@ int link_get_address(Link *link, int family, const union in_addr_union *address,
          * arbitrary prefixlen will be returned. */
 
         if (family == AF_INET6 || prefixlen != 0) {
-                _cleanup_(address_freep) Address *tmp = NULL;
+                _cleanup_(address_unrefp) Address *tmp = NULL;
 
                 /* In this case, we can use address_get(). */
 
@@ -1071,18 +1111,35 @@ static int address_set_netlink_message(const Address *address, sd_netlink_messag
         return 0;
 }
 
-static int address_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
+static int address_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, RemoveRequest *rreq) {
         int r;
 
         assert(m);
-        assert(link);
+        assert(rreq);
 
-        if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
+        Link *link = ASSERT_PTR(rreq->link);
+        Address *address = ASSERT_PTR(rreq->userdata);
+
+        if (link->state == LINK_STATE_LINGER)
                 return 0;
 
         r = sd_netlink_message_get_errno(m);
-        if (r < 0 && r != -EADDRNOTAVAIL)
-                log_link_message_warning_errno(link, m, r, "Could not drop address");
+        if (r < 0) {
+                log_link_message_full_errno(link, m,
+                                            (r == -EADDRNOTAVAIL || !address->link) ? LOG_DEBUG : LOG_WARNING,
+                                            r, "Could not drop address");
+
+                if (address->link) {
+                        /* If the address cannot be removed, then assume the address is already removed. */
+                        log_address_debug(address, "Forgetting", link);
+
+                        Request *req;
+                        if (address_get_request(link, address, &req) >= 0)
+                                address_enter_removed(req->userdata);
+
+                        (void) address_drop(address);
+                }
+        }
 
         return 1;
 }
@@ -1109,13 +1166,9 @@ int address_remove(Address *address, Link *link) {
         if (r < 0)
                 return log_link_warning_errno(link, r, "Could not set netlink attributes: %m");
 
-        r = netlink_call_async(link->manager->rtnl, NULL, m,
-                               address_remove_handler,
-                               link_netlink_destroy_callback, link);
+        r = link_remove_request_add(link, address, address, link->manager->rtnl, m, address_remove_handler);
         if (r < 0)
-                return log_link_warning_errno(link, r, "Could not send rtnetlink message: %m");
-
-        link_ref(link);
+                return log_link_warning_errno(link, r, "Could not queue rtnetlink message: %m");
 
         address_enter_removing(address);
 
@@ -1140,7 +1193,7 @@ int address_remove_and_cancel(Address *address, Link *link) {
          * notification about the request, then explicitly remove the address. */
         if (address_get_request(link, address, &req) >= 0) {
                 waiting = req->waiting_reply;
-                request_detach(link->manager, req);
+                request_detach(req);
                 address_cancel_requesting(address);
         }
 
@@ -1211,7 +1264,7 @@ int link_drop_ipv6ll_addresses(Link *link) {
                 return r;
 
         for (sd_netlink_message *addr = reply; addr; addr = sd_netlink_message_next(addr)) {
-                _cleanup_(address_freep) Address *a = NULL;
+                _cleanup_(address_unrefp) Address *a = NULL;
                 unsigned char flags, prefixlen;
                 struct in6_addr address;
                 int ifindex;
@@ -1356,7 +1409,7 @@ void link_foreignize_addresses(Link *link) {
 }
 
 static int address_acquire(Link *link, const Address *original, Address **ret) {
-        _cleanup_(address_freep) Address *na = NULL;
+        _cleanup_(address_unrefp) Address *na = NULL;
         union in_addr_union in_addr;
         int r;
 
@@ -1523,7 +1576,7 @@ int link_request_address(
                 address_netlink_handler_t netlink_handler,
                 Request **ret) {
 
-        _cleanup_(address_freep) Address *tmp = NULL;
+        _cleanup_(address_unrefp) Address *tmp = NULL;
         Address *existing = NULL;
         int r;
 
@@ -1568,7 +1621,7 @@ int link_request_address(
         log_address_debug(tmp, "Requesting", link);
         r = link_queue_request_safe(link, REQUEST_TYPE_ADDRESS,
                                     tmp,
-                                    address_free,
+                                    address_unref,
                                     address_hash_func,
                                     address_compare_func,
                                     address_process_request,
@@ -1644,7 +1697,7 @@ int link_request_static_addresses(Link *link) {
 }
 
 int manager_rtnl_process_address(sd_netlink *rtnl, sd_netlink_message *message, Manager *m) {
-        _cleanup_(address_freep) Address *tmp = NULL;
+        _cleanup_(address_unrefp) Address *tmp = NULL;
         struct ifa_cacheinfo cinfo;
         Link *link;
         uint16_t type;
@@ -1781,13 +1834,13 @@ int manager_rtnl_process_address(sd_netlink *rtnl, sd_netlink_message *message,
 
         if (!address) {
                 /* If we did not know the address, then save it. */
-                r = address_add(link, tmp);
+                r = address_attach(link, tmp);
                 if (r < 0) {
                         log_link_warning_errno(link, r, "Failed to save received address %s, ignoring: %m",
                                                IN_ADDR_PREFIX_TO_STRING(tmp->family, &tmp->in_addr, tmp->prefixlen));
                         return 0;
                 }
-                address = TAKE_PTR(tmp);
+                address = tmp;
 
                 is_new = true;
 
@@ -1887,7 +1940,7 @@ int config_parse_broadcast(
                 void *userdata) {
 
         Network *network = userdata;
-        _cleanup_(address_free_or_set_invalidp) Address *n = NULL;
+        _cleanup_(address_unref_or_set_invalidp) Address *n = NULL;
         union in_addr_union u;
         int r;
 
@@ -1964,7 +2017,7 @@ int config_parse_address(
                 void *userdata) {
 
         Network *network = userdata;
-        _cleanup_(address_free_or_set_invalidp) Address *n = NULL;
+        _cleanup_(address_unref_or_set_invalidp) Address *n = NULL;
         union in_addr_union buffer;
         unsigned char prefixlen;
         int r, f;
@@ -1978,7 +2031,7 @@ int config_parse_address(
         if (streq(section, "Network")) {
                 if (isempty(rvalue)) {
                         /* If an empty string specified in [Network] section, clear previously assigned addresses. */
-                        network->addresses_by_section = ordered_hashmap_free_with_destructor(network->addresses_by_section, address_free);
+                        network->addresses_by_section = ordered_hashmap_free(network->addresses_by_section);
                         return 0;
                 }
 
@@ -2051,7 +2104,7 @@ int config_parse_label(
                 void *data,
                 void *userdata) {
 
-        _cleanup_(address_free_or_set_invalidp) Address *n = NULL;
+        _cleanup_(address_unref_or_set_invalidp) Address *n = NULL;
         Network *network = userdata;
         int r;
 
@@ -2103,7 +2156,7 @@ int config_parse_lifetime(
                 void *userdata) {
 
         Network *network = userdata;
-        _cleanup_(address_free_or_set_invalidp) Address *n = NULL;
+        _cleanup_(address_unref_or_set_invalidp) Address *n = NULL;
         usec_t k;
         int r;
 
@@ -2152,7 +2205,7 @@ int config_parse_address_flags(
                 void *userdata) {
 
         Network *network = userdata;
-        _cleanup_(address_free_or_set_invalidp) Address *n = NULL;
+        _cleanup_(address_unref_or_set_invalidp) Address *n = NULL;
         int r;
 
         assert(filename);
@@ -2199,7 +2252,7 @@ int config_parse_address_scope(
                 void *userdata) {
 
         Network *network = userdata;
-        _cleanup_(address_free_or_set_invalidp) Address *n = NULL;
+        _cleanup_(address_unref_or_set_invalidp) Address *n = NULL;
         int r;
 
         assert(filename);
@@ -2243,7 +2296,7 @@ int config_parse_address_route_metric(
                 void *userdata) {
 
         Network *network = userdata;
-        _cleanup_(address_free_or_set_invalidp) Address *n = NULL;
+        _cleanup_(address_unref_or_set_invalidp) Address *n = NULL;
         int r;
 
         assert(filename);
@@ -2285,7 +2338,7 @@ int config_parse_duplicate_address_detection(
                 void *userdata) {
 
         Network *network = userdata;
-        _cleanup_(address_free_or_set_invalidp) Address *n = NULL;
+        _cleanup_(address_unref_or_set_invalidp) Address *n = NULL;
         int r;
 
         assert(filename);
@@ -2339,7 +2392,7 @@ int config_parse_address_netlabel(
                 void *userdata) {
 
         Network *network = userdata;
-        _cleanup_(address_free_or_set_invalidp) Address *n = NULL;
+        _cleanup_(address_unref_or_set_invalidp) Address *n = NULL;
         int r;
 
         assert(filename);
@@ -2481,8 +2534,8 @@ int network_drop_invalid_addresses(Network *network) {
 
                 if (address_section_verify(address) < 0) {
                         /* Drop invalid [Address] sections or Address= settings in [Network].
-                         * Note that address_free() will drop the address from addresses_by_section. */
-                        address_free(address);
+                         * Note that address_detach() will drop the address from addresses_by_section. */
+                        address_detach(address);
                         continue;
                 }
 
@@ -2495,12 +2548,13 @@ int network_drop_invalid_addresses(Network *network) {
                                     IN_ADDR_PREFIX_TO_STRING(address->family, &address->in_addr, address->prefixlen),
                                     address->section->line,
                                     dup->section->line, dup->section->line);
-                        /* address_free() will drop the address from addresses_by_section. */
-                        address_free(dup);
+
+                        /* address_detach() will drop the address from addresses_by_section. */
+                        address_detach(dup);
                 }
 
-                /* Use address_hash_ops, instead of address_hash_ops_free. Otherwise, the Address objects
-                 * will be freed. */
+                /* Use address_hash_ops, instead of address_hash_ops_detach. Otherwise, the Address objects
+                 * will be detached. */
                 r = set_ensure_put(&addresses, &address_hash_ops, address);
                 if (r < 0)
                         return log_oom();
@@ -2527,7 +2581,7 @@ int config_parse_address_ip_nft_set(
                 void *userdata) {
 
         Network *network = userdata;
-        _cleanup_(address_free_or_set_invalidp) Address *n = NULL;
+        _cleanup_(address_unref_or_set_invalidp) Address *n = NULL;
         int r;
 
         assert(filename);
index 53e7a798212578e60ffc120bb071211b59835217..cc2429094e701c7443cccc37034a9d59983ebe84 100644 (file)
@@ -34,6 +34,8 @@ struct Address {
         NetworkConfigState state;
         union in_addr_union provider; /* DHCP server or router address */
 
+        unsigned n_ref;
+
         int family;
         unsigned char prefixlen;
         unsigned char scope;
@@ -83,9 +85,11 @@ void link_get_address_states(
 
 extern const struct hash_ops address_hash_ops;
 
+Address* address_ref(Address *address);
+Address* address_unref(Address *address);
+
 int address_new(Address **ret);
 int address_new_static(Network *network, const char *filename, unsigned section_line, Address **ret);
-Address* address_free(Address *address);
 int address_get(Link *link, const Address *in, Address **ret);
 int address_get_harder(Link *link, const Address *in, Address **ret);
 int address_configure_handler_internal(sd_netlink *rtnl, sd_netlink_message *m, Link *link, const char *error_msg);
@@ -95,7 +99,7 @@ int address_dup(const Address *src, Address **ret);
 bool address_is_ready(const Address *a);
 bool link_check_addresses_ready(Link *link, NetworkConfigSource source);
 
-DEFINE_SECTION_CLEANUP_FUNCTIONS(Address, address_free);
+DEFINE_SECTION_CLEANUP_FUNCTIONS(Address, address_unref);
 
 int link_drop_managed_addresses(Link *link);
 int link_drop_foreign_addresses(Link *link);
index f5d97dd7af2399903a6e559b5cf0632b04d23510..b30527e99fcca89406cb7b2ad0637f7f25ac7fd2 100644 (file)
@@ -265,7 +265,7 @@ static int dhcp_pd_route_handler(sd_netlink *rtnl, sd_netlink_message *m, Reques
 
         assert(link);
 
-        r = route_configure_handler_internal(rtnl, m, link, "Failed to add prefix route for DHCP delegated subnet prefix");
+        r = route_configure_handler_internal(rtnl, m, link, route, "Failed to add prefix route for DHCP delegated subnet prefix");
         if (r <= 0)
                 return r;
 
@@ -366,7 +366,7 @@ static int dhcp_pd_request_address(
                 return log_link_warning_errno(link, r, "Failed to generate addresses for acquired DHCP delegated prefix: %m");
 
         SET_FOREACH(a, addresses) {
-                _cleanup_(address_freep) Address *address = NULL;
+                _cleanup_(address_unrefp) Address *address = NULL;
                 Address *existing;
 
                 r = address_new(&address);
@@ -625,7 +625,7 @@ static int dhcp4_unreachable_route_handler(sd_netlink *rtnl, sd_netlink_message
 
         assert(link);
 
-        r = route_configure_handler_internal(rtnl, m, link, "Failed to set unreachable route for DHCPv4 delegated prefix");
+        r = route_configure_handler_internal(rtnl, m, link, route, "Failed to set unreachable route for DHCPv4 delegated prefix");
         if (r <= 0)
                 return r;
 
@@ -641,7 +641,7 @@ static int dhcp6_unreachable_route_handler(sd_netlink *rtnl, sd_netlink_message
 
         assert(link);
 
-        r = route_configure_handler_internal(rtnl, m, link, "Failed to set unreachable route for DHCPv6 delegated prefix");
+        r = route_configure_handler_internal(rtnl, m, link, route, "Failed to set unreachable route for DHCPv6 delegated prefix");
         if (r <= 0)
                 return r;
 
@@ -782,8 +782,8 @@ static int dhcp4_pd_request_default_gateway_on_6rd_tunnel(Link *link, const stru
 
         route->source = NETWORK_CONFIG_SOURCE_DHCP_PD;
         route->family = AF_INET6;
-        route->gw_family = AF_INET6;
-        route->gw.in6.s6_addr32[3] = br_address->s_addr;
+        route->nexthop.family = AF_INET6;
+        route->nexthop.gw.in6.s6_addr32[3] = br_address->s_addr;
         route->scope = RT_SCOPE_UNIVERSE;
         route->protocol = RTPROT_DHCP;
         route->priority = IP6_RT_PRIO_USER;
index a486fd892752e272723f37174109b0c17c7513cd..81ef6d80f759156c4b12bf3f4cdc19c83a4490df 100644 (file)
@@ -92,7 +92,7 @@ int network_adjust_dhcp_server(Network *network, Set **addresses) {
                 }
 
         } else {
-                _cleanup_(address_freep) Address *a = NULL;
+                _cleanup_(address_unrefp) Address *a = NULL;
                 Address *existing;
                 unsigned line;
 
index 8aee30e7266435f7f9ec4b7bfc41945caa28a739..b56106867bd529b65b9983ae4a61f65fc744ac14 100644 (file)
@@ -342,12 +342,9 @@ static int dhcp4_route_handler(sd_netlink *rtnl, sd_netlink_message *m, Request
         assert(m);
         assert(link);
 
-        r = sd_netlink_message_get_errno(m);
-        if (r < 0 && r != -EEXIST) {
-                log_link_message_warning_errno(link, m, r, "Could not set DHCPv4 route");
-                link_enter_failed(link);
-                return 1;
-        }
+        r = route_configure_handler_internal(rtnl, m, link, route, "Could not set DHCPv4 route");
+        if (r <= 0)
+                return r;
 
         r = dhcp4_check_ready(link);
         if (r < 0)
@@ -380,14 +377,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;
@@ -481,8 +482,8 @@ static int dhcp4_request_route_auto(
                                        IPV4_ADDRESS_FMT_VAL(route->dst.in), route->dst_prefixlen, IPV4_ADDRESS_FMT_VAL(*gw));
 
                 route->scope = RT_SCOPE_HOST;
-                route->gw_family = AF_UNSPEC;
-                route->gw = IN_ADDR_NULL;
+                route->nexthop.family = AF_UNSPEC;
+                route->nexthop.gw = IN_ADDR_NULL;
                 route->prefsrc = IN_ADDR_NULL;
 
         } else if (in4_addr_equal(&route->dst.in, &address)) {
@@ -492,8 +493,8 @@ static int dhcp4_request_route_auto(
                                        IPV4_ADDRESS_FMT_VAL(route->dst.in), route->dst_prefixlen, IPV4_ADDRESS_FMT_VAL(*gw));
 
                 route->scope = RT_SCOPE_HOST;
-                route->gw_family = AF_UNSPEC;
-                route->gw = IN_ADDR_NULL;
+                route->nexthop.family = AF_UNSPEC;
+                route->nexthop.gw = IN_ADDR_NULL;
                 route->prefsrc.in = address;
 
         } else if (in4_addr_is_null(gw)) {
@@ -515,8 +516,8 @@ static int dhcp4_request_route_auto(
                 }
 
                 route->scope = RT_SCOPE_LINK;
-                route->gw_family = AF_UNSPEC;
-                route->gw = IN_ADDR_NULL;
+                route->nexthop.family = AF_UNSPEC;
+                route->nexthop.gw = IN_ADDR_NULL;
                 route->prefsrc.in = address;
 
         } else {
@@ -525,8 +526,8 @@ static int dhcp4_request_route_auto(
                         return r;
 
                 route->scope = RT_SCOPE_UNIVERSE;
-                route->gw_family = AF_INET;
-                route->gw.in = *gw;
+                route->nexthop.family = AF_INET;
+                route->nexthop.gw.in = *gw;
                 route->prefsrc.in = address;
         }
 
@@ -618,8 +619,8 @@ static int dhcp4_request_default_gateway(Link *link) {
                 return r;
 
         /* Next, add a default gateway. */
-        route->gw_family = AF_INET;
-        route->gw.in = router;
+        route->nexthop.family = AF_INET;
+        route->nexthop.gw.in = router;
         route->prefsrc.in = address;
 
         return dhcp4_request_route(TAKE_PTR(route), link);
@@ -640,7 +641,7 @@ static int dhcp4_request_semi_static_routes(Link *link) {
                 if (!rt->gateway_from_dhcp_or_ra)
                         continue;
 
-                if (rt->gw_family != AF_INET)
+                if (rt->nexthop.family != AF_INET)
                         continue;
 
                 assert(rt->family == AF_INET);
@@ -662,7 +663,7 @@ static int dhcp4_request_semi_static_routes(Link *link) {
                 if (r < 0)
                         return r;
 
-                route->gw.in = gw;
+                route->nexthop.gw.in = gw;
 
                 r = dhcp4_request_route(TAKE_PTR(route), link);
                 if (r < 0)
@@ -879,7 +880,7 @@ static int dhcp4_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Reques
 }
 
 static int dhcp4_request_address(Link *link, bool announce) {
-        _cleanup_(address_freep) Address *addr = NULL;
+        _cleanup_(address_unrefp) Address *addr = NULL;
         struct in_addr address, server;
         uint8_t prefixlen;
         Address *existing;
index 6d8b3c3ce26bc6f92f510f9ad954ade7ad0854c0..0024adb816bf833d5b13942aacbf356587bfa5a1 100644 (file)
@@ -191,7 +191,7 @@ static int dhcp6_request_address(
                 usec_t lifetime_preferred_usec,
                 usec_t lifetime_valid_usec) {
 
-        _cleanup_(address_freep) Address *addr = NULL;
+        _cleanup_(address_unrefp) Address *addr = NULL;
         Address *existing;
         int r;
 
index 629a3734095f2f1c872f154a07c1ed0ac7b96155..299aaedd070a66da4c410f78f0853764ca482ccb 100644 (file)
@@ -28,7 +28,7 @@ bool link_ipv4ll_enabled(Link *link) {
 }
 
 static int address_new_from_ipv4ll(Link *link, Address **ret) {
-        _cleanup_(address_freep) Address *address = NULL;
+        _cleanup_(address_unrefp) Address *address = NULL;
         struct in_addr addr;
         int r;
 
@@ -56,7 +56,7 @@ static int address_new_from_ipv4ll(Link *link, Address **ret) {
 }
 
 static int ipv4ll_address_lost(Link *link) {
-        _cleanup_(address_freep) Address *address = NULL;
+        _cleanup_(address_unrefp) Address *address = NULL;
         int r;
 
         assert(link);
@@ -92,7 +92,7 @@ static int ipv4ll_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Reque
 }
 
 static int ipv4ll_address_claimed(sd_ipv4ll *ll, Link *link) {
-        _cleanup_(address_freep) Address *address = NULL;
+        _cleanup_(address_unrefp) Address *address = NULL;
         int r;
 
         assert(ll);
index d972588b3ea0d5017ad8d93634ebb4699da7da0b..9ca54db5ed5408b59bf00a50a25c8611dcc9897e 100644 (file)
@@ -237,7 +237,7 @@ static int route_append_json(Route *route, JsonVariant **array) {
                                 JSON_BUILD_PAIR_INTEGER("Family", route->family),
                                 JSON_BUILD_PAIR_IN_ADDR("Destination", &route->dst, route->family),
                                 JSON_BUILD_PAIR_UNSIGNED("DestinationPrefixLength", route->dst_prefixlen),
-                                JSON_BUILD_PAIR_IN_ADDR_NON_NULL("Gateway", &route->gw, route->gw_family),
+                                JSON_BUILD_PAIR_IN_ADDR_NON_NULL("Gateway", &route->nexthop.gw, route->nexthop.family),
                                 JSON_BUILD_PAIR_CONDITION(route->src_prefixlen > 0,
                                                           "Source", JSON_BUILD_IN_ADDR(&route->src, route->family)),
                                 JSON_BUILD_PAIR_UNSIGNED_NON_ZERO("SourcePrefixLength", route->src_prefixlen),
@@ -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..49ed8f59bb1804c67d1eb46203e3edd8135fe3a7 100644 (file)
 #include "udev-util.h"
 #include "vrf.h"
 
+void link_required_operstate_for_online(Link *link, LinkOperationalStateRange *ret) {
+        assert(link);
+        assert(ret);
+
+        if (link->network && operational_state_range_is_valid(&link->network->required_operstate_for_online))
+                *ret = link->network->required_operstate_for_online;
+        else if (link->iftype == ARPHRD_CAN)
+                *ret = (const LinkOperationalStateRange) {
+                        .min = LINK_OPERSTATE_CARRIER,
+                        .max = LINK_OPERSTATE_CARRIER,
+                };
+        else
+                *ret = LINK_OPERSTATE_RANGE_DEFAULT;
+}
+
 bool link_ipv6_enabled(Link *link) {
         assert(link);
 
@@ -381,6 +396,8 @@ int link_stop_engines(Link *link, bool may_keep_dhcp) {
 }
 
 void link_enter_failed(Link *link) {
+        int r;
+
         assert(link);
 
         if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
@@ -390,7 +407,22 @@ void link_enter_failed(Link *link) {
 
         link_set_state(link, LINK_STATE_FAILED);
 
-        (void) link_stop_engines(link, false);
+        if (!ratelimit_below(&link->automatic_reconfigure_ratelimit)) {
+                log_link_warning(link, "The interface entered the failed state frequently, refusing to reconfigure it automatically.");
+                goto stop;
+        }
+
+        log_link_info(link, "Trying to reconfigure the interface.");
+        r = link_reconfigure(link, /* force = */ true);
+        if (r < 0) {
+                log_link_warning_errno(link, r, "Failed to reconfigure interface: %m");
+                goto stop;
+        }
+
+        return;
+
+stop:
+        (void) link_stop_engines(link, /* may_keep_dhcp = */ false);
 }
 
 void link_check_ready(Link *link) {
@@ -416,11 +448,9 @@ void link_check_ready(Link *link) {
         if (!link->activated)
                 return (void) log_link_debug(link, "%s(): link is not activated.", __func__);
 
-        if (link->iftype == ARPHRD_CAN) {
+        if (link->iftype == ARPHRD_CAN)
                 /* let's shortcut things for CAN which doesn't need most of checks below. */
-                link_set_state(link, LINK_STATE_CONFIGURED);
-                return;
-        }
+                goto ready;
 
         if (!link->stacked_netdevs_created)
                 return (void) log_link_debug(link, "%s(): stacked netdevs are not created.", __func__);
@@ -931,15 +961,51 @@ static void link_drop_from_master(Link *link) {
         link_unref(set_remove(master->slaves, link));
 }
 
-static void link_drop_requests(Link *link) {
+static int link_drop_requests(Link *link) {
         Request *req;
+        int ret = 0;
 
         assert(link);
         assert(link->manager);
 
-        ORDERED_SET_FOREACH(req, link->manager->request_queue)
-                if (req->link == link)
-                        request_detach(link->manager, req);
+        ORDERED_SET_FOREACH(req, link->manager->request_queue) {
+                if (req->link != link)
+                        continue;
+
+                /* If the request is already called, but its reply is not received, then we need to
+                 * drop the configuration (e.g. address) here. Note, if the configuration is known,
+                 * it will be handled later by link_drop_foreign_addresses() or so. */
+                if (req->waiting_reply && link->state != LINK_STATE_LINGER)
+                        switch (req->type) {
+                        case REQUEST_TYPE_ADDRESS: {
+                                Address *address = ASSERT_PTR(req->userdata);
+
+                                if (address_get(link, address, NULL) < 0)
+                                        RET_GATHER(ret, address_remove(address, link));
+                                break;
+                        }
+                        case REQUEST_TYPE_NEIGHBOR: {
+                                Neighbor *neighbor = ASSERT_PTR(req->userdata);
+
+                                if (neighbor_get(link, neighbor, NULL) < 0)
+                                        RET_GATHER(ret, neighbor_remove(neighbor, link));
+                                break;
+                        }
+                        case REQUEST_TYPE_NEXTHOP: {
+                                NextHop *nexthop = ASSERT_PTR(req->userdata);
+
+                                if (nexthop_get_by_id(link->manager, nexthop->id, NULL) < 0)
+                                        RET_GATHER(ret, nexthop_remove(nexthop, link->manager));
+                                break;
+                        }
+                        default:
+                                ;
+                        }
+
+                request_detach(req);
+        }
+
+        return ret;
 }
 
 static Link *link_drop(Link *link) {
@@ -953,7 +1019,7 @@ static Link *link_drop(Link *link) {
         /* Drop all references from other links and manager. Note that async netlink calls may have
          * references to the link, and they will be dropped when we receive replies. */
 
-        link_drop_requests(link);
+        (void) link_drop_requests(link);
 
         link_free_bound_to_list(link);
         link_free_bound_by_list(link);
@@ -1263,7 +1329,9 @@ int link_reconfigure_impl(Link *link, bool force) {
         if (r < 0)
                 return r;
 
-        link_drop_requests(link);
+        r = link_drop_requests(link);
+        if (r < 0)
+                return r;
 
         if (network && !force && network->keep_configuration != KEEP_CONFIGURATION_YES)
                 /* When a new/updated .network file is assigned, first make all configs (addresses,
@@ -1665,7 +1733,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)
@@ -1792,12 +1860,16 @@ void link_update_operstate(Link *link, bool also_update_master) {
         else
                 operstate = LINK_OPERSTATE_ENSLAVED;
 
+        LinkOperationalStateRange req;
+        link_required_operstate_for_online(link, &req);
+
         /* Only determine online state for managed links with RequiredForOnline=yes */
         if (!link->network || !link->network->required_for_online)
                 online_state = _LINK_ONLINE_STATE_INVALID;
-        else if (operstate < link->network->required_operstate_for_online.min ||
-                 operstate > link->network->required_operstate_for_online.max)
+
+        else if (!operational_state_is_in_range(operstate, &req))
                 online_state = LINK_ONLINE_STATE_OFFLINE;
+
         else {
                 AddressFamily required_family = link->network->required_family_for_online;
                 bool needs_ipv4 = required_family & ADDRESS_FAMILY_IPV4;
@@ -1808,14 +1880,14 @@ void link_update_operstate(Link *link, bool also_update_master) {
                  * to offline in the blocks below. */
                 online_state = LINK_ONLINE_STATE_ONLINE;
 
-                if (link->network->required_operstate_for_online.min >= LINK_OPERSTATE_DEGRADED) {
+                if (req.min >= LINK_OPERSTATE_DEGRADED) {
                         if (needs_ipv4 && ipv4_address_state < LINK_ADDRESS_STATE_DEGRADED)
                                 online_state = LINK_ONLINE_STATE_OFFLINE;
                         if (needs_ipv6 && ipv6_address_state < LINK_ADDRESS_STATE_DEGRADED)
                                 online_state = LINK_ONLINE_STATE_OFFLINE;
                 }
 
-                if (link->network->required_operstate_for_online.min >= LINK_OPERSTATE_ROUTABLE) {
+                if (req.min >= LINK_OPERSTATE_ROUTABLE) {
                         if (needs_ipv4 && ipv4_address_state < LINK_ADDRESS_STATE_ROUTABLE)
                                 online_state = LINK_ONLINE_STATE_OFFLINE;
                         if (needs_ipv6 && ipv6_address_state < LINK_ADDRESS_STATE_ROUTABLE)
@@ -2000,20 +2072,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)
@@ -2518,6 +2588,7 @@ static int link_new(Manager *manager, sd_netlink_message *message, Link **ret) {
                 .n_ref = 1,
                 .state = LINK_STATE_PENDING,
                 .online_state = _LINK_ONLINE_STATE_INVALID,
+                .automatic_reconfigure_ratelimit = (const RateLimit) { .interval = 10 * USEC_PER_SEC, .burst = 5 },
                 .ifindex = ifindex,
                 .iftype = iftype,
                 .ifname = TAKE_PTR(ifname),
index cad9fa8410685fa20b968c66ef34d7623945012a..b5b1995361a77ad38d11a6d8d5332d16419f6218 100644 (file)
@@ -25,6 +25,7 @@
 #include "networkd-ipv6ll.h"
 #include "networkd-util.h"
 #include "ordered-set.h"
+#include "ratelimit.h"
 #include "resolve-util.h"
 #include "set.h"
 
@@ -106,6 +107,7 @@ typedef struct Link {
         LinkAddressState ipv4_address_state;
         LinkAddressState ipv6_address_state;
         LinkOnlineState online_state;
+        RateLimit automatic_reconfigure_ratelimit;
 
         unsigned static_address_messages;
         unsigned static_address_label_messages;
@@ -257,3 +259,5 @@ int manager_rtnl_process_link(sd_netlink *rtnl, sd_netlink_message *message, Man
 
 int link_flags_to_string_alloc(uint32_t flags, char **ret);
 const char *kernel_operstate_to_string(int t) _const_;
+
+void link_required_operstate_for_online(Link *link, LinkOperationalStateRange *ret);
index a8906f81c1ba606f0326a73979b31e6a5438d8cd..035537c869aa73ca33ecd94e060bc5fa250ee955 100644 (file)
@@ -279,6 +279,31 @@ static int property_get_namespace_id(
         return sd_bus_message_append(reply, "t", id);
 }
 
+static int property_get_namespace_nsid(
+                sd_bus *bus,
+                const char *path,
+                const char *interface,
+                const char *property,
+                sd_bus_message *reply,
+                void *userdata,
+                sd_bus_error *error) {
+
+        uint32_t nsid = UINT32_MAX;
+        int r;
+
+        assert(bus);
+        assert(reply);
+
+        /* Returns our own "nsid", which is another ID for the network namespace, different from the inode
+         * number. */
+
+        r = netns_get_nsid(/* netnsfd= */ -EBADF, &nsid);
+        if (r < 0)
+                log_warning_errno(r, "Failed to query network nsid, ignoring: %m");
+
+        return sd_bus_message_append(reply, "u", nsid);
+}
+
 static const sd_bus_vtable manager_vtable[] = {
         SD_BUS_VTABLE_START(0),
 
@@ -289,6 +314,7 @@ static const sd_bus_vtable manager_vtable[] = {
         SD_BUS_PROPERTY("IPv6AddressState", "s", property_get_address_state, offsetof(Manager, ipv6_address_state), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
         SD_BUS_PROPERTY("OnlineState", "s", property_get_online_state, offsetof(Manager, online_state), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
         SD_BUS_PROPERTY("NamespaceId", "t", property_get_namespace_id, 0, SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("NamespaceNSID", "u", property_get_namespace_nsid, 0, 0),
 
         SD_BUS_METHOD_WITH_ARGS("ListLinks",
                                 SD_BUS_NO_ARGS,
index d9750d9b641f04133aaffc98cd64156bd3b36ac7..30aebb5d9e1a6ed621c667fbff3a504471b1f4da 100644 (file)
@@ -25,23 +25,35 @@ static int vl_method_get_states(Varlink *link, JsonVariant *parameters, VarlinkM
 }
 
 static int vl_method_get_namespace_id(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata) {
-        uint64_t id;
+        uint64_t inode = 0;
+        uint32_t nsid = UINT32_MAX;
+        int r;
 
         assert(link);
 
         if (json_variant_elements(parameters) > 0)
                 return varlink_error_invalid_parameter(link, parameters);
 
+        /* Network namespaces have two identifiers: the inode number (which all namespace types have), and
+         * the "nsid" (aka the "cookie"), which only network namespaces know as a concept, and which is not
+         * assigned by default, but once it is, is fixed. Let's return both, to avoid any confusion which one
+         * this is. */
+
         struct stat st;
-        if (stat("/proc/self/ns/net", &st) < 0) {
+        if (stat("/proc/self/ns/net", &st) < 0)
                 log_warning_errno(errno, "Failed to stat network namespace, ignoring: %m");
-                id = 0;
-        } else
-                id = st.st_ino;
+        else
+                inode = st.st_ino;
+
+        r = netns_get_nsid(/* netnsfd= */ -EBADF, &nsid);
+        if (r < 0)
+                log_warning_errno(r, "Failed to query network nsid, ignoring: %m");
 
         return varlink_replyb(link,
                               JSON_BUILD_OBJECT(
-                                              JSON_BUILD_PAIR_UNSIGNED("NamespaceId", id)));
+                                              JSON_BUILD_PAIR_UNSIGNED("NamespaceId", inode),
+                                              JSON_BUILD_PAIR_CONDITION(nsid == UINT32_MAX, "NamespaceNSID", JSON_BUILD_NULL),
+                                              JSON_BUILD_PAIR_CONDITION(nsid != UINT32_MAX, "NamespaceNSID", JSON_BUILD_UNSIGNED(nsid))));
 }
 
 int manager_connect_varlink(Manager *m) {
index 8933fc49776d6053b37c412ab6f9d90d293d3daf..6f3d90d64c9debe56a4637a61d9fa97051784aad 100644 (file)
@@ -424,6 +424,7 @@ static int manager_connect_rtnl(Manager *m, int fd) {
 static int manager_post_handler(sd_event_source *s, void *userdata) {
         Manager *manager = ASSERT_PTR(userdata);
 
+        (void) manager_process_remove_requests(manager);
         (void) manager_process_requests(manager);
         (void) manager_clean_all(manager);
         return 0;
@@ -594,6 +595,7 @@ Manager* manager_free(Manager *m) {
                 (void) link_stop_engines(link, true);
 
         m->request_queue = ordered_set_free(m->request_queue);
+        m->remove_request_queue = ordered_set_free(m->remove_request_queue);
 
         m->dirty_links = set_free_with_destructor(m->dirty_links, link_unref);
         m->new_wlan_ifindices = set_free(m->new_wlan_ifindices);
index 3f3569f44d5a2e27f5824bc52ae8ba04ae47526d..a97ae8ea213e67149a0d902cab6a2381a40b847d 100644 (file)
@@ -100,7 +100,9 @@ struct Manager {
 
         FirewallContext *fw_ctx;
 
+        bool request_queued;
         OrderedSet *request_queue;
+        OrderedSet *remove_request_queue;
 
         Hashmap *tuntap_fds_by_name;
 };
index 369e205a99a229b49c346772c1ac8a1dac761bf3..33d11509d3715c5e91228dcd3334e30b69c88146 100644 (file)
@@ -139,7 +139,7 @@ static int ndisc_route_handler(sd_netlink *rtnl, sd_netlink_message *m, Request
 
         assert(link);
 
-        r = route_configure_handler_internal(rtnl, m, link, "Could not set NDisc route");
+        r = route_configure_handler_internal(rtnl, m, link, route, "Could not set NDisc route");
         if (r <= 0)
                 return r;
 
@@ -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;
 
@@ -244,7 +247,7 @@ static int ndisc_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Reques
 }
 
 static int ndisc_request_address(Address *in, Link *link, sd_ndisc_router *rt) {
-        _cleanup_(address_freep) Address *address = in;
+        _cleanup_(address_unrefp) Address *address = in;
         struct in6_addr router;
         bool is_new;
         int r;
@@ -318,8 +321,8 @@ static int ndisc_router_process_default(Link *link, sd_ndisc_router *rt) {
 
                 route->family = AF_INET6;
                 route->pref = preference;
-                route->gw_family = AF_INET6;
-                route->gw.in6 = gateway;
+                route->nexthop.family = AF_INET6;
+                route->nexthop.gw.in6 = gateway;
                 route->lifetime_usec = lifetime_usec;
 
                 r = ndisc_request_route(TAKE_PTR(route), link, rt);
@@ -334,14 +337,14 @@ static int ndisc_router_process_default(Link *link, sd_ndisc_router *rt) {
                 if (!route_gw->gateway_from_dhcp_or_ra)
                         continue;
 
-                if (route_gw->gw_family != AF_INET6)
+                if (route_gw->nexthop.family != AF_INET6)
                         continue;
 
                 r = route_dup(route_gw, &route);
                 if (r < 0)
                         return r;
 
-                route->gw.in6 = gateway;
+                route->nexthop.gw.in6 = gateway;
                 if (!route->pref_set)
                         route->pref = preference;
                 route->lifetime_usec = lifetime_usec;
@@ -355,8 +358,7 @@ static int ndisc_router_process_default(Link *link, sd_ndisc_router *rt) {
 }
 
 static int ndisc_router_process_icmp6_ratelimit(Link *link, sd_ndisc_router *rt) {
-        char buf[DECIMAL_STR_MAX(usec_t)];
-        usec_t icmp6_ratelimit;
+        usec_t icmp6_ratelimit, msec;
         int r;
 
         assert(link);
@@ -372,14 +374,17 @@ static int ndisc_router_process_icmp6_ratelimit(Link *link, sd_ndisc_router *rt)
                 return 0;
         }
 
+        /* We do not allow 0 here. */
         if (!timestamp_is_set(icmp6_ratelimit))
                 return 0;
 
+        msec = DIV_ROUND_UP(icmp6_ratelimit, USEC_PER_MSEC);
+        if (msec <= 0 || msec > INT_MAX)
+                return 0;
+
         /* Limit the maximal rates for sending ICMPv6 packets. 0 to disable any limiting, otherwise the
          * minimal space between responses in milliseconds. Default: 1000. */
-        xsprintf(buf, USEC_FMT, DIV_ROUND_UP(icmp6_ratelimit, USEC_PER_MSEC));
-
-        r = sysctl_write_ip_property(AF_INET6, NULL, "icmp/ratelimit", buf);
+        r = sysctl_write_ip_property_int(AF_INET6, NULL, "icmp/ratelimit", (int) msec);
         if (r < 0)
                 log_link_warning_errno(link, r, "Failed to apply ICMP6 ratelimit, ignoring: %m");
 
@@ -432,7 +437,7 @@ static int ndisc_router_process_autonomous_prefix(Link *link, sd_ndisc_router *r
                 return log_link_warning_errno(link, r, "Failed to generate SLAAC addresses: %m");
 
         SET_FOREACH(a, addresses) {
-                _cleanup_(address_freep) Address *address = NULL;
+                _cleanup_(address_unrefp) Address *address = NULL;
 
                 r = address_new(&address);
                 if (r < 0)
@@ -621,8 +626,8 @@ static int ndisc_router_process_route(Link *link, sd_ndisc_router *rt) {
 
         route->family = AF_INET6;
         route->pref = preference;
-        route->gw.in6 = gateway;
-        route->gw_family = AF_INET6;
+        route->nexthop.gw.in6 = gateway;
+        route->nexthop.family = AF_INET6;
         route->dst.in6 = dst;
         route->dst_prefixlen = prefixlen;
         route->lifetime_usec = lifetime_usec;
index e172c564b006d852fe80fefc40918de187cb745f..d30282fe1ee547e25e9ac5c70400f4b85b5e28b9 100644 (file)
 #include "networkd-queue.h"
 #include "set.h"
 
-Neighbor *neighbor_free(Neighbor *neighbor) {
-        if (!neighbor)
-                return NULL;
+static Neighbor* neighbor_detach_impl(Neighbor *neighbor) {
+        assert(neighbor);
+        assert(!neighbor->link || !neighbor->network);
 
         if (neighbor->network) {
                 assert(neighbor->section);
                 ordered_hashmap_remove(neighbor->network->neighbors_by_section, neighbor->section);
+                neighbor->network = NULL;
+                return neighbor;
         }
 
-        config_section_free(neighbor->section);
-
-        if (neighbor->link)
+        if (neighbor->link) {
                 set_remove(neighbor->link->neighbors, neighbor);
+                neighbor->link = NULL;
+                return neighbor;
+        }
 
+        return NULL;
+}
+
+static void neighbor_detach(Neighbor *neighbor) {
+        neighbor_unref(neighbor_detach_impl(neighbor));
+}
+
+static Neighbor* neighbor_free(Neighbor *neighbor) {
+        if (!neighbor)
+                return NULL;
+
+        neighbor_detach_impl(neighbor);
+
+        config_section_free(neighbor->section);
         return mfree(neighbor);
 }
 
-DEFINE_SECTION_CLEANUP_FUNCTIONS(Neighbor, neighbor_free);
+DEFINE_TRIVIAL_REF_UNREF_FUNC(Neighbor, neighbor, neighbor_free);
+DEFINE_SECTION_CLEANUP_FUNCTIONS(Neighbor, neighbor_unref);
+
+static void neighbor_hash_func(const Neighbor *neighbor, struct siphash *state);
+static int neighbor_compare_func(const Neighbor *a, const Neighbor *b);
+
+DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(
+        neighbor_hash_ops_detach,
+        Neighbor,
+        neighbor_hash_func,
+        neighbor_compare_func,
+        neighbor_detach);
+
+DEFINE_PRIVATE_HASH_OPS(
+        neighbor_hash_ops,
+        Neighbor,
+        neighbor_hash_func,
+        neighbor_compare_func);
+
+DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(
+        neighbor_section_hash_ops,
+        ConfigSection,
+        config_section_hash_func,
+        config_section_compare_func,
+        Neighbor,
+        neighbor_detach);
+
+static int neighbor_new(Neighbor **ret) {
+        Neighbor *neighbor;
+
+        assert(ret);
+
+        neighbor = new(Neighbor, 1);
+        if (!neighbor)
+                return -ENOMEM;
+
+        *neighbor = (Neighbor) {
+                .n_ref = 1,
+        };
+
+        *ret = TAKE_PTR(neighbor);
+        return 0;
+}
 
 static int neighbor_new_static(Network *network, const char *filename, unsigned section_line, Neighbor **ret) {
         _cleanup_(config_section_freep) ConfigSection *n = NULL;
-        _cleanup_(neighbor_freep) Neighbor *neighbor = NULL;
+        _cleanup_(neighbor_unrefp) Neighbor *neighbor = NULL;
         int r;
 
         assert(network);
@@ -49,18 +108,15 @@ static int neighbor_new_static(Network *network, const char *filename, unsigned
                 return 0;
         }
 
-        neighbor = new(Neighbor, 1);
-        if (!neighbor)
-                return -ENOMEM;
+        r = neighbor_new(&neighbor);
+        if (r < 0)
+                return r;
 
-        *neighbor = (Neighbor) {
-                .network = network,
-                .family = AF_UNSPEC,
-                .section = TAKE_PTR(n),
-                .source = NETWORK_CONFIG_SOURCE_STATIC,
-        };
+        neighbor->network = network;
+        neighbor->section = TAKE_PTR(n);
+        neighbor->source = NETWORK_CONFIG_SOURCE_STATIC;
 
-        r = ordered_hashmap_ensure_put(&network->neighbors_by_section, &config_section_hash_ops, neighbor->section, neighbor);
+        r = ordered_hashmap_ensure_put(&network->neighbors_by_section, &neighbor_section_hash_ops, neighbor->section, neighbor);
         if (r < 0)
                 return r;
 
@@ -69,7 +125,7 @@ static int neighbor_new_static(Network *network, const char *filename, unsigned
 }
 
 static int neighbor_dup(const Neighbor *neighbor, Neighbor **ret) {
-        _cleanup_(neighbor_freep) Neighbor *dest = NULL;
+        _cleanup_(neighbor_unrefp) Neighbor *dest = NULL;
 
         assert(neighbor);
         assert(ret);
@@ -78,7 +134,8 @@ static int neighbor_dup(const Neighbor *neighbor, Neighbor **ret) {
         if (!dest)
                 return -ENOMEM;
 
-        /* Unset all pointers */
+        /* Clear the reference counter and all pointers */
+        dest->n_ref = 1;
         dest->link = NULL;
         dest->network = NULL;
         dest->section = NULL;
@@ -115,19 +172,6 @@ static int neighbor_compare_func(const Neighbor *a, const Neighbor *b) {
         return memcmp(&a->in_addr, &b->in_addr, FAMILY_ADDRESS_SIZE(a->family));
 }
 
-DEFINE_PRIVATE_HASH_OPS(
-        neighbor_hash_ops,
-        Neighbor,
-        neighbor_hash_func,
-        neighbor_compare_func);
-
-DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(
-        neighbor_hash_ops_free,
-        Neighbor,
-        neighbor_hash_func,
-        neighbor_compare_func,
-        neighbor_free);
-
 static int neighbor_get_request(Link *link, const Neighbor *neighbor, Request **ret) {
         Request *req;
 
@@ -152,7 +196,7 @@ static int neighbor_get_request(Link *link, const Neighbor *neighbor, Request **
         return 0;
 }
 
-static int neighbor_get(Link *link, const Neighbor *in, Neighbor **ret) {
+int neighbor_get(Link *link, const Neighbor *in, Neighbor **ret) {
         Neighbor *existing;
 
         assert(link);
@@ -167,19 +211,21 @@ static int neighbor_get(Link *link, const Neighbor *in, Neighbor **ret) {
         return 0;
 }
 
-static int neighbor_add(Link *link, Neighbor *neighbor) {
+static int neighbor_attach(Link *link, Neighbor *neighbor) {
         int r;
 
         assert(link);
         assert(neighbor);
+        assert(!neighbor->link);
 
-        r = set_ensure_put(&link->neighbors, &neighbor_hash_ops_free, neighbor);
+        r = set_ensure_put(&link->neighbors, &neighbor_hash_ops_detach, neighbor);
         if (r < 0)
                 return r;
         if (r == 0)
                 return -EEXIST;
 
         neighbor->link = link;
+        neighbor_ref(neighbor);
         return 0;
 }
 
@@ -279,7 +325,7 @@ static int static_neighbor_configure_handler(sd_netlink *rtnl, sd_netlink_messag
 }
 
 static int link_request_neighbor(Link *link, const Neighbor *neighbor) {
-        _cleanup_(neighbor_freep) Neighbor *tmp = NULL;
+        _cleanup_(neighbor_unrefp) Neighbor *tmp = NULL;
         Neighbor *existing = NULL;
         int r;
 
@@ -308,7 +354,7 @@ static int link_request_neighbor(Link *link, const Neighbor *neighbor) {
         log_neighbor_debug(tmp, "Requesting", link);
         r = link_queue_request_safe(link, REQUEST_TYPE_NEIGHBOR,
                                     tmp,
-                                    neighbor_free,
+                                    neighbor_unref,
                                     neighbor_hash_func,
                                     neighbor_compare_func,
                                     neighbor_process_request,
@@ -353,35 +399,48 @@ int link_request_static_neighbors(Link *link) {
         return 0;
 }
 
-static int neighbor_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
+static int neighbor_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, RemoveRequest *rreq) {
         int r;
 
         assert(m);
-        assert(link);
+        assert(rreq);
 
-        if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
-                return 1;
+        Link *link = ASSERT_PTR(rreq->link);
+        Neighbor *neighbor = ASSERT_PTR(rreq->userdata);
+
+        if (link->state == LINK_STATE_LINGER)
+                return 0;
 
         r = sd_netlink_message_get_errno(m);
-        if (r < 0 && r != -ESRCH)
+        if (r < 0) {
                 /* Neighbor may not exist because it already got deleted, ignore that. */
-                log_link_message_warning_errno(link, m, r, "Could not remove neighbor");
+                log_link_message_full_errno(link, m,
+                                            (r == -ESRCH || !neighbor->link) ? LOG_DEBUG : LOG_WARNING,
+                                            r, "Could not remove neighbor");
+
+                if (neighbor->link) {
+                        /* If the neighbor cannot be removed, then assume the neighbor is already removed. */
+                        log_neighbor_debug(neighbor, "Forgetting", link);
+
+                        Request *req;
+                        if (neighbor_get_request(link, neighbor, &req) >= 0)
+                                neighbor_enter_removed(req->userdata);
+
+                        neighbor_detach(neighbor);
+                }
+        }
 
         return 1;
 }
 
-static int neighbor_remove(Neighbor *neighbor) {
+int neighbor_remove(Neighbor *neighbor, Link *link) {
         _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
-        Request *req;
-        Link *link;
         int r;
 
         assert(neighbor);
-        assert(neighbor->link);
-        assert(neighbor->link->manager);
-        assert(neighbor->link->manager->rtnl);
-
-        link = neighbor->link;
+        assert(link);
+        assert(link->manager);
+        assert(link->manager->rtnl);
 
         log_neighbor_debug(neighbor, "Removing", link);
 
@@ -394,17 +453,11 @@ static int neighbor_remove(Neighbor *neighbor) {
         if (r < 0)
                 return log_link_error_errno(link, r, "Could not append NDA_DST attribute: %m");
 
-        r = netlink_call_async(link->manager->rtnl, NULL, m, neighbor_remove_handler,
-                               link_netlink_destroy_callback, link);
+        r = link_remove_request_add(link, neighbor, neighbor, link->manager->rtnl, m, neighbor_remove_handler);
         if (r < 0)
-                return log_link_error_errno(link, r, "Could not send rtnetlink message: %m");
-
-        link_ref(link);
+                return log_link_error_errno(link, r, "Could not queue rtnetlink message: %m");
 
         neighbor_enter_removing(neighbor);
-        if (neighbor_get_request(neighbor->link, neighbor, &req) >= 0)
-                neighbor_enter_removing(req->userdata);
-
         return 0;
 }
 
@@ -440,7 +493,7 @@ int link_drop_foreign_neighbors(Link *link) {
                 if (!neighbor_is_marked(neighbor))
                         continue;
 
-                RET_GATHER(r, neighbor_remove(neighbor));
+                RET_GATHER(r, neighbor_remove(neighbor, link));
         }
 
         return r;
@@ -461,7 +514,7 @@ int link_drop_managed_neighbors(Link *link) {
                 if (!neighbor_exists(neighbor))
                         continue;
 
-                RET_GATHER(r, neighbor_remove(neighbor));
+                RET_GATHER(r, neighbor_remove(neighbor, link));
         }
 
         return r;
@@ -477,7 +530,7 @@ void link_foreignize_neighbors(Link *link) {
 }
 
 int manager_rtnl_process_neighbor(sd_netlink *rtnl, sd_netlink_message *message, Manager *m) {
-        _cleanup_(neighbor_freep) Neighbor *tmp = NULL;
+        _cleanup_(neighbor_unrefp) Neighbor *tmp = NULL;
         Neighbor *neighbor = NULL;
         Request *req = NULL;
         uint16_t type, state;
@@ -529,8 +582,8 @@ int manager_rtnl_process_neighbor(sd_netlink *rtnl, sd_netlink_message *message,
                  * kernel sends messages about neighbors after a link is removed. So, just ignore it. */
                 return 0;
 
-        tmp = new0(Neighbor, 1);
-        if (!tmp)
+        r = neighbor_new(&tmp);
+        if (r < 0)
                 return log_oom();
 
         /* First, retrieve the fundamental information about the neighbor. */
@@ -560,7 +613,7 @@ int manager_rtnl_process_neighbor(sd_netlink *rtnl, sd_netlink_message *message,
                 if (neighbor) {
                         neighbor_enter_removed(neighbor);
                         log_neighbor_debug(neighbor, "Forgetting removed", link);
-                        neighbor_free(neighbor);
+                        neighbor_detach(neighbor);
                 } else
                         log_neighbor_debug(tmp, "Kernel removed unknown", link);
 
@@ -572,12 +625,12 @@ int manager_rtnl_process_neighbor(sd_netlink *rtnl, sd_netlink_message *message,
 
         /* If we did not know the neighbor, then save it. */
         if (!neighbor) {
-                r = neighbor_add(link, tmp);
+                r = neighbor_attach(link, tmp);
                 if (r < 0) {
                         log_link_warning_errno(link, r, "Failed to save received neighbor, ignoring: %m");
                         return 0;
                 }
-                neighbor = TAKE_PTR(tmp);
+                neighbor = tmp;
                 is_new = true;
         }
 
@@ -638,9 +691,9 @@ int network_drop_invalid_neighbors(Network *network) {
                 Neighbor *dup;
 
                 if (neighbor_section_verify(neighbor) < 0) {
-                        /* Drop invalid [Neighbor] sections. Note that neighbor_free() will drop the
+                        /* Drop invalid [Neighbor] sections. Note that neighbor_detach() will drop the
                          * neighbor from neighbors_by_section. */
-                        neighbor_free(neighbor);
+                        neighbor_detach(neighbor);
                         continue;
                 }
 
@@ -653,12 +706,12 @@ int network_drop_invalid_neighbors(Network *network) {
                                     IN_ADDR_TO_STRING(neighbor->family, &neighbor->in_addr),
                                     neighbor->section->line,
                                     dup->section->line, dup->section->line);
-                        /* neighbor_free() will drop the neighbor from neighbors_by_section. */
-                        neighbor_free(dup);
+                        /* neighbor_detach() will drop the neighbor from neighbors_by_section. */
+                        neighbor_detach(dup);
                 }
 
-                /* Use neighbor_hash_ops, instead of neighbor_hash_ops_free. Otherwise, the Neighbor objects
-                 * will be freed. */
+                /* Use neighbor_hash_ops, instead of neighbor_hash_ops_detach. Otherwise, the Neighbor objects
+                 * will be detached. */
                 r = set_ensure_put(&neighbors, &neighbor_hash_ops, neighbor);
                 if (r < 0)
                         return log_oom();
@@ -681,7 +734,7 @@ int config_parse_neighbor_address(
                 void *data,
                 void *userdata) {
 
-        _cleanup_(neighbor_free_or_set_invalidp) Neighbor *n = NULL;
+        _cleanup_(neighbor_unref_or_set_invalidp) Neighbor *n = NULL;
         Network *network = ASSERT_PTR(userdata);
         int r;
 
@@ -724,7 +777,7 @@ int config_parse_neighbor_lladdr(
                 void *data,
                 void *userdata) {
 
-        _cleanup_(neighbor_free_or_set_invalidp) Neighbor *n = NULL;
+        _cleanup_(neighbor_unref_or_set_invalidp) Neighbor *n = NULL;
         Network *network = ASSERT_PTR(userdata);
         int r;
 
index 683a310b3f34e09d6cdb32a54d6f2a6c5ff0e9c9..93b3927f02ca0c3df119dafd9ed4a4ff6591928f 100644 (file)
@@ -21,12 +21,18 @@ typedef struct Neighbor {
         NetworkConfigSource source;
         NetworkConfigState state;
 
+        unsigned n_ref;
+
         int family;
         union in_addr_union in_addr;
         struct hw_addr_data ll_addr;
 } Neighbor;
 
-Neighbor *neighbor_free(Neighbor *neighbor);
+Neighbor* neighbor_ref(Neighbor *neighbor);
+Neighbor* neighbor_unref(Neighbor *neighbor);
+
+int neighbor_get(Link *link, const Neighbor *in, Neighbor **ret);
+int neighbor_remove(Neighbor *neighbor, Link *link);
 
 int network_drop_invalid_neighbors(Network *network);
 
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..08c7da56997422a82a36ed9cc9f7b9f0b3880f21 100644 (file)
@@ -187,7 +187,7 @@ int network_verify(Network *network) {
                         log_warning("%s: Cannot set routes when Bond= is specified, ignoring routes.",
                                     network->filename);
 
-                network->addresses_by_section = ordered_hashmap_free_with_destructor(network->addresses_by_section, address_free);
+                network->addresses_by_section = ordered_hashmap_free(network->addresses_by_section);
                 network->routes_by_section = hashmap_free_with_destructor(network->routes_by_section, route_free);
         }
 
@@ -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 ||
@@ -374,7 +372,7 @@ int network_load_one(Manager *manager, OrderedHashmap **networks, const char *fi
                 .n_ref = 1,
 
                 .required_for_online = -1,
-                .required_operstate_for_online = LINK_OPERSTATE_RANGE_DEFAULT,
+                .required_operstate_for_online = LINK_OPERSTATE_RANGE_INVALID,
                 .activation_policy = _ACTIVATION_POLICY_INVALID,
                 .group = -1,
                 .arp = -1,
@@ -782,12 +780,12 @@ static Network *network_free(Network *network) {
 
         /* static configs */
         set_free_free(network->ipv6_proxy_ndp_addresses);
-        ordered_hashmap_free_with_destructor(network->addresses_by_section, address_free);
+        ordered_hashmap_free(network->addresses_by_section);
         hashmap_free_with_destructor(network->routes_by_section, route_free);
-        ordered_hashmap_free_with_destructor(network->nexthops_by_section, nexthop_free);
+        ordered_hashmap_free(network->nexthops_by_section);
         hashmap_free_with_destructor(network->bridge_fdb_entries_by_section, bridge_fdb_free);
         hashmap_free_with_destructor(network->bridge_mdb_entries_by_section, bridge_mdb_free);
-        ordered_hashmap_free_with_destructor(network->neighbors_by_section, neighbor_free);
+        ordered_hashmap_free(network->neighbors_by_section);
         hashmap_free_with_destructor(network->address_labels_by_section, address_label_free);
         hashmap_free_with_destructor(network->prefixes_by_section, prefix_free);
         hashmap_free_with_destructor(network->route_prefixes_by_section, route_prefix_free);
@@ -1215,8 +1213,6 @@ int config_parse_required_for_online(
                 void *userdata) {
 
         Network *network = ASSERT_PTR(userdata);
-        LinkOperationalStateRange range;
-        bool required = true;
         int r;
 
         assert(filename);
@@ -1225,11 +1221,11 @@ int config_parse_required_for_online(
 
         if (isempty(rvalue)) {
                 network->required_for_online = -1;
-                network->required_operstate_for_online = LINK_OPERSTATE_RANGE_DEFAULT;
+                network->required_operstate_for_online = LINK_OPERSTATE_RANGE_INVALID;
                 return 0;
         }
 
-        r = parse_operational_state_range(rvalue, &range);
+        r = parse_operational_state_range(rvalue, &network->required_operstate_for_online);
         if (r < 0) {
                 r = parse_boolean(rvalue);
                 if (r < 0) {
@@ -1239,13 +1235,12 @@ int config_parse_required_for_online(
                         return 0;
                 }
 
-                required = r;
-                range = LINK_OPERSTATE_RANGE_DEFAULT;
+                network->required_for_online = r;
+                network->required_operstate_for_online = LINK_OPERSTATE_RANGE_DEFAULT;
+                return 0;
         }
 
-        network->required_for_online = required;
-        network->required_operstate_for_online = range;
-
+        network->required_for_online = true;
         return 0;
 }
 
index 1ab56a8ffdb79e8f43db748a52f99eefb1428818..cf62e0e82dc666450a42faf5bf2d87ec4c1bc980 100644 (file)
 #include "stdio-util.h"
 #include "string-util.h"
 
-NextHop *nexthop_free(NextHop *nexthop) {
-        if (!nexthop)
-                return NULL;
+static void nexthop_detach_from_group_members(NextHop *nexthop) {
+        assert(nexthop);
+        assert(nexthop->manager);
+        assert(nexthop->id > 0);
+
+        struct nexthop_grp *nhg;
+        HASHMAP_FOREACH(nhg, nexthop->group) {
+                NextHop *nh;
+
+                if (nexthop_get_by_id(nexthop->manager, nhg->id, &nh) < 0)
+                        continue;
+
+                set_remove(nh->nexthops, UINT32_TO_PTR(nexthop->id));
+        }
+}
+
+static void nexthop_attach_to_group_members(NextHop *nexthop) {
+        int r;
+
+        assert(nexthop);
+        assert(nexthop->manager);
+        assert(nexthop->id > 0);
+
+        struct nexthop_grp *nhg;
+        HASHMAP_FOREACH(nhg, nexthop->group) {
+                NextHop *nh;
+
+                r = nexthop_get_by_id(nexthop->manager, nhg->id, &nh);
+                if (r < 0) {
+                        if (nexthop->manager->manage_foreign_nexthops)
+                                log_debug_errno(r, "Nexthop (id=%"PRIu32") has unknown group member (%"PRIu32"), ignoring.",
+                                                nexthop->id, nhg->id);
+                        continue;
+                }
+
+                r = set_ensure_put(&nh->nexthops, NULL, UINT32_TO_PTR(nexthop->id));
+                if (r < 0)
+                        log_debug_errno(r, "Failed to save nexthop ID (%"PRIu32") to group member (%"PRIu32"), ignoring: %m",
+                                        nexthop->id, nhg->id);
+        }
+}
+
+static NextHop* nexthop_detach_impl(NextHop *nexthop) {
+        assert(nexthop);
+        assert(!nexthop->manager || !nexthop->network);
 
         if (nexthop->network) {
                 assert(nexthop->section);
                 ordered_hashmap_remove(nexthop->network->nexthops_by_section, nexthop->section);
+                nexthop->network = NULL;
+                return nexthop;
         }
 
-        config_section_free(nexthop->section);
-
         if (nexthop->manager) {
                 assert(nexthop->id > 0);
+
+                nexthop_detach_from_group_members(nexthop);
+
                 hashmap_remove(nexthop->manager->nexthops_by_id, UINT32_TO_PTR(nexthop->id));
+                nexthop->manager = NULL;
+                return nexthop;
         }
 
+        return NULL;
+}
+
+static void nexthop_detach(NextHop *nexthop) {
+        nexthop_unref(nexthop_detach_impl(nexthop));
+}
+
+static NextHop* nexthop_free(NextHop *nexthop) {
+        if (!nexthop)
+                return NULL;
+
+        nexthop_detach_impl(nexthop);
+
+        config_section_free(nexthop->section);
         hashmap_free_free(nexthop->group);
+        set_free(nexthop->nexthops);
 
         return mfree(nexthop);
 }
 
-DEFINE_SECTION_CLEANUP_FUNCTIONS(NextHop, nexthop_free);
+DEFINE_TRIVIAL_REF_UNREF_FUNC(NextHop, nexthop, nexthop_free);
+DEFINE_SECTION_CLEANUP_FUNCTIONS(NextHop, nexthop_unref);
 
 DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(
                 nexthop_hash_ops,
@@ -47,17 +110,25 @@ DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(
                 trivial_hash_func,
                 trivial_compare_func,
                 NextHop,
-                nexthop_free);
+                nexthop_detach);
+
+DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(
+                nexthop_section_hash_ops,
+                ConfigSection,
+                config_section_hash_func,
+                config_section_compare_func,
+                NextHop,
+                nexthop_detach);
 
 static int nexthop_new(NextHop **ret) {
-        _cleanup_(nexthop_freep) NextHop *nexthop = NULL;
+        _cleanup_(nexthop_unrefp) NextHop *nexthop = NULL;
 
         nexthop = new(NextHop, 1);
         if (!nexthop)
                 return -ENOMEM;
 
         *nexthop = (NextHop) {
-                .family = AF_UNSPEC,
+                .n_ref = 1,
                 .onlink = -1,
         };
 
@@ -68,7 +139,7 @@ static int nexthop_new(NextHop **ret) {
 
 static int nexthop_new_static(Network *network, const char *filename, unsigned section_line, NextHop **ret) {
         _cleanup_(config_section_freep) ConfigSection *n = NULL;
-        _cleanup_(nexthop_freep) NextHop *nexthop = NULL;
+        _cleanup_(nexthop_unrefp) NextHop *nexthop = NULL;
         int r;
 
         assert(network);
@@ -95,7 +166,7 @@ static int nexthop_new_static(Network *network, const char *filename, unsigned s
         nexthop->section = TAKE_PTR(n);
         nexthop->source = NETWORK_CONFIG_SOURCE_STATIC;
 
-        r = ordered_hashmap_ensure_put(&network->nexthops_by_section, &config_section_hash_ops, nexthop->section, nexthop);
+        r = ordered_hashmap_ensure_put(&network->nexthops_by_section, &nexthop_section_hash_ops, nexthop->section, nexthop);
         if (r < 0)
                 return r;
 
@@ -171,7 +242,7 @@ static int nexthop_compare_full(const NextHop *a, const NextHop *b) {
 }
 
 static int nexthop_dup(const NextHop *src, NextHop **ret) {
-        _cleanup_(nexthop_freep) NextHop *dest = NULL;
+        _cleanup_(nexthop_unrefp) NextHop *dest = NULL;
         struct nexthop_grp *nhg;
         int r;
 
@@ -182,7 +253,8 @@ static int nexthop_dup(const NextHop *src, NextHop **ret) {
         if (!dest)
                 return -ENOMEM;
 
-        /* unset all pointers */
+        /* clear the reference counter and all pointers */
+        dest->n_ref = 1;
         dest->manager = NULL;
         dest->network = NULL;
         dest->section = NULL;
@@ -269,6 +341,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) {
@@ -326,7 +401,7 @@ static int nexthop_get_request(Link *link, const NextHop *in, Request **ret) {
 }
 
 static int nexthop_add_new(Manager *manager, uint32_t id, NextHop **ret) {
-        _cleanup_(nexthop_freep) NextHop *nexthop = NULL;
+        _cleanup_(nexthop_unrefp) NextHop *nexthop = NULL;
         int r;
 
         assert(manager);
@@ -407,34 +482,69 @@ static void log_nexthop_debug(const NextHop *nexthop, const char *str, Manager *
                        yes_no(nexthop->blackhole), strna(group), strna(flags));
 }
 
-static int nexthop_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
+static int nexthop_remove_dependents(NextHop *nexthop, Manager *manager) {
+        int r = 0;
+
+        assert(nexthop);
+        assert(manager);
+
+        /* If a nexthop is removed, the kernel silently removes nexthops that depend on the
+         * removed nexthop. Let's remove them for safety (though, they are already removed in the kernel,
+         * hence that should fail), and forget them. */
+
+        void *id;
+        SET_FOREACH(id, nexthop->nexthops) {
+                NextHop *nh;
+
+                if (nexthop_get_by_id(manager, PTR_TO_UINT32(id), &nh) < 0)
+                        continue;
+
+                RET_GATHER(r, nexthop_remove(nh, manager));
+        }
+
+        return r;
+}
+
+static int nexthop_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, RemoveRequest *rreq) {
         int r;
 
         assert(m);
+        assert(rreq);
 
-        /* link may be NULL. */
-
-        if (link && IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
-                return 1;
+        Manager *manager = ASSERT_PTR(rreq->manager);
+        NextHop *nexthop = ASSERT_PTR(rreq->userdata);
 
         r = sd_netlink_message_get_errno(m);
-        if (r < 0 && r != -ENOENT)
-                log_link_message_warning_errno(link, m, r, "Could not drop nexthop, ignoring");
+        if (r < 0) {
+                log_message_full_errno(m,
+                                       (r == -ENOENT || !nexthop->manager) ? LOG_DEBUG : LOG_WARNING,
+                                       r, "Could not drop nexthop, ignoring");
+
+                (void) nexthop_remove_dependents(nexthop, manager);
+
+                if (nexthop->manager) {
+                        /* If the nexthop cannot be removed, then assume the nexthop is already removed. */
+                        log_nexthop_debug(nexthop, "Forgetting", manager);
+
+                        Request *req;
+                        if (nexthop_get_request_by_id(manager, nexthop->id, &req) >= 0)
+                                nexthop_enter_removed(req->userdata);
+
+                        nexthop_detach(nexthop);
+                }
+        }
 
         return 1;
 }
 
-static int nexthop_remove(NextHop *nexthop) {
+int nexthop_remove(NextHop *nexthop, Manager *manager) {
         _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
-        Manager *manager;
         Link *link = NULL;
-        Request *req;
         int r;
 
         assert(nexthop);
         assert(nexthop->id > 0);
-
-        manager = ASSERT_PTR(nexthop->manager);
+        assert(manager);
 
         /* link may be NULL. */
         (void) link_get_by_index(manager, nexthop->ifindex, &link);
@@ -449,17 +559,11 @@ static int nexthop_remove(NextHop *nexthop) {
         if (r < 0)
                 return log_link_error_errno(link, r, "Could not append NHA_ID attribute: %m");
 
-        r = netlink_call_async(manager->rtnl, NULL, m, nexthop_remove_handler,
-                               link ? link_netlink_destroy_callback : NULL, link);
+        r = manager_remove_request_add(manager, nexthop, nexthop, manager->rtnl, m, nexthop_remove_handler);
         if (r < 0)
-                return log_link_error_errno(link, r, "Could not send rtnetlink message: %m");
-
-        link_ref(link); /* link may be NULL, link_ref() is OK with that */
+                return log_link_error_errno(link, r, "Could not queue rtnetlink message: %m");
 
         nexthop_enter_removing(nexthop);
-        if (nexthop_get_request_by_id(manager, nexthop->id, &req) >= 0)
-                nexthop_enter_removing(req->userdata);
-
         return 0;
 }
 
@@ -549,8 +653,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 +707,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);
@@ -609,7 +739,7 @@ static int nexthop_process_request(Request *req, Link *link, NextHop *nexthop) {
 }
 
 static int link_request_nexthop(Link *link, const NextHop *nexthop) {
-        _cleanup_(nexthop_freep) NextHop *tmp = NULL;
+        _cleanup_(nexthop_unrefp) NextHop *tmp = NULL;
         NextHop *existing = NULL;
         int r;
 
@@ -644,7 +774,7 @@ static int link_request_nexthop(Link *link, const NextHop *nexthop) {
         log_nexthop_debug(tmp, "Requesting", link->manager);
         r = link_queue_request_safe(link, REQUEST_TYPE_NEXTHOP,
                                     tmp,
-                                    nexthop_free,
+                                    nexthop_unref,
                                     nexthop_hash_func,
                                     nexthop_compare_func,
                                     nexthop_process_request,
@@ -780,7 +910,7 @@ int link_drop_nexthops(Link *link, bool foreign) {
                 if (!nexthop_is_marked(nexthop))
                         continue;
 
-                RET_GATHER(r, nexthop_remove(nexthop));
+                RET_GATHER(r, nexthop_remove(nexthop, link->manager));
         }
 
         return r;
@@ -802,15 +932,22 @@ void link_foreignize_nexthops(Link *link) {
         }
 }
 
-static int nexthop_update_group(NextHop *nexthop, const struct nexthop_grp *group, size_t size) {
+static int nexthop_update_group(NextHop *nexthop, sd_netlink_message *message) {
         _cleanup_hashmap_free_free_ Hashmap *h = NULL;
-        size_t n_group;
+        _cleanup_free_ struct nexthop_grp *group = NULL;
+        size_t size = 0, n_group;
         int r;
 
         assert(nexthop);
-        assert(group || size == 0);
+        assert(message);
+
+        r = sd_netlink_message_read_data(message, NHA_GROUP, &size, (void**) &group);
+        if (r < 0 && r != -ENODATA)
+                return log_debug_errno(r, "rtnl: could not get NHA_GROUP attribute, ignoring: %m");
 
-        if (size == 0 || size % sizeof(struct nexthop_grp) != 0)
+        nexthop_detach_from_group_members(nexthop);
+
+        if (size % sizeof(struct nexthop_grp) != 0)
                 return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
                                        "rtnl: received nexthop message with invalid nexthop group size, ignoring.");
 
@@ -849,12 +986,12 @@ static int nexthop_update_group(NextHop *nexthop, const struct nexthop_grp *grou
 
         hashmap_free_free(nexthop->group);
         nexthop->group = TAKE_PTR(h);
+
+        nexthop_attach_to_group_members(nexthop);
         return 0;
 }
 
 int manager_rtnl_process_nexthop(sd_netlink *rtnl, sd_netlink_message *message, Manager *m) {
-        _cleanup_free_ void *raw_group = NULL;
-        size_t raw_group_size;
         uint16_t type;
         uint32_t id, ifindex;
         NextHop *nexthop = NULL;
@@ -902,7 +1039,8 @@ int manager_rtnl_process_nexthop(sd_netlink *rtnl, sd_netlink_message *message,
                 if (nexthop) {
                         nexthop_enter_removed(nexthop);
                         log_nexthop_debug(nexthop, "Forgetting removed", m);
-                        nexthop_free(nexthop);
+                        (void) nexthop_remove_dependents(nexthop, m);
+                        nexthop_detach(nexthop);
                 } else
                         log_nexthop_debug(&(const NextHop) { .id = id }, "Kernel removed unknown", m);
 
@@ -942,13 +1080,7 @@ int manager_rtnl_process_nexthop(sd_netlink *rtnl, sd_netlink_message *message,
         if (r < 0)
                 log_debug_errno(r, "rtnl: could not get nexthop flags, ignoring: %m");
 
-        r = sd_netlink_message_read_data(message, NHA_GROUP, &raw_group_size, &raw_group);
-        if (r == -ENODATA)
-                nexthop->group = hashmap_free_free(nexthop->group);
-        else if (r < 0)
-                log_debug_errno(r, "rtnl: could not get NHA_GROUP attribute, ignoring: %m");
-        else
-                (void) nexthop_update_group(nexthop, raw_group, raw_group_size);
+        (void) nexthop_update_group(nexthop, message);
 
         if (nexthop->family != AF_UNSPEC) {
                 r = netlink_message_read_in_addr_union(message, NHA_GATEWAY, nexthop->family, &nexthop->gw);
@@ -1065,7 +1197,7 @@ int network_drop_invalid_nexthops(Network *network) {
 
         ORDERED_HASHMAP_FOREACH(nh, network->nexthops_by_section) {
                 if (nexthop_section_verify(nh) < 0) {
-                        nexthop_free(nh);
+                        nexthop_detach(nh);
                         continue;
                 }
 
@@ -1080,8 +1212,8 @@ int network_drop_invalid_nexthops(Network *network) {
                                     dup->section->filename,
                                     nh->id, nh->section->line,
                                     dup->section->line, dup->section->line);
-                        /* nexthop_free() will drop the nexthop from nexthops_by_section. */
-                        nexthop_free(dup);
+                        /* nexthop_detach() will drop the nexthop from nexthops_by_section. */
+                        nexthop_detach(dup);
                 }
 
                 r = hashmap_ensure_put(&nexthops, NULL, UINT32_TO_PTR(nh->id), nh);
@@ -1132,7 +1264,7 @@ int config_parse_nexthop_id(
                 void *data,
                 void *userdata) {
 
-        _cleanup_(nexthop_free_or_set_invalidp) NextHop *n = NULL;
+        _cleanup_(nexthop_unref_or_set_invalidp) NextHop *n = NULL;
         Network *network = userdata;
         uint32_t id;
         int r;
@@ -1182,7 +1314,7 @@ int config_parse_nexthop_gateway(
                 void *data,
                 void *userdata) {
 
-        _cleanup_(nexthop_free_or_set_invalidp) NextHop *n = NULL;
+        _cleanup_(nexthop_unref_or_set_invalidp) NextHop *n = NULL;
         Network *network = userdata;
         int r;
 
@@ -1227,7 +1359,7 @@ int config_parse_nexthop_family(
                 void *data,
                 void *userdata) {
 
-        _cleanup_(nexthop_free_or_set_invalidp) NextHop *n = NULL;
+        _cleanup_(nexthop_unref_or_set_invalidp) NextHop *n = NULL;
         Network *network = userdata;
         AddressFamily a;
         int r;
@@ -1293,7 +1425,7 @@ int config_parse_nexthop_onlink(
                 void *data,
                 void *userdata) {
 
-        _cleanup_(nexthop_free_or_set_invalidp) NextHop *n = NULL;
+        _cleanup_(nexthop_unref_or_set_invalidp) NextHop *n = NULL;
         Network *network = userdata;
         int r;
 
@@ -1330,7 +1462,7 @@ int config_parse_nexthop_blackhole(
                 void *data,
                 void *userdata) {
 
-        _cleanup_(nexthop_free_or_set_invalidp) NextHop *n = NULL;
+        _cleanup_(nexthop_unref_or_set_invalidp) NextHop *n = NULL;
         Network *network = userdata;
         int r;
 
@@ -1369,7 +1501,7 @@ int config_parse_nexthop_group(
                 void *data,
                 void *userdata) {
 
-        _cleanup_(nexthop_free_or_set_invalidp) NextHop *n = NULL;
+        _cleanup_(nexthop_unref_or_set_invalidp) NextHop *n = NULL;
         Network *network = userdata;
         int r;
 
index 564b52532fa72fb7aff3a75b14f420f256bc5e20..74b23bd77203f0349e0f9ea98248256c1ba476ee 100644 (file)
@@ -24,18 +24,31 @@ typedef struct NextHop {
         NetworkConfigSource source;
         NetworkConfigState state;
 
-        uint8_t protocol;
-        int ifindex;
-        uint32_t id;
-        bool blackhole;
+        unsigned n_ref;
+
+        /* struct nhmsg */
         int family;
-        union in_addr_union gw;
+        uint8_t protocol;
         uint8_t flags;
-        int onlink; /* Only used in conf parser and nexthop_section_verify(). */
-        Hashmap *group;
+
+        /* attributes */
+        uint32_t id; /* NHA_ID */
+        Hashmap *group; /* NHA_GROUP */
+        bool blackhole; /* NHA_BLACKHOLE */
+        int ifindex; /* NHA_OIF */
+        union in_addr_union gw; /* NHA_GATEWAY */
+
+        /* Only used in conf parser and nexthop_section_verify(). */
+        int onlink;
+
+        /* For managing nexthops that depend on this nexthop. */
+        Set *nexthops;
 } NextHop;
 
-NextHop *nexthop_free(NextHop *nexthop);
+NextHop* nexthop_ref(NextHop *nexthop);
+NextHop* nexthop_unref(NextHop *nexthop);
+
+int nexthop_remove(NextHop *nexthop, Manager *manager);
 
 int network_drop_invalid_nexthops(Network *network);
 
@@ -51,6 +64,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..383f259fa7992971b86f8e67dc05966cccb459f2 100644 (file)
@@ -9,14 +9,28 @@
 
 #define REPLY_CALLBACK_COUNT_THRESHOLD 128
 
+static Request* request_detach_impl(Request *req) {
+        assert(req);
+
+        if (!req->manager)
+                return NULL;
+
+        ordered_set_remove(req->manager->request_queue, req);
+        req->manager = NULL;
+        return req;
+}
+
+void request_detach(Request *req) {
+        request_unref(request_detach_impl(req));
+}
+
 static Request *request_free(Request *req) {
         if (!req)
                 return NULL;
 
         /* To prevent from triggering assertions in the hash and compare functions, remove this request
          * from the set before freeing userdata below. */
-        if (req->manager)
-                ordered_set_remove(req->manager->request_queue, req);
+        request_detach_impl(req);
 
         if (req->free_func)
                 req->free_func(req->userdata);
@@ -31,26 +45,10 @@ static Request *request_free(Request *req) {
 
 DEFINE_TRIVIAL_REF_UNREF_FUNC(Request, request, request_free);
 
-void request_detach(Manager *manager, Request *req) {
-        assert(manager);
-
-        if (!req)
-                return;
-
-        req = ordered_set_remove(manager->request_queue, req);
-        if (!req)
-                return;
-
-        req->manager = NULL;
-        request_unref(req);
-}
-
 static void request_destroy_callback(Request *req) {
         assert(req);
 
-        if (req->manager)
-                request_detach(req->manager, req);
-
+        request_detach(req);
         request_unref(req);
 }
 
@@ -114,7 +112,7 @@ DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(
                 Request,
                 request_hash_func,
                 request_compare_func,
-                request_unref);
+                request_detach);
 
 static int request_new(
                 Manager *manager,
@@ -168,6 +166,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,52 +217,52 @@ 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);
+        /* Process only when no remove request is queued. */
+        if (!ordered_set_isempty(manager->remove_request_queue))
+                return 0;
 
-                        assert(req->process);
+        manager->request_queued = false;
 
-                        if (req->waiting_reply)
-                                continue; /* Waiting for netlink reply. */
+        ORDERED_SET_FOREACH(req, manager->request_queue) {
+                if (req->waiting_reply)
+                        continue; /* Already processed, and waiting for netlink reply. */
 
-                        /* 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;
+                /* 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)
+                        break;
 
-                        r = req->process(req, link, req->userdata);
-                        if (r == 0)
-                                continue;
+                /* Avoid the request and link freed by req->process() and request_detach(). */
+                _unused_ _cleanup_(request_unrefp) Request *req_unref = request_ref(req);
+                _cleanup_(link_unrefp) Link *link = link_ref(req->link);
 
-                        processed = true;
+                assert(req->process);
+                r = req->process(req, link, req->userdata);
+                if (r < 0) {
+                        request_detach(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) {
+                        if (link) {
                                 link_enter_failed(link);
-                                /* link_enter_failed() may remove multiple requests,
-                                 * hence we need to exit from the loop. */
+                                /* link_enter_failed() may detach multiple requests from the queue.
+                                 * Hence, we need to exit from the loop. */
                                 break;
                         }
                 }
+                if (r > 0 && !req->waiting_reply)
+                        /* 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. */
+                        request_detach(req);
 
-                /* When at least one request is processed, then another request may be ready now. */
-                if (!processed)
-                        break;
+                if (manager->request_queued)
+                        break; /* New request is queued. Exit from the loop. */
         }
 
         return 0;
@@ -337,3 +339,110 @@ static const char *const request_type_table[_REQUEST_TYPE_MAX] = {
 };
 
 DEFINE_STRING_TABLE_LOOKUP_TO_STRING(request_type, RequestType);
+
+static RemoveRequest* remove_request_free(RemoveRequest *req) {
+        if (!req)
+                return NULL;
+
+        if (req->manager)
+                ordered_set_remove(req->manager->remove_request_queue, req);
+
+        if (req->unref_func)
+                req->unref_func(req->userdata);
+
+        link_unref(req->link);
+        sd_netlink_unref(req->netlink);
+        sd_netlink_message_unref(req->message);
+
+        return mfree(req);
+}
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(RemoveRequest*, remove_request_free);
+DEFINE_TRIVIAL_DESTRUCTOR(remove_request_destroy_callback, RemoveRequest, remove_request_free);
+DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(
+                remove_request_hash_ops,
+                void,
+                trivial_hash_func,
+                trivial_compare_func,
+                remove_request_free);
+
+int remove_request_add(
+                Manager *manager,
+                Link *link,
+                void *userdata,
+                mfree_func_t unref_func,
+                sd_netlink *netlink,
+                sd_netlink_message *message,
+                remove_request_netlink_handler_t netlink_handler) {
+
+        _cleanup_(remove_request_freep) RemoveRequest *req = NULL;
+        int r;
+
+        assert(manager);
+        assert(userdata);
+        assert(netlink);
+        assert(message);
+
+        req = new(RemoveRequest, 1);
+        if (!req)
+                return -ENOMEM;
+
+        *req = (RemoveRequest) {
+                .link = link_ref(link), /* link may be NULL, but link_ref() handles it gracefully. */
+                .userdata = userdata,
+                .netlink = sd_netlink_ref(netlink),
+                .message = sd_netlink_message_ref(message),
+                .netlink_handler = netlink_handler,
+        };
+
+        r = ordered_set_ensure_put(&manager->remove_request_queue, &remove_request_hash_ops, req);
+        if (r < 0)
+                return r;
+        assert(r > 0);
+
+        req->manager = manager;
+        req->unref_func = unref_func;
+
+        TAKE_PTR(req);
+        return 0;
+}
+
+int manager_process_remove_requests(Manager *manager) {
+        RemoveRequest *req;
+        int r;
+
+        assert(manager);
+
+        while ((req = ordered_set_first(manager->remove_request_queue))) {
+
+                /* Do not make the reply callback queue in sd-netlink full. */
+                if (netlink_get_reply_callback_count(req->netlink) >= REPLY_CALLBACK_COUNT_THRESHOLD)
+                        return 0;
+
+                r = netlink_call_async(
+                                req->netlink, NULL, req->message,
+                                req->netlink_handler,
+                                remove_request_destroy_callback,
+                                req);
+                if (r < 0) {
+                        _cleanup_(link_unrefp) Link *link = link_ref(req->link);
+
+                        log_link_warning_errno(link, r, "Failed to call netlink message: %m");
+
+                        /* First free the request. */
+                        remove_request_free(req);
+
+                        /* Then, make the link enter the failed state. */
+                        if (link)
+                                link_enter_failed(link);
+
+                } else {
+                        /* On success, netlink needs to be unref()ed. Otherwise, the netlink and remove
+                         * request may not freed on shutting down. */
+                        req->netlink = sd_netlink_unref(req->netlink);
+                        ordered_set_remove(manager->remove_request_queue, req);
+                }
+        }
+
+        return 0;
+}
index 21fa7d9453475ae1182b4f31b20254e9c4ab0a0c..bdedc775377b9c56ed1df117cc01032757fe4642 100644 (file)
@@ -88,7 +88,7 @@ Request *request_ref(Request *req);
 Request *request_unref(Request *req);
 DEFINE_TRIVIAL_CLEANUP_FUNC(Request*, request_unref);
 
-void request_detach(Manager *manager, Request *req);
+void request_detach(Request *req);
 
 int netdev_queue_request(
                 NetDev *netdev,
@@ -139,3 +139,52 @@ int manager_process_requests(Manager *manager);
 int request_call_netlink_async(sd_netlink *nl, sd_netlink_message *m, Request *req);
 
 const char* request_type_to_string(RequestType t) _const_;
+
+typedef struct RemoveRequest RemoveRequest;
+typedef int (*remove_request_netlink_handler_t)(sd_netlink *nl, sd_netlink_message *m, RemoveRequest *req);
+
+struct RemoveRequest {
+        Manager *manager;
+        Link *link;
+        void *userdata; /* e.g. Address */
+        mfree_func_t unref_func; /* e.g. address_unref() */
+        sd_netlink *netlink;
+        sd_netlink_message *message;
+        remove_request_netlink_handler_t netlink_handler;
+};
+
+int remove_request_add(
+                Manager *manager,
+                Link *link,
+                void *userdata, /* This is unref()ed when the call failed. */
+                mfree_func_t unref_func,
+                sd_netlink *netlink,
+                sd_netlink_message *message,
+                remove_request_netlink_handler_t netlink_handler);
+
+#define _remove_request_add(manager, link, data, name, nl, m, handler)  \
+        ({                                                              \
+                typeof(*data) *_data = (data);                          \
+                int _r;                                                 \
+                                                                        \
+                _r = remove_request_add(manager, link, _data,           \
+                                        (mfree_func_t) name##_unref,    \
+                                        nl, m, handler);                \
+                if (_r >= 0)                                            \
+                        name##_ref(_data);                              \
+                _r;                                                     \
+        })
+
+
+#define link_remove_request_add(link, data, name, nl, m, handler)       \
+        ({                                                              \
+                Link *_link = (link);                                   \
+                                                                        \
+                _remove_request_add(_link->manager, _link, data, name,  \
+                                    nl, m, handler);                    \
+        })
+
+#define manager_remove_request_add(manager, data, name, nl, m, handler) \
+        _remove_request_add(manager, NULL, data, name, nl, m, handler)
+
+int manager_process_remove_requests(Manager *manager);
index fc36a001f8fbf1863bfd6c5a37c11be28dc0e486..7fd97e7206ffd4ab4168321668eeb909ce8b6791 100644 (file)
@@ -258,7 +258,7 @@ int link_request_radv_addresses(Link *link) {
                         return r;
 
                 SET_FOREACH(a, addresses) {
-                        _cleanup_(address_freep) Address *address = NULL;
+                        _cleanup_(address_unrefp) Address *address = NULL;
 
                         r = address_new(&address);
                         if (r < 0)
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..f58cdb3
--- /dev/null
@@ -0,0 +1,1009 @@
+/* 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-network.h"
+#include "networkd-nexthop.h"
+#include "networkd-route.h"
+#include "networkd-route-nexthop.h"
+#include "networkd-route-util.h"
+#include "parse-util.h"
+#include "string-util.h"
+
+static void route_nexthop_done(RouteNextHop *nh) {
+        assert(nh);
+
+        free(nh->ifname);
+}
+
+RouteNextHop* route_nexthop_free(RouteNextHop *nh) {
+        if (!nh)
+                return NULL;
+
+        route_nexthop_done(nh);
+
+        return mfree(nh);
+}
+
+void route_nexthops_done(Route *route) {
+        assert(route);
+
+        route_nexthop_done(&route->nexthop);
+        ordered_set_free(route->nexthops);
+}
+
+static void route_nexthop_hash_func_full(const RouteNextHop *nh, struct siphash *state, bool hash_all_parameters) {
+        assert(nh);
+        assert(state);
+
+        /* See nh_comp() in net/ipv4/fib_semantics.c of the kernel. */
+
+        siphash24_compress_typesafe(nh->family, state);
+        if (!IN_SET(nh->family, AF_INET, AF_INET6))
+                return;
+
+        in_addr_hash_func(&nh->gw, nh->family, state);
+        if (!hash_all_parameters)
+                return;
+        siphash24_compress_typesafe(nh->weight, state);
+        siphash24_compress_typesafe(nh->ifindex, state);
+        if (nh->ifindex == 0)
+                siphash24_compress_string(nh->ifname, state); /* For Network or Request object. */
+}
+
+static int route_nexthop_compare_func_full(const RouteNextHop *a, const RouteNextHop *b, bool hash_all_parameters) {
+        int r;
+
+        assert(a);
+        assert(b);
+
+        r = CMP(a->family, b->family);
+        if (r != 0)
+                return r;
+
+        if (!IN_SET(a->family, AF_INET, AF_INET6))
+                return 0;
+
+        r = memcmp(&a->gw, &b->gw, FAMILY_ADDRESS_SIZE(a->family));
+        if (r != 0)
+                return r;
+
+        if (!hash_all_parameters)
+                return 0;
+
+        r = CMP(a->weight, b->weight);
+        if (r != 0)
+                return r;
+
+        r = CMP(a->ifindex, b->ifindex);
+        if (r != 0)
+                return r;
+
+        if (a->ifindex == 0) {
+                r = strcmp_ptr(a->ifname, b->ifname);
+                if (r != 0)
+                        return r;
+        }
+
+        return 0;
+}
+
+static void route_nexthop_hash_func(const RouteNextHop *nh, struct siphash *state) {
+        route_nexthop_hash_func_full(nh, state, /* hash_all_parameters = */ true);
+}
+
+static int route_nexthop_compare_func(const RouteNextHop *a, const RouteNextHop *b) {
+        return route_nexthop_compare_func_full(a, b, /* hash_all_parameters = */ true);
+}
+
+DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(
+        route_nexthop_hash_ops,
+        RouteNextHop,
+        route_nexthop_hash_func,
+        route_nexthop_compare_func,
+        route_nexthop_free);
+
+static size_t route_n_nexthops(const Route *route) {
+        assert(route);
+
+        if (route->nexthop_id != 0 || route_type_is_reject(route))
+                return 0;
+
+        if (ordered_set_isempty(route->nexthops))
+                return 1;
+
+        return ordered_set_size(route->nexthops);
+}
+
+void route_nexthops_hash_func(const Route *route, struct siphash *state) {
+        assert(route);
+
+        size_t nhs = route_n_nexthops(route);
+        siphash24_compress_typesafe(nhs, state);
+
+        switch (nhs) {
+        case 0:
+                siphash24_compress_typesafe(route->nexthop_id, state);
+                return;
+
+        case 1:
+                route_nexthop_hash_func_full(&route->nexthop, state, /* hash_all_parameters = */ false);
+                return;
+
+        default: {
+                RouteNextHop *nh;
+                ORDERED_SET_FOREACH(nh, route->nexthops)
+                        route_nexthop_hash_func(nh, state);
+        }}
+}
+
+int route_nexthops_compare_func(const Route *a, const Route *b) {
+        int r;
+
+        assert(a);
+        assert(b);
+
+        size_t a_nhs = route_n_nexthops(a);
+        size_t b_nhs = route_n_nexthops(b);
+        r = CMP(a_nhs, b_nhs);
+        if (r != 0)
+                return r;
+
+        switch (a_nhs) {
+        case 0:
+                return CMP(a->nexthop_id, b->nexthop_id);
+
+        case 1:
+                return route_nexthop_compare_func_full(&a->nexthop, &b->nexthop, /* hash_all_parameters = */ false);
+
+        default: {
+                RouteNextHop *nh;
+                ORDERED_SET_FOREACH(nh, a->nexthops) {
+                        r = CMP(nh, (RouteNextHop*) ordered_set_get(a->nexthops, nh));
+                        if (r != 0)
+                                return r;
+                }
+                return 0;
+        }}
+}
+
+int route_nexthop_get_link(Manager *manager, Link *link, const RouteNextHop *nh, Link **ret) {
+        assert(manager);
+        assert(nh);
+
+        if (nh->ifindex > 0)
+                return link_get_by_index(manager, nh->ifindex, ret);
+        if (nh->ifname)
+                return link_get_by_name(manager, nh->ifname, ret);
+
+        if (link) {
+                if (ret)
+                        *ret = link;
+                return 0;
+        }
+
+        return -ENOENT;
+}
+
+static bool route_nexthop_is_ready_to_configure(const RouteNextHop *nh, Link *link, bool onlink) {
+        assert(nh);
+        assert(link);
+
+        if (route_nexthop_get_link(link->manager, link, nh, &link))
+                return false;
+
+        if (!link_is_ready_to_configure(link, /* allow_unmanaged = */ true))
+                return false;
+
+        /* If the interface is not managed by us, we request that the interface has carrier.
+         * That is, ConfigureWithoutCarrier=no is the default even for unamanaged interfaces. */
+        if (!link->network && !link_has_carrier(link))
+                return false;
+
+        return gateway_is_ready(link, onlink, nh->family, &nh->gw);
+}
+
+int route_nexthops_is_ready_to_configure(const Route *route, Link *link) {
+        int r;
+
+        assert(route);
+        assert(link);
+
+        Manager *manager = ASSERT_PTR(link->manager);
+
+        if (route->nexthop_id != 0) {
+                struct nexthop_grp *nhg;
+                NextHop *nh;
+
+                r = nexthop_is_ready(manager, route->nexthop_id, &nh);
+                if (r <= 0)
+                        return r;
+
+                HASHMAP_FOREACH(nhg, nh->group) {
+                        r = nexthop_is_ready(manager, nhg->id, NULL);
+                        if (r <= 0)
+                                return r;
+                }
+
+                return true;
+        }
+
+        if (route_type_is_reject(route))
+                return true;
+
+        if (ordered_set_isempty(route->nexthops))
+                return route_nexthop_is_ready_to_configure(&route->nexthop, link, FLAGS_SET(route->flags, RTNH_F_ONLINK));
+
+        RouteNextHop *nh;
+        ORDERED_SET_FOREACH(nh, route->nexthops)
+                if (!route_nexthop_is_ready_to_configure(nh, link, FLAGS_SET(route->flags, RTNH_F_ONLINK)))
+                        return false;
+
+        return true;
+}
+
+int route_nexthops_to_string(const Route *route, char **ret) {
+        _cleanup_free_ char *buf = NULL;
+        int r;
+
+        assert(route);
+        assert(ret);
+
+        if (route->nexthop_id != 0) {
+                if (asprintf(&buf, "nexthop: %"PRIu32, route->nexthop_id) < 0)
+                        return -ENOMEM;
+
+                *ret = TAKE_PTR(buf);
+                return 0;
+        }
+
+        if (route_type_is_reject(route)) {
+                buf = strdup("gw: n/a");
+                if (!buf)
+                        return -ENOMEM;
+
+                *ret = TAKE_PTR(buf);
+                return 0;
+        }
+
+        if (ordered_set_isempty(route->nexthops)) {
+                if (in_addr_is_set(route->nexthop.family, &route->nexthop.gw))
+                        buf = strjoin("gw: ", IN_ADDR_TO_STRING(route->nexthop.family, &route->nexthop.gw));
+                else if (route->gateway_from_dhcp_or_ra) {
+                        if (route->nexthop.family == AF_INET)
+                                buf = strdup("gw: _dhcp4");
+                        else if (route->nexthop.family == AF_INET6)
+                                buf = strdup("gw: _ipv6ra");
+                        else
+                                buf = strdup("gw: _dhcp");
+                } else
+                        buf = strdup("gw: n/a");
+                if (!buf)
+                        return -ENOMEM;
+
+                *ret = TAKE_PTR(buf);
+                return 0;
+        }
+
+        RouteNextHop *nh;
+        ORDERED_SET_FOREACH(nh, route->nexthops) {
+                const char *s = in_addr_is_set(nh->family, &nh->gw) ? IN_ADDR_TO_STRING(nh->family, &nh->gw) : NULL;
+
+                if (nh->ifindex > 0)
+                        r = strextendf_with_separator(&buf, ",", "%s@%i:%"PRIu32, strempty(s), nh->ifindex, nh->weight + 1);
+                else if (nh->ifname)
+                        r = strextendf_with_separator(&buf, ",", "%s@%s:%"PRIu32, strempty(s), nh->ifname, nh->weight + 1);
+                else
+                        r = strextendf_with_separator(&buf, ",", "%s:%"PRIu32, strempty(s), nh->weight + 1);
+                if (r < 0)
+                        return r;
+        }
+
+        char *p = strjoin("gw: ", strna(buf));
+        if (!p)
+                return -ENOMEM;
+
+        *ret = p;
+        return 0;
+}
+
+static int append_nexthop_one(Link *link, const Route *route, const RouteNextHop *nh, struct rtattr **rta, size_t offset) {
+        struct rtnexthop *rtnh;
+        struct rtattr *new_rta;
+        int r;
+
+        assert(route);
+        assert(IN_SET(route->family, AF_INET, AF_INET6));
+        assert(nh);
+        assert(rta);
+        assert(*rta);
+
+        if (nh->ifindex <= 0) {
+                assert(link);
+                assert(link->manager);
+
+                r = route_nexthop_get_link(link->manager, link, nh, &link);
+                if (r < 0)
+                        return r;
+        }
+
+        new_rta = realloc(*rta, RTA_ALIGN((*rta)->rta_len) + RTA_SPACE(sizeof(struct rtnexthop)));
+        if (!new_rta)
+                return -ENOMEM;
+        *rta = new_rta;
+
+        rtnh = (struct rtnexthop *)((uint8_t *) *rta + offset);
+        *rtnh = (struct rtnexthop) {
+                .rtnh_len = sizeof(*rtnh),
+                .rtnh_ifindex = nh->ifindex > 0 ? nh->ifindex : link->ifindex,
+                .rtnh_hops = nh->weight,
+        };
+
+        (*rta)->rta_len += sizeof(struct rtnexthop);
+
+        if (nh->family == route->family) {
+                r = rtattr_append_attribute(rta, RTA_GATEWAY, &nh->gw, FAMILY_ADDRESS_SIZE(nh->family));
+                if (r < 0)
+                        goto clear;
+
+                rtnh = (struct rtnexthop *)((uint8_t *) *rta + offset);
+                rtnh->rtnh_len += RTA_SPACE(FAMILY_ADDRESS_SIZE(nh->family));
+
+        } else if (nh->family == AF_INET6) {
+                assert(route->family == AF_INET);
+
+                r = rtattr_append_attribute(rta, RTA_VIA,
+                                            &(RouteVia) {
+                                                    .family = nh->family,
+                                                    .address = nh->gw,
+                                            }, sizeof(RouteVia));
+                if (r < 0)
+                        goto clear;
+
+                rtnh = (struct rtnexthop *)((uint8_t *) *rta + offset);
+                rtnh->rtnh_len += RTA_SPACE(sizeof(RouteVia));
+
+        } else if (nh->family == AF_INET)
+                assert_not_reached();
+
+        return 0;
+
+clear:
+        (*rta)->rta_len -= sizeof(struct rtnexthop);
+        return r;
+}
+
+static int netlink_message_append_multipath_route(Link *link, const Route *route, sd_netlink_message *message) {
+        _cleanup_free_ struct rtattr *rta = NULL;
+        size_t offset;
+        int r;
+
+        assert(route);
+        assert(message);
+
+        if (ordered_set_isempty(route->nexthops))
+                return 0;
+
+        rta = new(struct rtattr, 1);
+        if (!rta)
+                return -ENOMEM;
+
+        *rta = (struct rtattr) {
+                .rta_type = RTA_MULTIPATH,
+                .rta_len = RTA_LENGTH(0),
+        };
+        offset = (uint8_t *) RTA_DATA(rta) - (uint8_t *) rta;
+
+        RouteNextHop *nh;
+        ORDERED_SET_FOREACH(nh, route->nexthops) {
+                struct rtnexthop *rtnh;
+
+                r = append_nexthop_one(link, route, nh, &rta, offset);
+                if (r < 0)
+                        return r;
+
+                rtnh = (struct rtnexthop *)((uint8_t *) rta + offset);
+                offset = (uint8_t *) RTNH_NEXT(rtnh) - (uint8_t *) rta;
+        }
+
+        return sd_netlink_message_append_data(message, RTA_MULTIPATH, RTA_DATA(rta), RTA_PAYLOAD(rta));
+}
+
+int route_nexthops_set_netlink_message(Link *link, const Route *route, sd_netlink_message *message) {
+        int r;
+
+        assert(route);
+        assert(IN_SET(route->family, AF_INET, AF_INET6));
+        assert(message);
+
+        if (route->nexthop_id != 0)
+                return sd_netlink_message_append_u32(message, RTA_NH_ID, route->nexthop_id);
+
+        if (route_type_is_reject(route))
+                return 0;
+
+        if (ordered_set_isempty(route->nexthops)) {
+
+                if (in_addr_is_set(route->nexthop.family, &route->nexthop.gw)) {
+                        if (route->nexthop.family == route->family)
+                                r = netlink_message_append_in_addr_union(message, RTA_GATEWAY, route->nexthop.family, &route->nexthop.gw);
+                        else {
+                                assert(route->family == AF_INET);
+                                r = sd_netlink_message_append_data(message, RTA_VIA,
+                                                                   &(const RouteVia) {
+                                                                           .family = route->nexthop.family,
+                                                                           .address = route->nexthop.gw,
+                                                                   }, sizeof(RouteVia));
+                        }
+                        if (r < 0)
+                                return r;
+                }
+
+                return sd_netlink_message_append_u32(message, RTA_OIF, route->nexthop.ifindex > 0 ? route->nexthop.ifindex : ASSERT_PTR(link)->ifindex);
+        }
+
+        return netlink_message_append_multipath_route(link, route, message);
+}
+
+static int route_parse_nexthops(Route *route, const struct rtnexthop *rtnh, size_t size) {
+        _cleanup_ordered_set_free_ OrderedSet *nexthops = NULL;
+        int r;
+
+        assert(route);
+        assert(IN_SET(route->family, AF_INET, AF_INET6));
+        assert(rtnh);
+
+        if (size < sizeof(struct rtnexthop))
+                return -EBADMSG;
+
+        for (; size >= sizeof(struct rtnexthop); ) {
+                _cleanup_(route_nexthop_freep) RouteNextHop *nh = NULL;
+
+                if (NLMSG_ALIGN(rtnh->rtnh_len) > size)
+                        return -EBADMSG;
+
+                if (rtnh->rtnh_len < sizeof(struct rtnexthop))
+                        return -EBADMSG;
+
+                nh = new(RouteNextHop, 1);
+                if (!nh)
+                        return -ENOMEM;
+
+                *nh = (RouteNextHop) {
+                        .ifindex = rtnh->rtnh_ifindex,
+                        .weight = rtnh->rtnh_hops,
+                };
+
+                if (rtnh->rtnh_len > sizeof(struct rtnexthop)) {
+                        size_t len = rtnh->rtnh_len - sizeof(struct rtnexthop);
+                        bool have_gw = false;
+
+                        for (struct rtattr *attr = RTNH_DATA(rtnh); RTA_OK(attr, len); attr = RTA_NEXT(attr, len)) {
+
+                                switch (attr->rta_type) {
+                                case RTA_GATEWAY:
+                                        if (have_gw)
+                                                return -EBADMSG;
+
+                                        if (attr->rta_len != RTA_LENGTH(FAMILY_ADDRESS_SIZE(route->family)))
+                                                return -EBADMSG;
+
+                                        nh->family = route->family;
+                                        memcpy(&nh->gw, RTA_DATA(attr), FAMILY_ADDRESS_SIZE(nh->family));
+                                        have_gw = true;
+                                        break;
+
+                                case RTA_VIA:
+                                        if (have_gw)
+                                                return -EBADMSG;
+
+                                        if (route->family != AF_INET)
+                                                return -EBADMSG;
+
+                                        if (attr->rta_len != RTA_LENGTH(sizeof(RouteVia)))
+                                                return -EBADMSG;
+
+                                        RouteVia *via = RTA_DATA(attr);
+                                        if (via->family != AF_INET6)
+                                                return -EBADMSG;
+
+                                        nh->family = via->family;
+                                        nh->gw = via->address;
+                                        have_gw = true;
+                                        break;
+                                }
+                        }
+                }
+
+                r = ordered_set_ensure_put(&nexthops, &route_nexthop_hash_ops, nh);
+                assert(r != 0);
+                if (r > 0)
+                        TAKE_PTR(nh);
+                else if (r != -EEXIST)
+                        return r;
+
+                size -= NLMSG_ALIGN(rtnh->rtnh_len);
+                rtnh = RTNH_NEXT(rtnh);
+        }
+
+        ordered_set_free(route->nexthops);
+        route->nexthops = TAKE_PTR(nexthops);
+        return 0;
+}
+
+int route_nexthops_read_netlink_message(Route *route, sd_netlink_message *message) {
+        int r;
+
+        assert(route);
+        assert(message);
+
+        r = sd_netlink_message_read_u32(message, RTA_NH_ID, &route->nexthop_id);
+        if (r < 0 && r != -ENODATA)
+                return log_warning_errno(r, "rtnl: received route message with invalid nexthop id, ignoring: %m");
+
+        if (route->nexthop_id != 0 || route_type_is_reject(route))
+                /* IPv6 routes with reject type are always assigned to the loopback interface. See kernel's
+                 * fib6_nh_init() in net/ipv6/route.c. However, we'd like to make it consistent with IPv4
+                 * routes. Hence, skip reading of RTA_OIF. */
+                return 0;
+
+        uint32_t ifindex;
+        r = sd_netlink_message_read_u32(message, RTA_OIF, &ifindex);
+        if (r >= 0)
+                route->nexthop.ifindex = (int) ifindex;
+        else if (r != -ENODATA)
+                return log_warning_errno(r, "rtnl: could not get ifindex from route message, ignoring: %m");
+
+        if (route->nexthop.ifindex > 0) {
+                r = netlink_message_read_in_addr_union(message, RTA_GATEWAY, route->family, &route->nexthop.gw);
+                if (r >= 0) {
+                        route->nexthop.family = route->family;
+                        return 0;
+                }
+                if (r != -ENODATA)
+                        return log_warning_errno(r, "rtnl: received route message without valid gateway, ignoring: %m");
+
+                if (route->family != AF_INET)
+                        return 0;
+
+                RouteVia via;
+                r = sd_netlink_message_read(message, RTA_VIA, sizeof(via), &via);
+                if (r >= 0) {
+                        route->nexthop.family = via.family;
+                        route->nexthop.gw = via.address;
+                        return 0;
+                }
+                if (r != -ENODATA)
+                        return log_warning_errno(r, "rtnl: received route message without valid gateway, ignoring: %m");
+
+                return 0;
+        }
+
+        size_t rta_len;
+        _cleanup_free_ void *rta = NULL;
+        r = sd_netlink_message_read_data(message, RTA_MULTIPATH, &rta_len, &rta);
+        if (r == -ENODATA)
+                return 0;
+        if (r < 0)
+                return log_warning_errno(r, "rtnl: failed to read RTA_MULTIPATH attribute, ignoring: %m");
+
+        r = route_parse_nexthops(route, rta, rta_len);
+        if (r < 0)
+                return log_warning_errno(r, "rtnl: failed to parse RTA_MULTIPATH attribute, ignoring: %m");
+
+        return 0;
+}
+
+int route_section_verify_nexthops(Route *route) {
+        assert(route);
+        assert(route->section);
+
+        if (route->gateway_from_dhcp_or_ra) {
+                assert(route->network);
+
+                if (route->nexthop.family == AF_UNSPEC)
+                        /* When deprecated Gateway=_dhcp is set, then assume gateway family based on other settings. */
+                        switch (route->family) {
+                        case AF_UNSPEC:
+                                log_warning("%s: Deprecated value \"_dhcp\" is specified for Gateway= in [Route] section from line %u. "
+                                            "Please use \"_dhcp4\" or \"_ipv6ra\" instead. Assuming \"_dhcp4\".",
+                                            route->section->filename, route->section->line);
+
+                                route->nexthop.family = route->family = AF_INET;
+                                break;
+                        case AF_INET:
+                        case AF_INET6:
+                                log_warning("%s: Deprecated value \"_dhcp\" is specified for Gateway= in [Route] section from line %u. "
+                                            "Assuming \"%s\" based on Destination=, Source=, or PreferredSource= setting.",
+                                            route->section->filename, route->section->line, route->family == AF_INET ? "_dhcp4" : "_ipv6ra");
+
+                                route->nexthop.family = route->family;
+                                break;
+                        default:
+                                return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
+                                                         "%s: Invalid route family. Ignoring [Route] section from line %u.",
+                                                         route->section->filename, route->section->line);
+                        }
+
+                if (route->nexthop.family == AF_INET && !FLAGS_SET(route->network->dhcp, ADDRESS_FAMILY_IPV4))
+                        return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
+                                                 "%s: Gateway=\"_dhcp4\" is specified but DHCPv4 client is disabled. "
+                                                 "Ignoring [Route] section from line %u.",
+                                                 route->section->filename, route->section->line);
+
+                if (route->nexthop.family == AF_INET6 && !route->network->ipv6_accept_ra)
+                        return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
+                                                 "%s: Gateway=\"_ipv6ra\" is specified but IPv6AcceptRA= is disabled. "
+                                                 "Ignoring [Route] section from line %u.",
+                                                 route->section->filename, route->section->line);
+        }
+
+        /* When only Gateway= is specified, assume the route family based on the Gateway address. */
+        if (route->family == AF_UNSPEC)
+                route->family = route->nexthop.family;
+
+        if (route->family == AF_UNSPEC) {
+                assert(route->section);
+
+                return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
+                                         "%s: Route section without Gateway=, Destination=, Source=, "
+                                         "or PreferredSource= field configured. "
+                                         "Ignoring [Route] section from line %u.",
+                                         route->section->filename, route->section->line);
+        }
+
+        if (route->gateway_onlink < 0 && in_addr_is_set(route->nexthop.family, &route->nexthop.gw) &&
+            route->network && ordered_hashmap_isempty(route->network->addresses_by_section)) {
+                /* If no address is configured, in most cases the gateway cannot be reachable.
+                 * TODO: we may need to improve the condition above. */
+                log_warning("%s: Gateway= without static address configured. "
+                            "Enabling GatewayOnLink= option.",
+                            route->section->filename);
+                route->gateway_onlink = true;
+        }
+
+        if (route->gateway_onlink >= 0)
+                SET_FLAG(route->flags, RTNH_F_ONLINK, route->gateway_onlink);
+
+        if (route->family == AF_INET6) {
+                if (route->nexthop.family == AF_INET)
+                        return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
+                                                 "%s: IPv4 gateway is configured for IPv6 route. "
+                                                 "Ignoring [Route] section from line %u.",
+                                                 route->section->filename, route->section->line);
+
+                RouteNextHop *nh;
+                ORDERED_SET_FOREACH(nh, route->nexthops)
+                        if (nh->family == AF_INET)
+                                return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
+                                                         "%s: IPv4 multipath route is specified for IPv6 route. "
+                                                         "Ignoring [Route] section from line %u.",
+                                                         route->section->filename, route->section->line);
+        }
+
+        if (route->nexthop_id != 0 &&
+            (route->gateway_from_dhcp_or_ra ||
+             in_addr_is_set(route->nexthop.family, &route->nexthop.gw) ||
+             !ordered_set_isempty(route->nexthops)))
+                return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
+                                         "%s: NextHopId= cannot be specified with Gateway= or MultiPathRoute=. "
+                                         "Ignoring [Route] section from line %u.",
+                                         route->section->filename, route->section->line);
+
+        if (route_type_is_reject(route) &&
+            (route->gateway_from_dhcp_or_ra ||
+             in_addr_is_set(route->nexthop.family, &route->nexthop.gw) ||
+             !ordered_set_isempty(route->nexthops)))
+                return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
+                                         "%s: reject type route cannot be specified with Gateway= or MultiPathRoute=. "
+                                         "Ignoring [Route] section from line %u.",
+                                         route->section->filename, route->section->line);
+
+        if ((route->gateway_from_dhcp_or_ra ||
+             in_addr_is_set(route->nexthop.family, &route->nexthop.gw)) &&
+            !ordered_set_isempty(route->nexthops))
+                return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
+                                         "%s: Gateway= cannot be specified with MultiPathRoute=. "
+                                         "Ignoring [Route] section from line %u.",
+                                         route->section->filename, route->section->line);
+
+        return 0;
+}
+
+int config_parse_gateway(
+                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;
+        int r;
+
+        assert(filename);
+        assert(section);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        if (streq(section, "Network")) {
+                /* we are not in an Route section, so use line number instead */
+                r = route_new_static(network, filename, 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;
+                }
+        } else {
+                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->gateway_from_dhcp_or_ra = false;
+                        route->nexthop.family = AF_UNSPEC;
+                        route->nexthop.gw = IN_ADDR_NULL;
+                        TAKE_PTR(route);
+                        return 0;
+                }
+
+                if (streq(rvalue, "_dhcp")) {
+                        route->gateway_from_dhcp_or_ra = true;
+                        route->nexthop.family = AF_UNSPEC;
+                        route->nexthop.gw = IN_ADDR_NULL;
+                        TAKE_PTR(route);
+                        return 0;
+                }
+
+                if (streq(rvalue, "_dhcp4")) {
+                        route->gateway_from_dhcp_or_ra = true;
+                        route->nexthop.family = AF_INET;
+                        route->nexthop.gw = IN_ADDR_NULL;
+                        TAKE_PTR(route);
+                        return 0;
+                }
+
+                if (streq(rvalue, "_ipv6ra")) {
+                        route->gateway_from_dhcp_or_ra = true;
+                        route->nexthop.family = AF_INET6;
+                        route->nexthop.gw = IN_ADDR_NULL;
+                        TAKE_PTR(route);
+                        return 0;
+                }
+        }
+
+        r = in_addr_from_string_auto(rvalue, &route->nexthop.family, &route->nexthop.gw);
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "Invalid %s='%s', ignoring assignment: %m", lvalue, rvalue);
+                return 0;
+        }
+
+        route->gateway_from_dhcp_or_ra = false;
+        TAKE_PTR(route);
+        return 0;
+}
+
+int config_parse_route_gateway_onlink(
+                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;
+        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;
+        }
+
+        r = config_parse_tristate(unit, filename, line, section, section_line, lvalue, ltype, rvalue,
+                                  &route->gateway_onlink, network);
+        if (r <= 0)
+                return r;
+
+        TAKE_PTR(route);
+        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 *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_(route_nexthop_freep) RouteNextHop *nh = NULL;
+        _cleanup_(route_free_or_set_invalidp) Route *route = NULL;
+        _cleanup_free_ char *word = NULL;
+        Network *network = userdata;
+        const char *p;
+        char *dev;
+        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->nexthops = ordered_set_free(route->nexthops);
+                TAKE_PTR(route);
+                return 0;
+        }
+
+        nh = new0(RouteNextHop, 1);
+        if (!nh)
+                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)
+                        nh->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;
+                        }
+
+                        nh->ifname = strdup(dev);
+                        if (!nh->ifname)
+                                return log_oom();
+                }
+        }
+
+        r = in_addr_from_string_auto(word, &nh->family, &nh->gw);
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "Invalid multipath route gateway '%s', ignoring assignment: %m", rvalue);
+                return 0;
+        }
+
+        if (!isempty(p)) {
+                r = safe_atou32(p, &nh->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 (nh->weight == 0 || nh->weight > 256) {
+                        log_syntax(unit, LOG_WARNING, filename, line, 0,
+                                   "Invalid multipath route weight, ignoring assignment: %s", p);
+                        return 0;
+                }
+                nh->weight--;
+        }
+
+        r = ordered_set_ensure_put(&route->nexthops, &route_nexthop_hash_ops, nh);
+        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(nh);
+        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..f06a2c4
--- /dev/null
@@ -0,0 +1,48 @@
+/* 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 "in-addr-util.h"
+#include "macro.h"
+#include "siphash24.h"
+
+typedef struct Link Link;
+typedef struct Manager Manager;
+typedef struct Route Route;
+
+typedef struct RouteNextHop {
+        int family; /* used in RTA_VIA (IPv4 only) */
+        union in_addr_union gw; /* RTA_GATEWAY or RTA_VIA (IPv4 only) */
+        uint32_t weight; /* rtnh_hops */
+        int ifindex; /* RTA_OIF(u32) or rtnh_ifindex */
+        char *ifname; /* only used by Route object owned by Network object */
+        /* unsupported attributes: RTA_FLOW (IPv4 only), RTA_ENCAP_TYPE, RTA_ENCAP. */
+} RouteNextHop;
+
+RouteNextHop* route_nexthop_free(RouteNextHop *nh);
+DEFINE_TRIVIAL_CLEANUP_FUNC(RouteNextHop*, route_nexthop_free);
+
+void route_nexthops_done(Route *route);
+
+void route_nexthops_hash_func(const Route *route, struct siphash *state);
+int route_nexthops_compare_func(const Route *a, const Route *b);
+
+int route_nexthop_get_link(Manager *manager, Link *link, const RouteNextHop *nh, Link **ret);
+int route_nexthops_is_ready_to_configure(const Route *route, Link *link);
+
+int route_nexthops_to_string(const Route *route, char **ret);
+
+int route_nexthops_set_netlink_message(Link *link, const Route *route, sd_netlink_message *message);
+int route_nexthops_read_netlink_message(Route *route, sd_netlink_message *message);
+
+int route_section_verify_nexthops(Route *route);
+
+CONFIG_PARSER_PROTOTYPE(config_parse_gateway);
+CONFIG_PARSER_PROTOTYPE(config_parse_route_gateway_onlink);
+CONFIG_PARSER_PROTOTYPE(config_parse_route_nexthop);
+CONFIG_PARSER_PROTOTYPE(config_parse_multipath_route);
index d49a0b95124a2cac1295f809c372eea12085b09f..fde7dfa5d42e52c8b1bd03f130836de10a805547 100644 (file)
@@ -39,6 +39,12 @@ unsigned routes_max(void) {
         return cached;
 }
 
+bool route_type_is_reject(const Route *route) {
+        assert(route);
+
+        return IN_SET(route->type, RTN_UNREACHABLE, RTN_PROHIBIT, RTN_BLACKHOLE, RTN_THROW);
+}
+
 static bool route_lifetime_is_valid(const Route *route) {
         assert(route);
 
@@ -68,7 +74,7 @@ bool link_find_default_gateway(Link *link, int family, Route **gw) {
                         continue;
                 if (route->scope != RT_SCOPE_UNIVERSE)
                         continue;
-                if (!in_addr_is_set(route->gw_family, &route->gw))
+                if (!in_addr_is_set(route->nexthop.family, &route->nexthop.gw))
                         continue;
 
                 /* Found a default gateway. */
@@ -77,7 +83,7 @@ bool link_find_default_gateway(Link *link, int family, Route **gw) {
 
                 /* If we have already found another gw, then let's compare their weight and priority. */
                 if (*gw) {
-                        if (route->gw_weight > (*gw)->gw_weight)
+                        if (route->nexthop.weight > (*gw)->nexthop.weight)
                                 continue;
                         if (route->priority >= (*gw)->priority)
                                 continue;
index f326888c9347dd2c9d87ed58966264a054ba26dc..eba823a2d0d5c3a1d23c73f507e4aca46acdb37b 100644 (file)
@@ -13,6 +13,8 @@ typedef struct Route Route;
 
 unsigned routes_max(void);
 
+bool route_type_is_reject(const Route *route);
+
 bool link_find_default_gateway(Link *link, int family, Route **gw);
 static inline bool link_has_default_gateway(Link *link, int family) {
         return link_find_default_gateway(link, family, NULL);
index 72dbf9ed0675179315cf2add7e1fa2eaf2ae04a5..06a114b5a06f81ce61e3d30c86a3139507ac3f63 100644 (file)
 #include "vrf.h"
 #include "wireguard.h"
 
-int route_new(Route **ret) {
-        _cleanup_(route_freep) Route *route = NULL;
-
-        route = new(Route, 1);
-        if (!route)
-                return -ENOMEM;
-
-        *route = (Route) {
-                .family = AF_UNSPEC,
-                .scope = RT_SCOPE_UNIVERSE,
-                .protocol = RTPROT_UNSPEC,
-                .type = RTN_UNICAST,
-                .table = RT_TABLE_MAIN,
-                .lifetime_usec = USEC_INFINITY,
-                .quickack = -1,
-                .fast_open_no_cookie = -1,
-                .gateway_onlink = -1,
-        };
-
-        *ret = TAKE_PTR(route);
-
-        return 0;
-}
-
-static 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;
-
-        assert(network);
-        assert(ret);
-        assert(filename);
-        assert(section_line > 0);
-
-        r = config_section_new(filename, section_line, &n);
-        if (r < 0)
-                return r;
-
-        route = hashmap_get(network->routes_by_section, n);
-        if (route) {
-                *ret = TAKE_PTR(route);
-                return 0;
-        }
-
-        if (hashmap_size(network->routes_by_section) >= routes_max())
-                return -E2BIG;
-
-        r = route_new(&route);
-        if (r < 0)
-                return r;
-
-        route->protocol = RTPROT_STATIC;
-        route->network = network;
-        route->section = TAKE_PTR(n);
-        route->source = NETWORK_CONFIG_SOURCE_STATIC;
-
-        r = hashmap_ensure_put(&network->routes_by_section, &config_section_hash_ops, route->section, route);
-        if (r < 0)
-                return r;
-
-        *ret = TAKE_PTR(route);
-        return 0;
-}
-
-Route *route_free(Route *route) {
+Route* route_free(Route *route) {
         if (!route)
                 return NULL;
 
@@ -94,20 +30,20 @@ Route *route_free(Route *route) {
                 hashmap_remove(route->network->routes_by_section, route->section);
         }
 
-        config_section_free(route->section);
-
         if (route->link)
                 set_remove(route->link->routes, route);
 
         if (route->manager)
                 set_remove(route->manager->routes, route);
 
-        ordered_set_free_with_destructor(route->multipath_routes, multipath_route_free);
+        if (route->wireguard)
+                set_remove(route->wireguard->routes, route);
 
+        config_section_free(route->section);
+        route_nexthops_done(route);
+        route_metric_done(&route->metric);
         sd_event_source_disable_unref(route->expire);
 
-        free(route->tcp_congestion_control_algo);
-
         return mfree(route);
 }
 
@@ -118,35 +54,51 @@ static void route_hash_func(const Route *route, struct siphash *state) {
 
         switch (route->family) {
         case AF_INET:
-        case AF_INET6:
-                siphash24_compress_typesafe(route->dst_prefixlen, state);
+                /* First, the table, destination prefix, priority, and tos (dscp), are used to find routes.
+                 * See fib_table_insert(), fib_find_node(), and fib_find_alias() in net/ipv4/fib_trie.c of the kernel. */
+                siphash24_compress_typesafe(route->table, state);
                 in_addr_hash_func(&route->dst, route->family, state);
+                siphash24_compress_typesafe(route->dst_prefixlen, state);
+                siphash24_compress_typesafe(route->priority, state);
+                siphash24_compress_typesafe(route->tos, state);
 
-                siphash24_compress_typesafe(route->src_prefixlen, state);
-                in_addr_hash_func(&route->src, route->family, state);
+                /* Then, protocol, scope, type, flags, prefsrc, metrics (RTAX_* attributes), and nexthops (gateways)
+                 * are used to find routes. See fib_find_info() in net/ipv4/fib_semantics.c of the kernel. */
+                siphash24_compress_typesafe(route->protocol, state);
+                siphash24_compress_typesafe(route->scope, state);
+                siphash24_compress_typesafe(route->type, state);
+                unsigned flags = route->flags & ~RTNH_COMPARE_MASK;
+                siphash24_compress_typesafe(flags, state);
+                in_addr_hash_func(&route->prefsrc, route->family, state);
 
-                siphash24_compress_typesafe(route->gw_family, state);
-                if (IN_SET(route->gw_family, AF_INET, AF_INET6)) {
-                        in_addr_hash_func(&route->gw, route->gw_family, state);
-                        siphash24_compress_typesafe(route->gw_weight, state);
-                }
+                /* nexthops (id, number of nexthops, nexthop) */
+                route_nexthops_hash_func(route, state);
 
-                in_addr_hash_func(&route->prefsrc, route->family, state);
+                /* metrics */
+                route_metric_hash_func(&route->metric, state);
+                break;
 
-                siphash24_compress_typesafe(route->tos, state);
-                siphash24_compress_typesafe(route->priority, state);
+        case AF_INET6:
+                /* First, table and destination prefix are used for classifying routes.
+                 * See fib6_add() and fib6_add_1() in net/ipv6/ip6_fib.c of the kernel. */
                 siphash24_compress_typesafe(route->table, state);
-                siphash24_compress_typesafe(route->protocol, state);
-                siphash24_compress_typesafe(route->scope, state);
-                siphash24_compress_typesafe(route->type, state);
+                in_addr_hash_func(&route->dst, route->family, state);
+                siphash24_compress_typesafe(route->dst_prefixlen, state);
 
-                siphash24_compress_typesafe(route->initcwnd, state);
-                siphash24_compress_typesafe(route->initrwnd, state);
+                /* Then, source prefix is used. See fib6_add(). */
+                in_addr_hash_func(&route->src, route->family, state);
+                siphash24_compress_typesafe(route->src_prefixlen, state);
+
+                /* See fib6_add_rt2node(). */
+                siphash24_compress_typesafe(route->priority, state);
 
-                siphash24_compress_typesafe(route->advmss, state);
-                siphash24_compress_typesafe(route->nexthop_id, state);
+                /* See rt6_duplicate_nexthop() in include/net/ip6_route.h of the kernel.
+                 * Here, we hash nexthop in a similar way as the one for IPv4. */
+                route_nexthops_hash_func(route, state);
 
+                /* Unlike IPv4 routes, metrics are not taken into account. */
                 break;
+
         default:
                 /* treat any other address family as AF_UNSPEC */
                 break;
@@ -162,8 +114,7 @@ static int route_compare_func(const Route *a, const Route *b) {
 
         switch (a->family) {
         case AF_INET:
-        case AF_INET6:
-                r = CMP(a->dst_prefixlen, b->dst_prefixlen);
+                r = CMP(a->table, b->table);
                 if (r != 0)
                         return r;
 
@@ -171,73 +122,71 @@ static int route_compare_func(const Route *a, const Route *b) {
                 if (r != 0)
                         return r;
 
-                r = CMP(a->src_prefixlen, b->src_prefixlen);
+                r = CMP(a->dst_prefixlen, b->dst_prefixlen);
                 if (r != 0)
                         return r;
 
-                r = memcmp(&a->src, &b->src, FAMILY_ADDRESS_SIZE(a->family));
+                r = CMP(a->priority, b->priority);
                 if (r != 0)
                         return r;
 
-                r = CMP(a->gw_family, b->gw_family);
+                r = CMP(a->tos, b->tos);
                 if (r != 0)
                         return r;
 
-                if (IN_SET(a->gw_family, AF_INET, AF_INET6)) {
-                        r = memcmp(&a->gw, &b->gw, FAMILY_ADDRESS_SIZE(a->family));
-                        if (r != 0)
-                                return r;
-
-                        r = CMP(a->gw_weight, b->gw_weight);
-                        if (r != 0)
-                                return r;
-                }
+                r = CMP(a->protocol, b->protocol);
+                if (r != 0)
+                        return r;
 
-                r = memcmp(&a->prefsrc, &b->prefsrc, FAMILY_ADDRESS_SIZE(a->family));
+                r = CMP(a->scope, b->scope);
                 if (r != 0)
                         return r;
 
-                r = CMP(a->tos, b->tos);
+                r = CMP(a->type, b->type);
                 if (r != 0)
                         return r;
 
-                r = CMP(a->priority, b->priority);
+                r = CMP(a->flags & ~RTNH_COMPARE_MASK, b->flags & ~RTNH_COMPARE_MASK);
                 if (r != 0)
                         return r;
 
-                r = CMP(a->table, b->table);
+                r = memcmp(&a->prefsrc, &b->prefsrc, FAMILY_ADDRESS_SIZE(a->family));
                 if (r != 0)
                         return r;
 
-                r = CMP(a->protocol, b->protocol);
+                r = route_nexthops_compare_func(a, b);
                 if (r != 0)
                         return r;
 
-                r = CMP(a->scope, b->scope);
+                return route_metric_compare_func(&a->metric, &b->metric);
+
+        case AF_INET6:
+                r = CMP(a->table, b->table);
                 if (r != 0)
                         return r;
 
-                r = CMP(a->type, b->type);
+                r = memcmp(&a->dst, &b->dst, FAMILY_ADDRESS_SIZE(a->family));
                 if (r != 0)
                         return r;
 
-                r = CMP(a->initcwnd, b->initcwnd);
+                r = CMP(a->dst_prefixlen, b->dst_prefixlen);
                 if (r != 0)
                         return r;
 
-                r = CMP(a->initrwnd, b->initrwnd);
+                r = memcmp(&a->src, &b->src, FAMILY_ADDRESS_SIZE(a->family));
                 if (r != 0)
                         return r;
 
-                r = CMP(a->advmss, b->advmss);
+                r = CMP(a->src_prefixlen, b->src_prefixlen);
                 if (r != 0)
                         return r;
 
-                r = CMP(a->nexthop_id, b->nexthop_id);
+                r = CMP(a->priority, b->priority);
                 if (r != 0)
                         return r;
 
-                return 0;
+                return route_nexthops_compare_func(a, b);
+
         default:
                 /* treat any other address family as AF_UNSPEC */
                 return 0;
@@ -251,16 +200,66 @@ DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(
                 route_compare_func,
                 route_free);
 
-static bool route_type_is_reject(const Route *route) {
-        assert(route);
+int route_new(Route **ret) {
+        _cleanup_(route_freep) Route *route = NULL;
+
+        route = new(Route, 1);
+        if (!route)
+                return -ENOMEM;
+
+        *route = (Route) {
+                .family = AF_UNSPEC,
+                .scope = RT_SCOPE_UNIVERSE,
+                .protocol = RTPROT_UNSPEC,
+                .type = RTN_UNICAST,
+                .table = RT_TABLE_MAIN,
+                .lifetime_usec = USEC_INFINITY,
+                .gateway_onlink = -1,
+        };
+
+        *ret = TAKE_PTR(route);
 
-        return IN_SET(route->type, RTN_UNREACHABLE, RTN_PROHIBIT, RTN_BLACKHOLE, RTN_THROW);
+        return 0;
 }
 
-static bool route_needs_convert(const Route *route) {
-        assert(route);
+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;
+
+        assert(network);
+        assert(ret);
+        assert(filename);
+        assert(section_line > 0);
+
+        r = config_section_new(filename, section_line, &n);
+        if (r < 0)
+                return r;
+
+        route = hashmap_get(network->routes_by_section, n);
+        if (route) {
+                *ret = TAKE_PTR(route);
+                return 0;
+        }
+
+        if (hashmap_size(network->routes_by_section) >= routes_max())
+                return -E2BIG;
+
+        r = route_new(&route);
+        if (r < 0)
+                return r;
+
+        route->protocol = RTPROT_STATIC;
+        route->network = network;
+        route->section = TAKE_PTR(n);
+        route->source = NETWORK_CONFIG_SOURCE_STATIC;
+
+        r = hashmap_ensure_put(&network->routes_by_section, &config_section_hash_ops, route->section, route);
+        if (r < 0)
+                return r;
 
-        return route->nexthop_id > 0 || !ordered_set_isempty(route->multipath_routes);
+        *ret = TAKE_PTR(route);
+        return 0;
 }
 
 static int route_add(Manager *manager, Link *link, Route *route) {
@@ -318,6 +317,25 @@ int route_get(Manager *manager, Link *link, const Route *in, Route **ret) {
         return 0;
 }
 
+static int route_get_link(Manager *manager, const Route *route, Link **ret) {
+        int r;
+
+        assert(manager);
+        assert(route);
+
+        if (route->nexthop_id != 0) {
+                NextHop *nh;
+
+                r = nexthop_get_by_id(manager, route->nexthop_id, &nh);
+                if (r < 0)
+                        return r;
+
+                return link_get_by_index(manager, nh->ifindex, ret);
+        }
+
+        return route_nexthop_get_link(manager, NULL, &route->nexthop, ret);
+}
+
 int route_dup(const Route *src, Route **ret) {
         _cleanup_(route_freep) Route *dest = NULL;
         int r;
@@ -332,15 +350,16 @@ int route_dup(const Route *src, Route **ret) {
                 return -ENOMEM;
 
         /* Unset all pointers */
+        dest->manager = NULL;
         dest->network = NULL;
+        dest->wireguard = NULL;
         dest->section = NULL;
         dest->link = NULL;
-        dest->manager = NULL;
-        dest->multipath_routes = NULL;
+        dest->nexthops = 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;
 
@@ -353,43 +372,23 @@ static void route_apply_nexthop(Route *route, const NextHop *nh, uint8_t nh_weig
         assert(nh);
         assert(hashmap_isempty(nh->group));
 
-        route->gw_family = nh->family;
-        route->gw = nh->gw;
+        route->nexthop.family = nh->family;
+        route->nexthop.gw = nh->gw;
 
         if (nh_weight != UINT8_MAX)
-                route->gw_weight = nh_weight;
+                route->nexthop.weight = nh_weight;
 
         if (nh->blackhole)
                 route->type = RTN_BLACKHOLE;
 }
 
-static void route_apply_multipath_route(Route *route, const MultipathRoute *m) {
+static void route_apply_route_nexthop(Route *route, const RouteNextHop *nh) {
         assert(route);
-        assert(m);
-
-        route->gw_family = m->gateway.family;
-        route->gw = m->gateway.address;
-        route->gw_weight = m->weight;
-}
-
-static int multipath_route_get_link(Manager *manager, const MultipathRoute *m, Link **ret) {
-        int r;
-
-        assert(manager);
-        assert(m);
-
-        if (m->ifname) {
-                r = link_get_by_name(manager, m->ifname, ret);
-                return r < 0 ? r : 1;
-
-        } else if (m->ifindex > 0) { /* Always ignore ifindex if ifname is set. */
-                r = link_get_by_index(manager, m->ifindex, ret);
-                return r < 0 ? r : 1;
-        }
+        assert(nh);
 
-        if (ret)
-                *ret = NULL;
-        return 0;
+        route->nexthop.family = nh->family;
+        route->nexthop.gw = nh->gw;
+        route->nexthop.weight = nh->weight;
 }
 
 typedef struct ConvertedRoutes {
@@ -443,7 +442,13 @@ static int converted_routes_new(size_t n, ConvertedRoutes **ret) {
         return 0;
 }
 
-static int route_convert(Manager *manager, const Route *route, ConvertedRoutes **ret) {
+static bool route_needs_convert(const Route *route) {
+        assert(route);
+
+        return route->nexthop_id > 0 || !ordered_set_isempty(route->nexthops);
+}
+
+static int route_convert(Manager *manager, Link *link, const Route *route, ConvertedRoutes **ret) {
         _cleanup_(converted_routes_freep) ConvertedRoutes *c = NULL;
         int r;
 
@@ -451,6 +456,8 @@ static int route_convert(Manager *manager, const Route *route, ConvertedRoutes *
         assert(route);
         assert(ret);
 
+        /* link may be NULL */
+
         if (!route_needs_convert(route)) {
                 *ret = NULL;
                 return 0;
@@ -507,22 +514,22 @@ static int route_convert(Manager *manager, const Route *route, ConvertedRoutes *
 
         }
 
-        assert(!ordered_set_isempty(route->multipath_routes));
+        assert(!ordered_set_isempty(route->nexthops));
 
-        r = converted_routes_new(ordered_set_size(route->multipath_routes), &c);
+        r = converted_routes_new(ordered_set_size(route->nexthops), &c);
         if (r < 0)
                 return r;
 
         size_t i = 0;
-        MultipathRoute *m;
-        ORDERED_SET_FOREACH(m, route->multipath_routes) {
+        RouteNextHop *nh;
+        ORDERED_SET_FOREACH(nh, route->nexthops) {
                 r = route_dup(route, &c->routes[i]);
                 if (r < 0)
                         return r;
 
-                route_apply_multipath_route(c->routes[i], m);
+                route_apply_route_nexthop(c->routes[i], nh);
 
-                r = multipath_route_get_link(manager, m, &c->links[i]);
+                r = route_nexthop_get_link(manager, link, nh, &c->links[i]);
                 if (r < 0)
                         return r;
 
@@ -546,20 +553,21 @@ void link_mark_routes(Link *link, NetworkConfigSource source) {
         }
 }
 
-static void log_route_debug(const Route *route, const char *str, const Link *link, const Manager *manager) {
-        _cleanup_free_ char *state = NULL, *gw_alloc = NULL, *prefsrc = NULL,
+static void log_route_debug(const Route *route, const char *str, Manager *manager) {
+        _cleanup_free_ char *state = NULL, *nexthop = NULL, *prefsrc = NULL,
                 *table = NULL, *scope = NULL, *proto = NULL, *flags = NULL;
-        const char *gw = NULL, *dst, *src;
+        const char *dst, *src;
+        Link *link = NULL;
 
         assert(route);
         assert(str);
         assert(manager);
 
-        /* link may be NULL. */
-
         if (!DEBUG_LOGGING)
                 return;
 
+        (void) route_get_link(manager, route, &link);
+
         (void) network_config_state_to_string_alloc(route->state, &state);
 
         dst = in_addr_is_set(route->family, &route->dst) || route->dst_prefixlen > 0 ?
@@ -567,32 +575,8 @@ static void log_route_debug(const Route *route, const char *str, const Link *lin
         src = in_addr_is_set(route->family, &route->src) || route->src_prefixlen > 0 ?
                 IN_ADDR_PREFIX_TO_STRING(route->family, &route->src, route->src_prefixlen) : NULL;
 
-        if (in_addr_is_set(route->gw_family, &route->gw)) {
-                (void) in_addr_to_string(route->gw_family, &route->gw, &gw_alloc);
-                gw = gw_alloc;
-        } else if (route->gateway_from_dhcp_or_ra) {
-                if (route->gw_family == AF_INET)
-                        gw = "_dhcp4";
-                else if (route->gw_family == AF_INET6)
-                        gw = "_ipv6ra";
-        } else {
-                MultipathRoute *m;
-
-                ORDERED_SET_FOREACH(m, route->multipath_routes) {
-                        _cleanup_free_ char *buf = NULL;
-                        union in_addr_union a = m->gateway.address;
-
-                        (void) in_addr_to_string(m->gateway.family, &a, &buf);
-                        (void) strextend_with_separator(&gw_alloc, ",", strna(buf));
-                        if (m->ifname)
-                                (void) strextend(&gw_alloc, "@", m->ifname);
-                        else if (m->ifindex > 0)
-                                (void) strextendf(&gw_alloc, "@%i", m->ifindex);
-                        /* See comments in config_parse_multipath_route(). */
-                        (void) strextendf(&gw_alloc, ":%"PRIu32, m->weight + 1);
-                }
-                gw = gw_alloc;
-        }
+        (void) route_nexthops_to_string(route, &nexthop);
+
         if (in_addr_is_set(route->family, &route->prefsrc))
                 (void) in_addr_to_string(route->family, &route->prefsrc, &prefsrc);
         (void) route_scope_to_string_alloc(route->scope, &scope);
@@ -601,110 +585,97 @@ static void log_route_debug(const Route *route, const char *str, const Link *lin
         (void) route_flags_to_string_alloc(route->flags, &flags);
 
         log_link_debug(link,
-                       "%s %s route (%s): dst: %s, src: %s, gw: %s, prefsrc: %s, scope: %s, table: %s, "
-                       "proto: %s, type: %s, nexthop: %"PRIu32", priority: %"PRIu32", flags: %s",
+                       "%s %s route (%s): dst: %s, src: %s, %s, prefsrc: %s, "
+                       "table: %s, priority: %"PRIu32", "
+                       "proto: %s, scope: %s, type: %s, flags: %s",
                        str, strna(network_config_source_to_string(route->source)), strna(state),
-                       strna(dst), strna(src), strna(gw), strna(prefsrc),
-                       strna(scope), strna(table), strna(proto),
-                       strna(route_type_to_string(route->type)),
-                       route->nexthop_id, route->priority, strna(flags));
+                       strna(dst), strna(src), strna(nexthop), strna(prefsrc),
+                       strna(table), route->priority,
+                       strna(proto), strna(scope), strna(route_type_to_string(route->type)), strna(flags));
 }
 
-static int route_set_netlink_message(const Route *route, sd_netlink_message *req, Link *link) {
+static int route_set_netlink_message(const Route *route, sd_netlink_message *m, Link *link) {
         int r;
 
         assert(route);
-        assert(req);
+        assert(m);
 
         /* link may be NULL */
 
-        if (in_addr_is_set(route->gw_family, &route->gw) && route->nexthop_id == 0) {
-                if (route->gw_family == route->family) {
-                        r = netlink_message_append_in_addr_union(req, RTA_GATEWAY, route->gw_family, &route->gw);
-                        if (r < 0)
-                                return r;
-                } else {
-                        RouteVia rtvia = {
-                                .family = route->gw_family,
-                                .address = route->gw,
-                        };
-
-                        r = sd_netlink_message_append_data(req, RTA_VIA, &rtvia, sizeof(rtvia));
-                        if (r < 0)
-                                return r;
-                }
-        }
-
+        /* rtmsg header (and relevant attributes) */
         if (route->dst_prefixlen > 0) {
-                r = netlink_message_append_in_addr_union(req, RTA_DST, route->family, &route->dst);
+                r = netlink_message_append_in_addr_union(m, RTA_DST, route->family, &route->dst);
                 if (r < 0)
                         return r;
 
-                r = sd_rtnl_message_route_set_dst_prefixlen(req, route->dst_prefixlen);
+                r = sd_rtnl_message_route_set_dst_prefixlen(m, route->dst_prefixlen);
                 if (r < 0)
                         return r;
         }
 
         if (route->src_prefixlen > 0) {
-                r = netlink_message_append_in_addr_union(req, RTA_SRC, route->family, &route->src);
+                r = netlink_message_append_in_addr_union(m, RTA_SRC, route->family, &route->src);
                 if (r < 0)
                         return r;
 
-                r = sd_rtnl_message_route_set_src_prefixlen(req, route->src_prefixlen);
+                r = sd_rtnl_message_route_set_src_prefixlen(m, route->src_prefixlen);
                 if (r < 0)
                         return r;
         }
 
-        if (in_addr_is_set(route->family, &route->prefsrc)) {
-                r = netlink_message_append_in_addr_union(req, RTA_PREFSRC, route->family, &route->prefsrc);
-                if (r < 0)
-                        return r;
-        }
+        r = sd_rtnl_message_route_set_tos(m, route->tos);
+        if (r < 0)
+                return r;
 
-        r = sd_rtnl_message_route_set_scope(req, route->scope);
+        r = sd_rtnl_message_route_set_scope(m, route->scope);
         if (r < 0)
                 return r;
 
-        r = sd_rtnl_message_route_set_flags(req, route->flags & RTNH_F_ONLINK);
+        r = sd_rtnl_message_route_set_type(m, route->type);
         if (r < 0)
                 return r;
 
-        if (route->table < 256) {
-                r = sd_rtnl_message_route_set_table(req, route->table);
-                if (r < 0)
-                        return r;
-        } else {
-                r = sd_rtnl_message_route_set_table(req, RT_TABLE_UNSPEC);
-                if (r < 0)
-                        return r;
+        r = sd_rtnl_message_route_set_flags(m, route->flags & ~RTNH_COMPARE_MASK);
+        if (r < 0)
+                return r;
 
-                /* Table attribute to allow more than 256. */
-                r = sd_netlink_message_append_u32(req, RTA_TABLE, route->table);
+        /* attributes */
+        r = sd_netlink_message_append_u32(m, RTA_PRIORITY, route->priority);
+        if (r < 0)
+                return r;
+
+        if (in_addr_is_set(route->family, &route->prefsrc)) {
+                r = netlink_message_append_in_addr_union(m, RTA_PREFSRC, route->family, &route->prefsrc);
                 if (r < 0)
                         return r;
         }
 
-        if (!route_type_is_reject(route) &&
-            route->nexthop_id == 0 &&
-            ordered_set_isempty(route->multipath_routes)) {
-                assert(link); /* Those routes must be attached to a specific link */
-
-                r = sd_netlink_message_append_u32(req, RTA_OIF, link->ifindex);
+        if (route->table < 256) {
+                r = sd_rtnl_message_route_set_table(m, route->table);
+                if (r < 0)
+                        return r;
+        } else {
+                r = sd_rtnl_message_route_set_table(m, RT_TABLE_UNSPEC);
                 if (r < 0)
                         return r;
-        }
 
-        if (route->nexthop_id > 0) {
-                r = sd_netlink_message_append_u32(req, RTA_NH_ID, route->nexthop_id);
+                /* Table attribute to allow more than 256. */
+                r = sd_netlink_message_append_u32(m, RTA_TABLE, route->table);
                 if (r < 0)
                         return r;
         }
 
-        r = sd_netlink_message_append_u8(req, RTA_PREF, route->pref);
+        r = sd_netlink_message_append_u8(m, RTA_PREF, route->pref);
+        if (r < 0)
+                return r;
+
+        /* nexthops */
+        r = route_nexthops_set_netlink_message(link, route, m);
         if (r < 0)
                 return r;
 
-        r = sd_netlink_message_append_u32(req, RTA_PRIORITY, route->priority);
+        /* metrics */
+        r = route_metric_set_netlink_message(&route->metric, m);
         if (r < 0)
                 return r;
 
@@ -729,7 +700,7 @@ static int route_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *l
 }
 
 int route_remove(Route *route) {
-        _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
+        _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
         unsigned char type;
         Manager *manager;
         Link *link;
@@ -742,13 +713,15 @@ int route_remove(Route *route) {
         link = route->link;
         manager = route->manager ?: link->manager;
 
-        log_route_debug(route, "Removing", link, manager);
+        log_route_debug(route, "Removing", manager);
+
+        r = sd_rtnl_message_new_route(manager->rtnl, &m, RTM_DELROUTE, route->family, route->protocol);
+        if (r < 0)
+                return log_link_warning_errno(link, r, "Could not create netlink message: %m");
 
-        r = sd_rtnl_message_new_route(manager->rtnl, &req,
-                                      RTM_DELROUTE, route->family,
-                                      route->protocol);
+        r = route_set_netlink_message(route, m, link);
         if (r < 0)
-                return log_link_error_errno(link, r, "Could not create netlink message: %m");
+                return log_link_warning_errno(link, r, "Could not fill netlink message: %m");
 
         if (route->family == AF_INET && route->nexthop_id > 0 && route->type == RTN_BLACKHOLE)
                 /* When IPv4 route has nexthop id and the nexthop type is blackhole, even though kernel
@@ -761,18 +734,14 @@ int route_remove(Route *route) {
         else
                 type = route->type;
 
-        r = sd_rtnl_message_route_set_type(req, type);
+        r = sd_rtnl_message_route_set_type(m, type);
         if (r < 0)
                 return log_link_error_errno(link, r, "Could not set route type: %m");
 
-        r = route_set_netlink_message(route, req, link);
-        if (r < 0)
-                return log_error_errno(r, "Could not fill netlink message: %m");
-
-        r = netlink_call_async(manager->rtnl, NULL, req, route_remove_handler,
+        r = netlink_call_async(manager->rtnl, NULL, m, route_remove_handler,
                                link ? link_netlink_destroy_callback : NULL, link);
         if (r < 0)
-                return log_link_error_errno(link, r, "Could not send netlink message: %m");
+                return log_link_warning_errno(link, r, "Could not send netlink message: %m");
 
         link_ref(link);
 
@@ -838,7 +807,7 @@ static void manager_mark_routes(Manager *manager, bool foreign, const Link *exce
                         _cleanup_(converted_routes_freep) ConvertedRoutes *converted = NULL;
                         Route *existing;
 
-                        r = route_convert(manager, route, &converted);
+                        r = route_convert(manager, link, route, &converted);
                         if (r < 0)
                                 continue;
                         if (r == 0) {
@@ -937,7 +906,7 @@ int link_drop_foreign_routes(Link *link) {
                 _cleanup_(converted_routes_freep) ConvertedRoutes *converted = NULL;
                 Route *existing;
 
-                r = route_convert(link->manager, route, &converted);
+                r = route_convert(link->manager, link, route, &converted);
                 if (r < 0)
                         continue;
                 if (r == 0) {
@@ -1030,124 +999,51 @@ static int route_expire_handler(sd_event_source *s, uint64_t usec, void *userdat
 }
 
 static int route_setup_timer(Route *route, const struct rta_cacheinfo *cacheinfo) {
-        Manager *manager;
         int r;
 
         assert(route);
-        assert(route->manager || (route->link && route->link->manager));
-
-        manager = route->manager ?: route->link->manager;
-
-        if (route->lifetime_usec == USEC_INFINITY)
-                return 0;
 
         if (cacheinfo && cacheinfo->rta_expires != 0)
-                /* Assume that non-zero rta_expires means kernel will handle the route expiration. */
+                route->expiration_managed_by_kernel = true;
+
+        if (route->lifetime_usec == USEC_INFINITY || /* We do not request expiration for the route. */
+            route->expiration_managed_by_kernel) {   /* We have received nonzero expiration previously. The expiration is managed by the kernel. */
+                route->expire = sd_event_source_disable_unref(route->expire);
                 return 0;
+        }
 
+        Manager *manager = ASSERT_PTR(route->manager ?: ASSERT_PTR(route->link)->manager);
         r = event_reset_time(manager->event, &route->expire, CLOCK_BOOTTIME,
                              route->lifetime_usec, 0, route_expire_handler, route, 0, "route-expiration", true);
         if (r < 0)
-                return r;
+                return log_link_warning_errno(route->link, r, "Failed to configure expiration timer for route, ignoring: %m");
 
+        log_route_debug(route, "Configured expiration timer for", manager);
         return 1;
 }
 
-static int append_nexthop_one(const Link *link, const Route *route, const MultipathRoute *m, struct rtattr **rta, size_t offset) {
-        struct rtnexthop *rtnh;
-        struct rtattr *new_rta;
+int route_configure_handler_internal(sd_netlink *rtnl, sd_netlink_message *m, Link *link, Route *route, const char *error_msg) {
         int r;
 
-        assert(route);
         assert(m);
-        assert(rta);
-        assert(*rta);
-
-        new_rta = realloc(*rta, RTA_ALIGN((*rta)->rta_len) + RTA_SPACE(sizeof(struct rtnexthop)));
-        if (!new_rta)
-                return -ENOMEM;
-        *rta = new_rta;
-
-        rtnh = (struct rtnexthop *)((uint8_t *) *rta + offset);
-        *rtnh = (struct rtnexthop) {
-                .rtnh_len = sizeof(*rtnh),
-                .rtnh_ifindex = m->ifindex > 0 ? m->ifindex : link->ifindex,
-                .rtnh_hops = m->weight,
-        };
-
-        (*rta)->rta_len += sizeof(struct rtnexthop);
-
-        if (route->family == m->gateway.family) {
-                r = rtattr_append_attribute(rta, RTA_GATEWAY, &m->gateway.address, FAMILY_ADDRESS_SIZE(m->gateway.family));
-                if (r < 0)
-                        goto clear;
-                rtnh = (struct rtnexthop *)((uint8_t *) *rta + offset);
-                rtnh->rtnh_len += RTA_SPACE(FAMILY_ADDRESS_SIZE(m->gateway.family));
-        } else {
-                r = rtattr_append_attribute(rta, RTA_VIA, &m->gateway, FAMILY_ADDRESS_SIZE(m->gateway.family) + sizeof(m->gateway.family));
-                if (r < 0)
-                        goto clear;
-                rtnh = (struct rtnexthop *)((uint8_t *) *rta + offset);
-                rtnh->rtnh_len += RTA_SPACE(FAMILY_ADDRESS_SIZE(m->gateway.family) + sizeof(m->gateway.family));
-        }
-
-        return 0;
-
-clear:
-        (*rta)->rta_len -= sizeof(struct rtnexthop);
-        return r;
-}
-
-static int append_nexthops(const Link *link, const Route *route, sd_netlink_message *req) {
-        _cleanup_free_ struct rtattr *rta = NULL;
-        struct rtnexthop *rtnh;
-        MultipathRoute *m;
-        size_t offset;
-        int r;
-
         assert(link);
+        assert(link->manager);
         assert(route);
-        assert(req);
-
-        if (ordered_set_isempty(route->multipath_routes))
-                return 0;
-
-        rta = new(struct rtattr, 1);
-        if (!rta)
-                return -ENOMEM;
-
-        *rta = (struct rtattr) {
-                .rta_type = RTA_MULTIPATH,
-                .rta_len = RTA_LENGTH(0),
-        };
-        offset = (uint8_t *) RTA_DATA(rta) - (uint8_t *) rta;
-
-        ORDERED_SET_FOREACH(m, route->multipath_routes) {
-                r = append_nexthop_one(link, route, m, &rta, offset);
-                if (r < 0)
-                        return r;
-
-                rtnh = (struct rtnexthop *)((uint8_t *) rta + offset);
-                offset = (uint8_t *) RTNH_NEXT(rtnh) - (uint8_t *) rta;
-        }
-
-        r = sd_netlink_message_append_data(req, RTA_MULTIPATH, RTA_DATA(rta), RTA_PAYLOAD(rta));
-        if (r < 0)
-                return r;
-
-        return 0;
-}
-
-int route_configure_handler_internal(sd_netlink *rtnl, sd_netlink_message *m, Link *link, const char *error_msg) {
-        int r;
-
-        assert(m);
-        assert(link);
         assert(error_msg);
 
         r = sd_netlink_message_get_errno(m);
-        if (r < 0 && r != -EEXIST) {
-                log_link_message_warning_errno(link, m, r, "Could not set route");
+        if (r == -EEXIST) {
+                Route *existing;
+
+                if (route_get(link->manager, link, route, &existing) >= 0) {
+                        /* When re-configuring an existing route, kernel does not send RTM_NEWROUTE
+                         * notification, so we need to update the timer here. */
+                        existing->lifetime_usec = route->lifetime_usec;
+                        (void) route_setup_timer(existing, NULL);
+                }
+
+        } else if (r < 0) {
+                log_link_message_warning_errno(link, m, r, error_msg);
                 link_enter_failed(link);
                 return 0;
         }
@@ -1160,23 +1056,16 @@ static int route_configure(const Route *route, uint32_t lifetime_sec, Link *link
         int r;
 
         assert(route);
-        assert(IN_SET(route->family, AF_INET, AF_INET6));
         assert(link);
         assert(link->manager);
-        assert(link->manager->rtnl);
-        assert(link->ifindex > 0);
         assert(req);
 
-        log_route_debug(route, "Configuring", link, link->manager);
+        log_route_debug(route, "Configuring", link->manager);
 
         r = sd_rtnl_message_new_route(link->manager->rtnl, &m, RTM_NEWROUTE, route->family, route->protocol);
         if (r < 0)
                 return r;
 
-        r = sd_rtnl_message_route_set_type(m, route->type);
-        if (r < 0)
-                return r;
-
         r = route_set_netlink_message(route, m, link);
         if (r < 0)
                 return r;
@@ -1187,77 +1076,6 @@ 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);
-        if (r < 0)
-                return r;
-
-        if (!ordered_set_isempty(route->multipath_routes)) {
-                assert(route->nexthop_id == 0);
-                assert(!in_addr_is_set(route->gw_family, &route->gw));
-
-                r = append_nexthops(link, route, m);
-                if (r < 0)
-                        return r;
-        }
-
         return request_call_netlink_async(link->manager->rtnl, m, req);
 }
 
@@ -1267,63 +1085,19 @@ static int route_is_ready_to_configure(const Route *route, Link *link) {
         assert(route);
         assert(link);
 
-        if (!link_is_ready_to_configure(link, false))
+        if (!link_is_ready_to_configure(link, /* allow_unmanaged = */ false))
                 return false;
 
         if (set_size(link->routes) >= routes_max())
                 return false;
 
-        if (route->nexthop_id > 0) {
-                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;
-
-                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;
-                }
-        }
-
         if (in_addr_is_set(route->family, &route->prefsrc) > 0) {
                 r = manager_has_address(link->manager, route->family, &route->prefsrc);
                 if (r <= 0)
                         return r;
         }
 
-        if (!gateway_is_ready(link, FLAGS_SET(route->flags, RTNH_F_ONLINK), route->gw_family, &route->gw))
-                return false;
-
-        MultipathRoute *m;
-        ORDERED_SET_FOREACH(m, route->multipath_routes) {
-                union in_addr_union a = m->gateway.address;
-                Link *l = NULL;
-
-                r = multipath_route_get_link(link->manager, m, &l);
-                if (r < 0)
-                        return false;
-                if (r > 0) {
-                        if (!link_is_ready_to_configure(l, /* allow_unmanaged = */ true) ||
-                            !link_has_carrier(l))
-                                return false;
-
-                        m->ifindex = l->ifindex;
-                }
-
-                if (!gateway_is_ready(l ?: link, FLAGS_SET(route->flags, RTNH_F_ONLINK), m->gateway.family, &a))
-                        return false;
-        }
-
-        return true;
+        return route_nexthops_is_ready_to_configure(route, link);
 }
 
 static int route_process_request(Request *req, Link *link, Route *route) {
@@ -1342,7 +1116,7 @@ static int route_process_request(Request *req, Link *link, Route *route) {
                 return 0;
 
         if (route_needs_convert(route)) {
-                r = route_convert(link->manager, route, &converted);
+                r = route_convert(link->manager, link, route, &converted);
                 if (r < 0)
                         return log_link_warning_errno(link, r, "Failed to convert route: %m");
 
@@ -1457,19 +1231,9 @@ int link_request_route(
                 existing->lifetime_usec = route->lifetime_usec;
                 if (consume_object)
                         route_free(route);
-
-                if (existing->expire) {
-                        /* When re-configuring an existing route, kernel does not send RTM_NEWROUTE
-                         * message, so we need to update the timer here. */
-                        r = route_setup_timer(existing, NULL);
-                        if (r < 0)
-                                log_link_warning_errno(link, r, "Failed to update expiration timer for route, ignoring: %m");
-                        if (r > 0)
-                                log_route_debug(existing, "Updated expiration timer for", link, link->manager);
-                }
         }
 
-        log_route_debug(existing, "Requesting", link, link->manager);
+        log_route_debug(existing, "Requesting", link->manager);
         r = link_queue_request_safe(link, REQUEST_TYPE_ROUTE,
                                     existing, NULL,
                                     route_hash_func,
@@ -1488,7 +1252,7 @@ static int static_route_handler(sd_netlink *rtnl, sd_netlink_message *m, Request
 
         assert(link);
 
-        r = route_configure_handler_internal(rtnl, m, link, "Could not set route");
+        r = route_configure_handler_internal(rtnl, m, link, route, "Could not set route");
         if (r <= 0)
                 return r;
 
@@ -1510,7 +1274,7 @@ static int link_request_static_route(Link *link, Route *route) {
                 return link_request_route(link, route, false, &link->static_route_messages,
                                           static_route_handler, NULL);
 
-        log_route_debug(route, "Requesting", link, link->manager);
+        log_route_debug(route, "Requesting", link->manager);
         return link_queue_request_safe(link, REQUEST_TYPE_ROUTE,
                                        route, NULL, route_hash_func, route_compare_func,
                                        route_process_request,
@@ -1581,26 +1345,29 @@ int link_request_static_routes(Link *link, bool only_ipv4) {
 }
 
 void route_cancel_request(Route *route, Link *link) {
-        Request req;
+        Request *req;
 
         assert(route);
 
         link = route->link ?: link;
 
         assert(link);
+        assert(link->manager);
 
         if (!route_is_requesting(route))
                 return;
 
-        req = (Request) {
-                .link = link,
-                .type = REQUEST_TYPE_ROUTE,
-                .userdata = route,
-                .hash_func = (hash_func_t) route_hash_func,
-                .compare_func = (compare_func_t) route_compare_func,
-        };
-
-        request_detach(link->manager, &req);
+        req = ordered_set_get(link->manager->request_queue,
+                              &(Request) {
+                                      .link = link,
+                                      .type = REQUEST_TYPE_ROUTE,
+                                      .userdata = route,
+                                      .hash_func = (hash_func_t) route_hash_func,
+                                      .compare_func = (compare_func_t) route_compare_func,
+                              });
+
+        if (req)
+                request_detach(req);
         route_cancel_requesting(route);
 }
 
@@ -1613,7 +1380,7 @@ static int process_route_one(
 
         _cleanup_(route_freep) Route *tmp = in;
         Route *route = NULL;
-        bool update_dhcp4;
+        bool is_new = false, update_dhcp4;
         int r;
 
         assert(manager);
@@ -1628,32 +1395,31 @@ static int process_route_one(
 
         switch (type) {
         case RTM_NEWROUTE:
-                if (route) {
-                        route->flags = tmp->flags;
-                        route_enter_configured(route);
-                        log_route_debug(route, "Received remembered", link, manager);
+                if (!route) {
+                        if (!manager->manage_foreign_routes) {
+                                route_enter_configured(tmp);
+                                log_route_debug(tmp, "Ignoring received", manager);
+                                return 0;
+                        }
 
-                        r = route_setup_timer(route, cacheinfo);
-                        if (r < 0)
-                                log_link_warning_errno(link, r, "Failed to configure expiration timer for route, ignoring: %m");
-                        if (r > 0)
-                                log_route_debug(route, "Configured expiration timer for", link, manager);
-
-                } else if (!manager->manage_foreign_routes) {
-                        route_enter_configured(tmp);
-                        log_route_debug(tmp, "Ignoring received", link, manager);
-
-                } else {
-                        /* A route appeared that we did not request */
-                        route_enter_configured(tmp);
-                        log_route_debug(tmp, "Received new", link, manager);
+                        /* If we do not know the route, then save it. */
                         r = route_add(manager, link, tmp);
                         if (r < 0) {
                                 log_link_warning_errno(link, r, "Failed to remember foreign route, ignoring: %m");
                                 return 0;
                         }
-                        TAKE_PTR(tmp);
-                }
+
+                        route = TAKE_PTR(tmp);
+                        is_new = true;
+
+                } else
+                        /* Update remembered route with the received notification. */
+                        route->flags = tmp->flags;
+
+                route_enter_configured(route);
+                log_route_debug(route, is_new ? "Received new" : "Received remembered", manager);
+
+                (void) route_setup_timer(route, cacheinfo);
 
                 break;
 
@@ -1661,14 +1427,14 @@ static int process_route_one(
                 if (route) {
                         route_enter_removed(route);
                         if (route->state == 0) {
-                                log_route_debug(route, "Forgetting", link, manager);
+                                log_route_debug(route, "Forgetting", manager);
                                 route_free(route);
                         } else
-                                log_route_debug(route, "Removed", link, manager);
+                                log_route_debug(route, "Removed", manager);
                 } else
                         log_route_debug(tmp,
                                         manager->manage_foreign_routes ? "Kernel removed unknown" : "Ignoring received",
-                                        link, manager);
+                                        manager);
 
                 break;
 
@@ -1688,15 +1454,7 @@ static int process_route_one(
 }
 
 int manager_rtnl_process_route(sd_netlink *rtnl, sd_netlink_message *message, Manager *m) {
-        _cleanup_(converted_routes_freep) ConvertedRoutes *converted = NULL;
         _cleanup_(route_freep) Route *tmp = NULL;
-        _cleanup_free_ void *rta_multipath = NULL;
-        struct rta_cacheinfo cacheinfo;
-        bool has_cacheinfo;
-        Link *link = NULL;
-        uint32_t ifindex;
-        uint16_t type;
-        size_t rta_len;
         int r;
 
         assert(rtnl);
@@ -1711,6 +1469,7 @@ int manager_rtnl_process_route(sd_netlink *rtnl, sd_netlink_message *message, Ma
                 return 0;
         }
 
+        uint16_t type;
         r = sd_netlink_message_get_type(message, &type);
         if (r < 0) {
                 log_warning_errno(r, "rtnl: could not get message type, ignoring: %m");
@@ -1720,115 +1479,84 @@ int manager_rtnl_process_route(sd_netlink *rtnl, sd_netlink_message *message, Ma
                 return 0;
         }
 
-        r = sd_netlink_message_read_u32(message, RTA_OIF, &ifindex);
-        if (r < 0 && r != -ENODATA) {
-                log_warning_errno(r, "rtnl: could not get ifindex from route message, ignoring: %m");
-                return 0;
-        } else if (r >= 0) {
-                if (ifindex <= 0) {
-                        log_warning("rtnl: received route message with invalid ifindex %u, ignoring.", ifindex);
-                        return 0;
-                }
-
-                r = link_get_by_index(m, ifindex, &link);
-                if (r < 0) {
-                        /* when enumerating we might be out of sync, but we will
-                         * get the route again, so just ignore it */
-                        if (!m->enumerating)
-                                log_warning("rtnl: received route message for link (%u) we do not know about, ignoring", ifindex);
-                        return 0;
-                }
-        }
-
         r = route_new(&tmp);
         if (r < 0)
                 return log_oom();
 
+        /* rtmsg header */
         r = sd_rtnl_message_route_get_family(message, &tmp->family);
         if (r < 0) {
-                log_link_warning(link, "rtnl: received route message without family, ignoring");
+                log_warning_errno(r, "rtnl: received route message without family, ignoring: %m");
                 return 0;
         } else if (!IN_SET(tmp->family, AF_INET, AF_INET6)) {
-                log_link_debug(link, "rtnl: received route message with invalid family '%i', ignoring", tmp->family);
+                log_debug("rtnl: received route message with invalid family '%i', ignoring.", tmp->family);
                 return 0;
         }
 
-        r = sd_rtnl_message_route_get_protocol(message, &tmp->protocol);
+        r = sd_rtnl_message_route_get_dst_prefixlen(message, &tmp->dst_prefixlen);
         if (r < 0) {
-                log_warning_errno(r, "rtnl: received route message without route protocol, ignoring: %m");
+                log_warning_errno(r, "rtnl: received route message with invalid destination prefixlen, ignoring: %m");
                 return 0;
         }
 
-        r = sd_rtnl_message_route_get_flags(message, &tmp->flags);
+        r = sd_rtnl_message_route_get_src_prefixlen(message, &tmp->src_prefixlen);
         if (r < 0) {
-                log_warning_errno(r, "rtnl: received route message without route flags, ignoring: %m");
+                log_warning_errno(r, "rtnl: received route message with invalid source prefixlen, ignoring: %m");
                 return 0;
         }
 
-        r = netlink_message_read_in_addr_union(message, RTA_DST, tmp->family, &tmp->dst);
-        if (r < 0 && r != -ENODATA) {
-                log_link_warning_errno(link, r, "rtnl: received route message without valid destination, ignoring: %m");
+        r = sd_rtnl_message_route_get_tos(message, &tmp->tos);
+        if (r < 0) {
+                log_warning_errno(r, "rtnl: received route message with invalid tos, ignoring: %m");
                 return 0;
         }
 
-        r = netlink_message_read_in_addr_union(message, RTA_GATEWAY, tmp->family, &tmp->gw);
-        if (r < 0 && r != -ENODATA) {
-                log_link_warning_errno(link, r, "rtnl: received route message without valid gateway, ignoring: %m");
+        r = sd_rtnl_message_route_get_protocol(message, &tmp->protocol);
+        if (r < 0) {
+                log_warning_errno(r, "rtnl: received route message without route protocol, ignoring: %m");
                 return 0;
-        } else if (r >= 0)
-                tmp->gw_family = tmp->family;
-        else if (tmp->family == AF_INET) {
-                RouteVia via;
-
-                r = sd_netlink_message_read(message, RTA_VIA, sizeof(via), &via);
-                if (r < 0 && r != -ENODATA) {
-                        log_link_warning_errno(link, r, "rtnl: received route message without valid gateway, ignoring: %m");
-                        return 0;
-                } else if (r >= 0) {
-                        tmp->gw_family = via.family;
-                        tmp->gw = via.address;
-                }
         }
 
-        r = netlink_message_read_in_addr_union(message, RTA_SRC, tmp->family, &tmp->src);
-        if (r < 0 && r != -ENODATA) {
-                log_link_warning_errno(link, r, "rtnl: received route message without valid source, ignoring: %m");
+        r = sd_rtnl_message_route_get_scope(message, &tmp->scope);
+        if (r < 0) {
+                log_warning_errno(r, "rtnl: received route message with invalid scope, ignoring: %m");
                 return 0;
         }
 
-        r = netlink_message_read_in_addr_union(message, RTA_PREFSRC, tmp->family, &tmp->prefsrc);
-        if (r < 0 && r != -ENODATA) {
-                log_link_warning_errno(link, r, "rtnl: received route message without valid preferred source, ignoring: %m");
+        r = sd_rtnl_message_route_get_type(message, &tmp->type);
+        if (r < 0) {
+                log_warning_errno(r, "rtnl: received route message with invalid type, ignoring: %m");
                 return 0;
         }
 
-        r = sd_rtnl_message_route_get_dst_prefixlen(message, &tmp->dst_prefixlen);
+        r = sd_rtnl_message_route_get_flags(message, &tmp->flags);
         if (r < 0) {
-                log_link_warning_errno(link, r, "rtnl: received route message with invalid destination prefixlen, ignoring: %m");
+                log_warning_errno(r, "rtnl: received route message without route flags, ignoring: %m");
                 return 0;
         }
 
-        r = sd_rtnl_message_route_get_src_prefixlen(message, &tmp->src_prefixlen);
-        if (r < 0) {
-                log_link_warning_errno(link, r, "rtnl: received route message with invalid source prefixlen, ignoring: %m");
+        /* attributes */
+        r = netlink_message_read_in_addr_union(message, RTA_DST, tmp->family, &tmp->dst);
+        if (r < 0 && r != -ENODATA) {
+                log_warning_errno(r, "rtnl: received route message without valid destination, ignoring: %m");
                 return 0;
         }
 
-        r = sd_rtnl_message_route_get_scope(message, &tmp->scope);
-        if (r < 0) {
-                log_link_warning_errno(link, r, "rtnl: received route message with invalid scope, ignoring: %m");
+        r = netlink_message_read_in_addr_union(message, RTA_SRC, tmp->family, &tmp->src);
+        if (r < 0 && r != -ENODATA) {
+                log_warning_errno(r, "rtnl: received route message without valid source, ignoring: %m");
                 return 0;
         }
 
-        r = sd_rtnl_message_route_get_tos(message, &tmp->tos);
-        if (r < 0) {
-                log_link_warning_errno(link, r, "rtnl: received route message with invalid tos, ignoring: %m");
+        r = sd_netlink_message_read_u32(message, RTA_PRIORITY, &tmp->priority);
+        if (r < 0 && r != -ENODATA) {
+                log_warning_errno(r, "rtnl: received route message with invalid priority, ignoring: %m");
                 return 0;
         }
 
-        r = sd_rtnl_message_route_get_type(message, &tmp->type);
-        if (r < 0) {
-                log_link_warning_errno(link, r, "rtnl: received route message with invalid type, ignoring: %m");
+        r = netlink_message_read_in_addr_union(message, RTA_PREFSRC, tmp->family, &tmp->prefsrc);
+        if (r < 0 && r != -ENODATA) {
+                log_warning_errno(r, "rtnl: received route message without valid preferred source, ignoring: %m");
                 return 0;
         }
 
@@ -1841,82 +1569,50 @@ int manager_rtnl_process_route(sd_netlink *rtnl, sd_netlink_message *message, Ma
                         tmp->table = table;
         }
         if (r < 0) {
-                log_link_warning_errno(link, r, "rtnl: received route message with invalid table, ignoring: %m");
+                log_warning_errno(r, "rtnl: received route message with invalid table, ignoring: %m");
                 return 0;
         }
 
-        r = sd_netlink_message_read_u32(message, RTA_PRIORITY, &tmp->priority);
+        r = sd_netlink_message_read_u8(message, RTA_PREF, &tmp->pref);
         if (r < 0 && r != -ENODATA) {
-                log_link_warning_errno(link, r, "rtnl: received route message with invalid priority, ignoring: %m");
+                log_warning_errno(r, "rtnl: received route message with invalid preference, ignoring: %m");
                 return 0;
         }
 
-        r = sd_netlink_message_read_u32(message, RTA_NH_ID, &tmp->nexthop_id);
-        if (r < 0 && r != -ENODATA) {
-                log_link_warning_errno(link, r, "rtnl: received route message with invalid nexthop id, ignoring: %m");
+        /* nexthops */
+        if (route_nexthops_read_netlink_message(tmp, message) < 0)
+                return 0;
+
+        /* metrics */
+        if (route_metric_read_netlink_message(&tmp->metric, message) < 0)
                 return 0;
-        }
 
-        r = sd_netlink_message_enter_container(message, RTA_METRICS);
+        bool has_cacheinfo;
+        struct rta_cacheinfo cacheinfo;
+        r = sd_netlink_message_read(message, RTA_CACHEINFO, sizeof(cacheinfo), &cacheinfo);
         if (r < 0 && r != -ENODATA) {
-                log_link_error_errno(link, r, "rtnl: Could not enter RTA_METRICS container, ignoring: %m");
+                log_warning_errno(r, "rtnl: failed to read RTA_CACHEINFO attribute, ignoring: %m");
                 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;
-                }
+        has_cacheinfo = r >= 0;
 
-                r = sd_netlink_message_exit_container(message);
+        Link *link = NULL;
+        if (tmp->nexthop.ifindex > 0) {
+                r = link_get_by_index(m, tmp->nexthop.ifindex, &link);
                 if (r < 0) {
-                        log_link_error_errno(link, r, "rtnl: Could not exit from RTA_METRICS container, ignoring: %m");
+                        /* when enumerating we might be out of sync, but we will
+                         * get the route again, so just ignore it */
+                        if (!m->enumerating)
+                                log_warning("rtnl: received route message for link (%i) we do not know about, ignoring", tmp->nexthop.ifindex);
                         return 0;
                 }
         }
 
-        r = sd_netlink_message_read_data(message, RTA_MULTIPATH, &rta_len, &rta_multipath);
-        if (r < 0 && r != -ENODATA) {
-                log_link_warning_errno(link, r, "rtnl: failed to read RTA_MULTIPATH attribute, ignoring: %m");
-                return 0;
-        } else if (r >= 0) {
-                r = rtattr_read_nexthop(rta_multipath, rta_len, tmp->family, &tmp->multipath_routes);
-                if (r < 0) {
-                        log_link_warning_errno(link, r, "rtnl: failed to parse RTA_MULTIPATH attribute, ignoring: %m");
-                        return 0;
-                }
-        }
-
-        r = sd_netlink_message_read(message, RTA_CACHEINFO, sizeof(cacheinfo), &cacheinfo);
-        if (r < 0 && r != -ENODATA) {
-                log_link_warning_errno(link, r, "rtnl: failed to read RTA_CACHEINFO attribute, ignoring: %m");
-                return 0;
-        }
-        has_cacheinfo = r >= 0;
-
-        /* IPv6 routes with reject type are always assigned to the loopback interface. See kernel's
-         * fib6_nh_init() in net/ipv6/route.c. However, we'd like to manage them by Manager. Hence, set
-         * link to NULL here. */
-        if (route_type_is_reject(tmp))
-                link = NULL;
-
         if (!route_needs_convert(tmp))
                 return process_route_one(m, link, type, TAKE_PTR(tmp), has_cacheinfo ? &cacheinfo : NULL);
 
-        r = route_convert(m, tmp, &converted);
+        _cleanup_(converted_routes_freep) ConvertedRoutes *converted = NULL;
+        r = route_convert(m, link, tmp, &converted);
         if (r < 0) {
                 log_link_warning_errno(link, r, "rtnl: failed to convert received route, ignoring: %m");
                 return 0;
@@ -1936,7 +1632,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 +1646,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,99 +1681,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;
-
-        TAKE_PTR(n);
-        return 0;
-}
-
-int config_parse_gateway(
-                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);
-
-        if (streq(section, "Network")) {
-                /* we are not in an Route section, so use line number instead */
-                r = route_new_static(network, filename, 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;
-                }
-        } else {
-                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->gateway_from_dhcp_or_ra = false;
-                        n->gw_family = AF_UNSPEC;
-                        n->gw = IN_ADDR_NULL;
-                        TAKE_PTR(n);
-                        return 0;
-                }
-
-                if (streq(rvalue, "_dhcp")) {
-                        n->gateway_from_dhcp_or_ra = true;
-                        TAKE_PTR(n);
-                        return 0;
-                }
-
-                if (streq(rvalue, "_dhcp4")) {
-                        n->gw_family = AF_INET;
-                        n->gateway_from_dhcp_or_ra = true;
-                        TAKE_PTR(n);
-                        return 0;
-                }
-
-                if (streq(rvalue, "_ipv6ra")) {
-                        n->gw_family = AF_INET6;
-                        n->gateway_from_dhcp_or_ra = true;
-                        TAKE_PTR(n);
-                        return 0;
-                }
-        }
-
-        r = in_addr_from_string_auto(rvalue, &n->gw_family, &n->gw);
-        if (r < 0) {
-                log_syntax(unit, LOG_WARNING, filename, line, r,
-                           "Invalid %s='%s', ignoring assignment: %m", lvalue, rvalue);
-                return 0;
-        }
+        route->family = AF_INET;
+        route->scope = RT_SCOPE_LINK;
+        route->scope_set = true;
+        route->protocol = RTPROT_STATIC;
 
-        n->gateway_from_dhcp_or_ra = false;
-        TAKE_PTR(n);
+        TAKE_PTR(route);
         return 0;
 }
 
@@ -2094,7 +1707,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 +1716,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 +1725,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 +1752,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 +1763,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 +1773,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 +1810,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 +1819,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 +1828,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 +1853,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 +1862,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 +1877,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 +1895,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 +1905,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,66 +1914,15 @@ 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);
-        return 0;
-}
-
-int config_parse_route_boolean(
-                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 = parse_boolean(rvalue);
-        if (r < 0) {
-                log_syntax(unit, LOG_WARNING, filename, line, r,
-                           "Could not parse %s=\"%s\", ignoring assignment: %m", lvalue, rvalue);
-                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();
-
-        TAKE_PTR(n);
+        route->table_set = true;
+        TAKE_PTR(route);
         return 0;
 }
 
@@ -2430,10 +1939,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 +1952,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 +1980,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 +1999,9 @@ int config_parse_route_protocol(
                 return 0;
         }
 
-        n->protocol = r;
+        route->protocol = r;
 
-        TAKE_PTR(n);
+        TAKE_PTR(route);
         return 0;
 }
 
@@ -2509,10 +2018,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,602 +2037,62 @@ 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;
+        route->type = (unsigned char) t;
 
-        TAKE_PTR(n);
+        TAKE_PTR(route);
         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 route_section_verify(Route *route) {
         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;
-        }
-
-        TAKE_PTR(m);
-        TAKE_PTR(n);
-        return 0;
-}
+        assert(route);
+        assert(route->section);
 
-static int route_section_verify(Route *route, Network *network) {
         if (section_is_invalid(route->section))
                 return -EINVAL;
 
         /* Currently, we do not support static route with finite lifetime. */
         assert(route->lifetime_usec == USEC_INFINITY);
 
-        if (route->gateway_from_dhcp_or_ra) {
-                if (route->gw_family == AF_UNSPEC) {
-                        /* When deprecated Gateway=_dhcp is set, then assume gateway family based on other settings. */
-                        switch (route->family) {
-                        case AF_UNSPEC:
-                                log_warning("%s: Deprecated value \"_dhcp\" is specified for Gateway= in [Route] section from line %u. "
-                                            "Please use \"_dhcp4\" or \"_ipv6ra\" instead. Assuming \"_dhcp4\".",
-                                            route->section->filename, route->section->line);
-                                route->family = AF_INET;
-                                break;
-                        case AF_INET:
-                        case AF_INET6:
-                                log_warning("%s: Deprecated value \"_dhcp\" is specified for Gateway= in [Route] section from line %u. "
-                                            "Assuming \"%s\" based on Destination=, Source=, or PreferredSource= setting.",
-                                            route->section->filename, route->section->line, route->family == AF_INET ? "_dhcp4" : "_ipv6ra");
-                                break;
-                        default:
-                                return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
-                                                         "%s: Invalid route family. Ignoring [Route] section from line %u.",
-                                                         route->section->filename, route->section->line);
-                        }
-                        route->gw_family = route->family;
-                }
-
-                if (route->gw_family == AF_INET && !FLAGS_SET(network->dhcp, ADDRESS_FAMILY_IPV4))
-                        return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
-                                                 "%s: Gateway=\"_dhcp4\" is specified but DHCPv4 client is disabled. "
-                                                 "Ignoring [Route] section from line %u.",
-                                                 route->section->filename, route->section->line);
-
-                if (route->gw_family == AF_INET6 && !network->ipv6_accept_ra)
-                        return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
-                                                 "%s: Gateway=\"_ipv6ra\" is specified but IPv6AcceptRA= is disabled. "
-                                                 "Ignoring [Route] section from line %u.",
-                                                 route->section->filename, route->section->line);
-        }
-
-        /* When only Gateway= is specified, assume the route family based on the Gateway address. */
-        if (route->family == AF_UNSPEC)
-                route->family = route->gw_family;
-
-        if (route->family == AF_UNSPEC) {
-                assert(route->section);
-
-                return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
-                                         "%s: Route section without Gateway=, Destination=, Source=, "
-                                         "or PreferredSource= field configured. "
-                                         "Ignoring [Route] section from line %u.",
-                                         route->section->filename, route->section->line);
-        }
-
-        if (route->family == AF_INET6 && route->gw_family == AF_INET)
-                return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
-                                         "%s: IPv4 gateway is configured for IPv6 route. "
-                                         "Ignoring [Route] section from line %u.",
-                                         route->section->filename, route->section->line);
+        r = route_section_verify_nexthops(route);
+        if (r < 0)
+                return r;
 
-        if (!route->table_set && network->vrf) {
-                route->table = VRF(network->vrf)->table;
+        /* table */
+        if (!route->table_set && route->network && route->network->vrf) {
+                route->table = VRF(route->network->vrf)->table;
                 route->table_set = true;
         }
 
         if (!route->table_set && IN_SET(route->type, RTN_LOCAL, RTN_BROADCAST, RTN_ANYCAST, RTN_NAT))
                 route->table = RT_TABLE_LOCAL;
 
-        if (!route->scope_set && route->family != AF_INET6) {
+        /* scope */
+        if (!route->scope_set && route->family == AF_INET) {
                 if (IN_SET(route->type, RTN_LOCAL, RTN_NAT))
                         route->scope = RT_SCOPE_HOST;
                 else if (IN_SET(route->type, RTN_BROADCAST, RTN_ANYCAST, RTN_MULTICAST))
                         route->scope = RT_SCOPE_LINK;
                 else if (IN_SET(route->type, RTN_UNICAST, RTN_UNSPEC) &&
                          !route->gateway_from_dhcp_or_ra &&
-                         !in_addr_is_set(route->gw_family, &route->gw) &&
-                         ordered_set_isempty(route->multipath_routes) &&
+                         !in_addr_is_set(route->nexthop.family, &route->nexthop.gw) &&
+                         ordered_set_isempty(route->nexthops) &&
                          route->nexthop_id == 0)
                         route->scope = RT_SCOPE_LINK;
         }
 
-        if (route->scope != RT_SCOPE_UNIVERSE && route->family == AF_INET6) {
-                log_warning("%s: Scope= is specified for IPv6 route. It will be ignored.", route->section->filename);
-                route->scope = RT_SCOPE_UNIVERSE;
-        }
-
-        if (route->family == AF_INET6 && route->priority == 0)
-                route->priority = IP6_RT_PRIO_USER;
+        /* IPv6 route */
+        if (route->family == AF_INET6) {
+                if (route->scope != RT_SCOPE_UNIVERSE) {
+                        log_warning("%s: Scope= is specified for IPv6 route. It will be ignored.", route->section->filename);
+                        route->scope = RT_SCOPE_UNIVERSE;
+                }
 
-        if (route->gateway_onlink < 0 && in_addr_is_set(route->gw_family, &route->gw) &&
-            ordered_hashmap_isempty(network->addresses_by_section)) {
-                /* If no address is configured, in most cases the gateway cannot be reachable.
-                 * TODO: we may need to improve the condition above. */
-                log_warning("%s: Gateway= without static address configured. "
-                            "Enabling GatewayOnLink= option.",
-                            network->filename);
-                route->gateway_onlink = true;
+                if (route->priority == 0)
+                        route->priority = IP6_RT_PRIO_USER;
         }
 
-        if (route->gateway_onlink >= 0)
-                SET_FLAG(route->flags, RTNH_F_ONLINK, route->gateway_onlink);
-
-        if (route->family == AF_INET6) {
-                MultipathRoute *m;
-
-                ORDERED_SET_FOREACH(m, route->multipath_routes)
-                        if (m->gateway.family == AF_INET)
-                                return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
-                                                         "%s: IPv4 multipath route is specified for IPv6 route. "
-                                                         "Ignoring [Route] section from line %u.",
-                                                         route->section->filename, route->section->line);
-        }
-
-        if ((route->gateway_from_dhcp_or_ra ||
-             in_addr_is_set(route->gw_family, &route->gw)) &&
-            !ordered_set_isempty(route->multipath_routes))
-                return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
-                                         "%s: Gateway= cannot be specified with MultiPathRoute=. "
-                                         "Ignoring [Route] section from line %u.",
-                                         route->section->filename, route->section->line);
-
-        if (route->nexthop_id > 0 &&
-            (route->gateway_from_dhcp_or_ra ||
-             in_addr_is_set(route->gw_family, &route->gw) ||
-             !ordered_set_isempty(route->multipath_routes)))
-                return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
-                                         "%s: NextHopId= cannot be specified with Gateway= or MultiPathRoute=. "
-                                         "Ignoring [Route] section from line %u.",
-                                         route->section->filename, route->section->line);
-
         return 0;
 }
 
@@ -3133,6 +2102,6 @@ void network_drop_invalid_routes(Network *network) {
         assert(network);
 
         HASHMAP_FOREACH(route, network->routes_by_section)
-                if (route_section_verify(route, network) < 0)
+                if (route_section_verify(route) < 0)
                         route_free(route);
 }
index 2e7f6290630dcb6fe51a6ec3ebf3e3e40ddbefe3..8d55a6ac00175783683e44b0e030ac2fa537317a 100644 (file)
@@ -9,12 +9,16 @@
 #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;
 typedef struct Network Network;
 typedef struct Request Request;
 typedef struct Route Route;
+typedef struct Wireguard Wireguard;
+
 typedef int (*route_netlink_handler_t)(
                 sd_netlink *rtnl,
                 sd_netlink_message *m,
@@ -26,65 +30,65 @@ struct Route {
         Link *link;
         Manager *manager;
         Network *network;
+        Wireguard *wireguard;
         ConfigSection *section;
         NetworkConfigSource source;
         NetworkConfigState state;
         union in_addr_union provider; /* DHCP server or router address */
 
+        /* rtmsg header */
         int family;
-        int gw_family;
-        uint32_t gw_weight;
-        int quickack;
-        int fast_open_no_cookie;
-
         unsigned char dst_prefixlen;
-        unsigned char src_prefixlen;
-        unsigned char scope;
+        unsigned char src_prefixlen; /* IPv6 only */
+        unsigned char tos; /* IPv4 only */
         unsigned char protocol;  /* RTPROT_* */
-        unsigned char type; /* RTN_* */
-        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;
+        unsigned char scope; /* IPv4 only */
+        unsigned char type; /* RTN_*, e.g. RTN_LOCAL, RTN_UNREACHABLE */
+        unsigned flags; /* e.g. RTNH_F_ONLINK */
+
+        /* attributes */
+        union in_addr_union dst; /* RTA_DST */
+        union in_addr_union src; /* RTA_SRC (IPv6 only) */
+        uint32_t priority; /* RTA_PRIORITY, note that ip(8) calls this 'metric' */
+        union in_addr_union prefsrc; /* RTA_PREFSRC */
+        uint32_t table; /* RTA_TABLE, also used in rtmsg header */
+        uint8_t pref; /* RTA_PREF (IPv6 only) */
+
+        /* nexthops */
+        RouteNextHop nexthop; /* RTA_OIF, and RTA_GATEWAY or RTA_VIA (IPv4 only) */
+        OrderedSet *nexthops; /* RTA_MULTIPATH */
+        uint32_t nexthop_id; /* RTA_NH_ID */
+
+        /* metrics (RTA_METRICS) */
+        RouteMetric metric;
+
+        /* This is an absolute point in time, and NOT a timespan/duration.
+         * Must be specified with clock_boottime_or_monotonic(). */
+        usec_t lifetime_usec; /* RTA_EXPIRES (IPv6 only) */
+        /* Used when kernel does not support RTA_EXPIRES attribute. */
+        sd_event_source *expire;
+        bool expiration_managed_by_kernel:1; /* RTA_CACHEINFO has nonzero rta_expires */
 
+        /* Only used by conf persers and route_section_verify(). */
         bool scope_set:1;
         bool table_set:1;
         bool priority_set:1;
         bool protocol_set:1;
         bool pref_set:1;
         bool gateway_from_dhcp_or_ra:1;
-
-        union in_addr_union gw;
-        union in_addr_union dst;
-        union in_addr_union src;
-        union in_addr_union prefsrc;
-        OrderedSet *multipath_routes;
-
-        /* This is an absolute point in time, and NOT a timespan/duration.
-         * Must be specified with clock_boottime_or_monotonic(). */
-        usec_t lifetime_usec;
-        /* Used when kernel does not support RTA_EXPIRES attribute. */
-        sd_event_source *expire;
+        int gateway_onlink;
 };
 
 extern const struct hash_ops route_hash_ops;
 
-int route_new(Route **ret);
-Route *route_free(Route *route);
+Route* route_free(Route *route);
 DEFINE_SECTION_CLEANUP_FUNCTIONS(Route, route_free);
+
+int route_new(Route **ret);
+int route_new_static(Network *network, const char *filename, unsigned section_line, Route **ret);
 int route_dup(const Route *src, Route **ret);
 
-int route_configure_handler_internal(sd_netlink *rtnl, sd_netlink_message *m, Link *link, const char *error_msg);
+int route_configure_handler_internal(sd_netlink *rtnl, sd_netlink_message *m, Link *link, Route *route, const char *error_msg);
 int route_remove(Route *route);
 int route_remove_and_drop(Route *route);
 
@@ -109,26 +113,16 @@ int manager_rtnl_process_route(sd_netlink *rtnl, sd_netlink_message *message, Ma
 int network_add_ipv4ll_route(Network *network);
 int network_add_default_route_on_device(Network *network);
 void network_drop_invalid_routes(Network *network);
+int route_section_verify(Route *route);
 
 DEFINE_NETWORK_CONFIG_STATE_FUNCTIONS(Route, route);
 void link_mark_routes(Link *link, NetworkConfigSource source);
 
-CONFIG_PARSER_PROTOTYPE(config_parse_gateway);
 CONFIG_PARSER_PROTOTYPE(config_parse_preferred_src);
 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_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 9f0e365c22b6ce1d3dbfa7e52f386b0ce73042e6..b79b898832b3b9dc1edc77163104edb54a4317a2 100644 (file)
@@ -630,11 +630,11 @@ static int link_save(Link *link) {
                 fprintf(f, "REQUIRED_FOR_ONLINE=%s\n",
                         yes_no(link->network->required_for_online));
 
-                LinkOperationalStateRange st = link->network->required_operstate_for_online;
-                fprintf(f, "REQUIRED_OPER_STATE_FOR_ONLINE=%s%s%s\n",
-                        strempty(link_operstate_to_string(st.min)),
-                        st.max != LINK_OPERSTATE_RANGE_DEFAULT.max ? ":" : "",
-                        st.max != LINK_OPERSTATE_RANGE_DEFAULT.max ? strempty(link_operstate_to_string(st.max)) : "");
+                LinkOperationalStateRange st;
+                link_required_operstate_for_online(link, &st);
+
+                fprintf(f, "REQUIRED_OPER_STATE_FOR_ONLINE=%s:%s\n",
+                        link_operstate_to_string(st.min), link_operstate_to_string(st.max));
 
                 fprintf(f, "REQUIRED_FAMILY_FOR_ONLINE=%s\n",
                         link_required_address_family_to_string(link->network->required_family_for_online));
index 98e7a72c1c79922655448b9fe3e2bea0279e7762..ee63c3ec9649558e4f446283b164d4cf5a1b0e58 100644 (file)
@@ -128,7 +128,7 @@ int manager_genl_process_nl80211_config(sd_netlink *genl, sd_netlink_message *me
                 return 0;
         }
 
-        r = sd_netlink_message_read_data_suffix0(message, NL80211_ATTR_SSID, &len, (void**) &ssid);
+        r = sd_netlink_message_read_data(message, NL80211_ATTR_SSID, &len, (void**) &ssid);
         if (r < 0 && r != -ENODATA) {
                 log_link_debug_errno(link, r, "nl80211: received %s(%u) message without valid SSID, ignoring: %m",
                                      strna(nl80211_cmd_to_string(cmd)), cmd);
index 9213795f54a5a7d58b3d29e6b808c0cb2e2f7904..da5eed5d73b9e2299559771b4be1c24e1abdef24 100644 (file)
@@ -52,7 +52,7 @@ static bool manager_ignore_link(Manager *m, Link *link) {
         return false;
 }
 
-static int manager_link_is_online(Manager *m, Link *l, LinkOperationalStateRange s) {
+static int manager_link_is_online(Manager *m, Link *l, const LinkOperationalStateRange *state_range) {
         AddressFamily required_family;
         bool needs_ipv4;
         bool needs_ipv6;
@@ -91,25 +91,23 @@ static int manager_link_is_online(Manager *m, Link *l, LinkOperationalStateRange
                                             "link is being processed by networkd: setup state is %s.",
                                             l->state);
 
-        if (s.min < 0)
-                s.min = m->required_operstate.min >= 0 ? m->required_operstate.min
-                                                       : l->required_operstate.min;
+        const LinkOperationalStateRange *range;
+        FOREACH_POINTER(range, state_range, &m->required_operstate, &l->required_operstate)
+                if (operational_state_range_is_valid(range))
+                        break;
+        assert(range != POINTER_MAX);
 
-        if (s.max < 0)
-                s.max = m->required_operstate.max >= 0 ? m->required_operstate.max
-                                                       : l->required_operstate.max;
-
-        if (l->operational_state < s.min || l->operational_state > s.max)
+        if (!operational_state_is_in_range(l->operational_state, range))
                 return log_link_debug_errno(l, SYNTHETIC_ERRNO(EADDRNOTAVAIL),
                                             "Operational state '%s' is not in range ['%s':'%s']",
                                             link_operstate_to_string(l->operational_state),
-                                            link_operstate_to_string(s.min), link_operstate_to_string(s.max));
+                                            link_operstate_to_string(range->min), link_operstate_to_string(range->max));
 
         required_family = m->required_family > 0 ? m->required_family : l->required_family;
         needs_ipv4 = required_family & ADDRESS_FAMILY_IPV4;
         needs_ipv6 = required_family & ADDRESS_FAMILY_IPV6;
 
-        if (s.min < LINK_OPERSTATE_ROUTABLE) {
+        if (range->min < LINK_OPERSTATE_ROUTABLE) {
                 if (needs_ipv4 && l->ipv4_address_state < LINK_ADDRESS_STATE_DEGRADED)
                         return log_link_debug_errno(l, SYNTHETIC_ERRNO(EADDRNOTAVAIL),
                                                     "No routable or link-local IPv4 address is configured.");
@@ -155,7 +153,7 @@ bool manager_configured(Manager *m) {
                                 continue;
                         }
 
-                        r = manager_link_is_online(m, l, *range);
+                        r = manager_link_is_online(m, l, range);
                         if (r <= 0 && !m->any)
                                 return false;
                         if (r > 0 && m->any)
@@ -175,9 +173,7 @@ bool manager_configured(Manager *m) {
                         continue;
                 }
 
-                r = manager_link_is_online(m, l,
-                                           (LinkOperationalStateRange) { _LINK_OPERSTATE_INVALID,
-                                                                         _LINK_OPERSTATE_INVALID });
+                r = manager_link_is_online(m, l, /* state_range = */ NULL);
                 if (r < 0 && !m->any) /* Unlike the above loop, unmanaged interfaces are ignored here. */
                         return false;
                 if (r > 0) {
index 5328bba2d8c0a53f18354153454d53cb33a1bab7..544c360eddcf980355c45c3e0d392cf21a08bb4d 100644 (file)
@@ -19,7 +19,7 @@ static bool arg_quiet = false;
 static usec_t arg_timeout = 120 * USEC_PER_SEC;
 static Hashmap *arg_interfaces = NULL;
 static char **arg_ignore = NULL;
-static LinkOperationalStateRange arg_required_operstate = { _LINK_OPERSTATE_INVALID, _LINK_OPERSTATE_INVALID };
+static LinkOperationalStateRange arg_required_operstate = LINK_OPERSTATE_RANGE_INVALID;
 static AddressFamily arg_required_family = ADDRESS_FAMILY_NO;
 static bool arg_any = false;
 
@@ -71,12 +71,11 @@ static int parse_interface_with_operstate_range(const char *str) {
         if (p) {
                 r = parse_operational_state_range(p + 1, range);
                 if (r < 0)
-                         log_error_errno(r, "Invalid operational state range '%s'", p + 1);
+                        return log_error_errno(r, "Invalid operational state range: %s", p + 1);
 
                 ifname = strndup(optarg, p - optarg);
         } else {
-                range->min = _LINK_OPERSTATE_INVALID;
-                range->max = _LINK_OPERSTATE_INVALID;
+                *range = LINK_OPERSTATE_RANGE_INVALID;
                 ifname = strdup(str);
         }
         if (!ifname)
@@ -84,18 +83,19 @@ static int parse_interface_with_operstate_range(const char *str) {
 
         if (!ifname_valid(ifname))
                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
-                                       "Invalid interface name '%s'", ifname);
+                                       "Invalid interface name: %s", ifname);
 
-        r = hashmap_ensure_put(&arg_interfaces, &string_hash_ops, ifname, TAKE_PTR(range));
+        r = hashmap_ensure_put(&arg_interfaces, &string_hash_ops, ifname, range);
         if (r == -ENOMEM)
                 return log_oom();
         if (r < 0)
                 return log_error_errno(r, "Failed to store interface name: %m");
         if (r == 0)
-                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
-                                       "Interface name %s is already specified", ifname);
+                return log_error_errno(SYNTHETIC_ERRNO(EEXIST),
+                                       "Interface name %s is already specified.", ifname);
 
         TAKE_PTR(ifname);
+        TAKE_PTR(range);
         return 0;
 }
 
@@ -154,17 +154,11 @@ static int parse_argv(int argc, char *argv[]) {
 
                         break;
 
-                case 'o': {
-                        LinkOperationalStateRange range;
-
-                        r = parse_operational_state_range(optarg, &range);
+                case 'o':
+                        r = parse_operational_state_range(optarg, &arg_required_operstate);
                         if (r < 0)
                                 return log_error_errno(r, "Invalid operational state range '%s'", optarg);
-
-                        arg_required_operstate = range;
-
                         break;
-                }
 
                 case '4':
                         arg_required_family |= ADDRESS_FAMILY_IPV4;
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 62cf331a4a9c1e521cb0705b7540e63c1ec56152..8926e275a38be6300d9c83052ea05918a23d8708 100644 (file)
@@ -1588,7 +1588,7 @@ static int oci_sysctl(const char *name, JsonVariant *v, JsonDispatchFlags flags,
                         return json_log(v, flags, SYNTHETIC_ERRNO(EINVAL),
                                         "sysctl key invalid, refusing: %s", k);
 
-                r = strv_extend_strv(&s->sysctl, STRV_MAKE(k, m), false);
+                r = strv_extend_many(&s->sysctl, k, m);
                 if (r < 0)
                         return log_oom();
         }
index 9e53c51f1ac2e295b3ee17ef14fdc08e7c54b974..7ec9889870cefeafcd63352d1f856c005e44567d 100644 (file)
@@ -1,10 +1,7 @@
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 
-#if HAVE_BLKID
-#endif
 #include <errno.h>
 #include <getopt.h>
-#include <linux/fs.h>
 #include <linux/loop.h>
 #if HAVE_SELINUX
 #include <selinux/selinux.h>
@@ -12,6 +9,7 @@
 #include <stdlib.h>
 #include <sys/file.h>
 #include <sys/ioctl.h>
+#include <sys/mount.h>
 #include <sys/personality.h>
 #include <sys/prctl.h>
 #include <sys/types.h>
@@ -19,6 +17,8 @@
 #include <termios.h>
 #include <unistd.h>
 
+#include <linux/fs.h> /* Must be included after <sys/mount.h> */
+
 #include "sd-bus.h"
 #include "sd-daemon.h"
 #include "sd-id128.h"
@@ -1396,7 +1396,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))
@@ -1413,7 +1413,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);
 
@@ -2364,6 +2364,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;
@@ -2371,9 +2383,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)
@@ -2713,9 +2725,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)
@@ -3581,9 +3593,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)
@@ -3596,6 +3610,102 @@ static int setup_notify_child(void) {
         return TAKE_FD(fd);
 }
 
+static int setup_unix_export_dir_outside(char **ret) {
+        int r;
+
+        assert(ret);
+
+        _cleanup_free_ char *p = NULL;
+        p = path_join("/run/systemd/nspawn/unix-export", arg_machine);
+        if (!p)
+                return log_oom();
+
+        r = path_is_mount_point(p, /* root= */ NULL, 0);
+        if (r > 0)
+                return log_error_errno(SYNTHETIC_ERRNO(EEXIST), "Mount point '%s' exists already, refusing.", p);
+        if (r < 0 && r != -ENOENT)
+                return log_error_errno(r, "Failed to detect if '%s' is a mount point: %m", p);
+
+        r = mkdir_p(p, 0755);
+        if (r < 0)
+                return log_error_errno(r, "Failed to create '%s': %m", p);
+
+        _cleanup_(rmdir_and_freep) char *q = TAKE_PTR(p);
+
+        /* Mount the "unix export" directory really tiny, just 64 inodes. We mark the superblock writable
+         * (since the container shall bind sockets into it). */
+        r = mount_nofollow_verbose(
+                        LOG_ERR,
+                        "tmpfs",
+                        q,
+                        "tmpfs",
+                        MS_NODEV|MS_NOEXEC|MS_NOSUID|ms_nosymfollow_supported(),
+                        "size=4M,nr_inodes=64,mode=0755");
+        if (r < 0)
+                return r;
+
+        _cleanup_(umount_and_rmdir_and_freep) char *w = TAKE_PTR(q);
+
+        /* After creating the superblock we change the bind mount to be read-only. This means that the fs
+         * itself is writable, but not through the mount accessible from the host. */
+        r = mount_nofollow_verbose(
+                        LOG_ERR,
+                        /* source= */ NULL,
+                        w,
+                        /* fstype= */ NULL,
+                        MS_BIND|MS_REMOUNT|MS_RDONLY|MS_NODEV|MS_NOEXEC|MS_NOSUID|ms_nosymfollow_supported(),
+                        /* options= */ NULL);
+        if (r < 0)
+                return r;
+
+        *ret = TAKE_PTR(w);
+        return 0;
+}
+
+static int setup_unix_export_host_inside(const char *directory, const char *unix_export_path) {
+        int r;
+
+        assert(directory);
+        assert(unix_export_path);
+
+        r = make_run_host(directory);
+        if (r < 0)
+                return r;
+
+        _cleanup_free_ char *p = path_join(directory, "run/host/unix-export");
+        if (!p)
+                return log_oom();
+
+        if (mkdir(p, 0755) < 0)
+                return log_error_errno(errno, "Failed to create '%s': %m", p);
+
+        r = mount_nofollow_verbose(
+                        LOG_ERR,
+                        unix_export_path,
+                        p,
+                        /* fstype= */ NULL,
+                        MS_BIND,
+                        /* options= */ NULL);
+        if (r < 0)
+                return r;
+
+        r = mount_nofollow_verbose(
+                        LOG_ERR,
+                        /* source= */ NULL,
+                        p,
+                        /* fstype= */ NULL,
+                        MS_BIND|MS_REMOUNT|MS_NODEV|MS_NOEXEC|MS_NOSUID|ms_nosymfollow_supported(),
+                        /* options= */ NULL);
+        if (r < 0)
+                return r;
+
+        r = userns_lchown(p, 0, 0);
+        if (r < 0)
+                return log_error_errno(r, "Failed to chown '%s': %m", p);
+
+        return 0;
+}
+
 static int outer_child(
                 Barrier *barrier,
                 const char *directory,
@@ -3603,7 +3713,8 @@ static int outer_child(
                 int fd_outer_socket,
                 int fd_inner_socket,
                 FDSet *fds,
-                int netns_fd) {
+                int netns_fd,
+                const char *unix_export_path) {
 
         _cleanup_(bind_user_context_freep) BindUserContext *bind_user_context = NULL;
         _cleanup_strv_free_ char **os_release_pairs = NULL;
@@ -3897,6 +4008,10 @@ static int outer_child(
         p = prefix_roota(directory, "/run/host");
         (void) make_inaccessible_nodes(p, arg_uid_shift, arg_uid_shift);
 
+        r = setup_unix_export_host_inside(directory, unix_export_path);
+        if (r < 0)
+                return r;
+
         r = setup_pts(directory);
         if (r < 0)
                 return r;
@@ -3946,11 +4061,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(
@@ -4748,6 +4863,7 @@ static int run_container(
         _cleanup_close_ int notify_socket = -EBADF, mntns_fd = -EBADF, fd_kmsg_fifo = -EBADF;
         _cleanup_(barrier_destroy) Barrier barrier = BARRIER_NULL;
         _cleanup_(sd_event_source_unrefp) sd_event_source *notify_event_source = NULL;
+        _cleanup_(umount_and_rmdir_and_freep) char *unix_export_host_dir = NULL;
         _cleanup_(sd_event_unrefp) sd_event *event = NULL;
         _cleanup_(pty_forward_freep) PTYForward *forward = NULL;
         _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
@@ -4763,6 +4879,11 @@ static int run_container(
         assert_se(sigemptyset(&mask_chld) == 0);
         assert_se(sigaddset(&mask_chld, SIGCHLD) == 0);
 
+        /* Set up the unix export host directory on the host first */
+        r = setup_unix_export_dir_outside(&unix_export_host_dir);
+        if (r < 0)
+                return r;
+
         if (arg_userns_mode == USER_NAMESPACE_PICK) {
                 /* When we shall pick the UID/GID range, let's first lock /etc/passwd, so that we can safely
                  * check with getpwuid() if the specific user already exists. Note that /etc might be
@@ -4833,7 +4954,8 @@ static int run_container(
                                 fd_outer_socket_pair[1],
                                 fd_inner_socket_pair[1],
                                 fds,
-                                child_netns_fd);
+                                child_netns_fd,
+                                unix_export_host_dir);
                 if (r < 0)
                         _exit(EXIT_FAILURE);
 
@@ -5907,6 +6029,10 @@ finish:
 
                 p = strjoina("/run/systemd/nspawn/propagate/", arg_machine);
                 (void) rm_rf(p, REMOVE_ROOT);
+
+                p = strjoina("/run/systemd/nspawn/unix-export/", arg_machine);
+                (void) umount2(p, MNT_DETACH|UMOUNT_NOFOLLOW);
+                (void) rmdir(p);
         }
 
         expose_port_flush(&fw_ctx, arg_expose_ports, AF_INET,  &expose_args.address4);
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 23c3ae64abca2d49d2a2be5a64f3eaff73413f9d..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              },
                 {},
         };
 
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 8841f4e76ce1ba03421a3c83d4b7b4f7429137d3..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-"
@@ -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 7c2ddd90ef77e14b5f11340736e12461b51108c6..d4a01fa53b0f3a84ad0363b6243ba29324b1654b 100644 (file)
@@ -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 8c9475cd3dee16380b3c720ae6040937b8526129..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,17 +188,18 @@ 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);
         }
 
-        case DNS_TRANSACTION_UPSTREAM_DNSSEC_FAILURE:
-                return reply_method_errorf(q, BUS_ERROR_DNSSEC_FAILED, "DNSSEC validation failed upstream: %s%s%s",
-                                           dns_ede_rcode_to_string(q->answer_ede_rcode),
-                                           isempty(q->answer_ede_msg) ? "" : ": ", q->answer_ede_msg);
-
         case DNS_TRANSACTION_NULL:
         case DNS_TRANSACTION_PENDING:
         case DNS_TRANSACTION_VALIDATING:
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 29d9dab060974cc78272e88dc78b8cf069ff789b..a7d04449b2f78bb33bd4bb36d8268fdbac79a4b9 100644 (file)
@@ -310,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 */
@@ -330,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. */
@@ -2677,17 +2694,15 @@ 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, char **ret_ede_msg) {
-        assert(p);
-
-        _cleanup_free_ char *msg = NULL, *msg_escaped = NULL;
-        int ede_rcode = _DNS_EDNS_OPT_MAX_DEFINED;
-        int r;
+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 _DNS_EDE_RCODE_INVALID;
+                return -ENOENT;
 
         d = p->opt->opt.data;
         l = p->opt->opt.data_size;
@@ -2707,31 +2722,40 @@ int dns_packet_ede_rcode(DnsPacket *p, char **ret_ede_msg) {
                                                "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.");
-                        ede_rcode = unaligned_read_be16(d + 4);
-                        r = make_cstring((char *)d + 6, length - 2U, MAKE_CSTRING_ALLOW_TRAILING_NUL, &msg);
+                                                       "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");
-                        else if (!utf8_is_valid(msg))
-                                return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "Invalid EDE text in opt");
-                        else if (ede_rcode < _DNS_EDNS_OPT_MAX_DEFINED) {
-                                msg_escaped = cescape(msg);
-                                if (!msg_escaped)
-                                        return -ENOMEM;
+                                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);
                         }
-                        break;
+
+                        if (ret_ede_rcode)
+                                *ret_ede_rcode = unaligned_read_be16(d + 4);
+
+                        return 0;
                 }
 
                 d += 4U + length;
                 l -= 4U + length;
         }
 
-        if (ret_ede_msg)
-                *ret_ede_msg = TAKE_PTR(msg_escaped);
-
-        return ede_rcode;
+        return -ENOENT;
 }
 
 bool dns_ede_rcode_is_dnssec(int ede_rcode) {
@@ -2821,6 +2845,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",
index 96e020a45cf1baca0b9f0d76f9d01a007610fb66..393b7b2364a5b5bcedf2929b974f61823e47f9f0 100644 (file)
@@ -253,94 +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, char **ret_ede_msg);
+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_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_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_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
+        _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_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
+        _DNS_EDE_RCODE_INVALID               = -EINVAL,
 };
 
 const char* dns_rcode_to_string(int i) _const_;
index d94fe1da997e4a7428483214a4a60e670459dcbe..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;
@@ -421,8 +423,6 @@ DnsQuery *dns_query_free(DnsQuery *q) {
         dns_answer_unref(q->reply_authoritative);
         dns_answer_unref(q->reply_additional);
 
-        free(q->answer_ede_msg);
-
         if (q->request_stream) {
                 /* Detach the stream from our query, in case something else keeps a reference to it. */
                 (void) set_remove(q->request_stream->queries, q);
@@ -516,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,
@@ -588,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,20 +899,13 @@ static void dns_query_accept(DnsQuery *q, DnsQueryCandidate *c) {
                             !FLAGS_SET(t->answer_query_flags, SD_RESOLVED_AUTHENTICATED))
                                 continue;
 
-                        char *answer_ede_msg = NULL;
-                        if (t->answer_ede_msg) {
-                                answer_ede_msg = strdup(t->answer_ede_msg);
-                                if (!answer_ede_msg) {
-                                        log_oom();
-                                        goto fail;
-                                }
-                        }
-
                         DNS_ANSWER_REPLACE(q->answer, dns_answer_ref(t->answer));
                         q->answer_rcode = t->answer_rcode;
-                        q->answer_dnssec_result = t->answer_dnssec_result;
                         q->answer_ede_rcode = t->answer_ede_rcode;
-                        q->answer_ede_msg = answer_ede_msg;
+                        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;
                         DNS_PACKET_REPLACE(q->answer_full_packet, dns_packet_ref(t->received));
index 74ad2c7350061d79afcf831800eb0f35e27401c8..29d7288981fa7e020650ca0d2af8660be7afb058 100644 (file)
@@ -73,9 +73,9 @@ struct DnsQuery {
         /* Discovered data */
         DnsAnswer *answer;
         int answer_rcode;
-        DnssecResult answer_dnssec_result;
         int answer_ede_rcode;
         char *answer_ede_msg;
+        DnssecResult answer_dnssec_result;
         uint64_t answer_query_flags;
         DnsProtocol answer_protocol;
         int answer_family;
index e3c70e88e27d95a78be669ec64af1e38d903c271..a99f8e8a587f09d8eeacfa3553c7ac233729d849 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;
@@ -166,8 +168,6 @@ DnsTransaction* dns_transaction_free(DnsTransaction *t) {
         dns_resource_key_unref(t->key);
         dns_packet_unref(t->bypass);
 
-        free(t->answer_ede_msg);
-
         return mfree(t);
 }
 
@@ -411,21 +411,6 @@ void dns_transaction_complete(DnsTransaction *t, DnsTransactionState state) {
                            "DNS_SERVER_FEATURE_LEVEL=%s", dns_server_feature_level_to_string(t->server->possible_feature_level));
         }
 
-        if (state == DNS_TRANSACTION_UPSTREAM_DNSSEC_FAILURE) {
-                dns_resource_key_to_string(dns_transaction_key(t), key_str, sizeof key_str);
-
-                log_struct(LOG_NOTICE,
-                           "MESSAGE_ID=" SD_MESSAGE_DNSSEC_FAILURE_STR,
-                           LOG_MESSAGE("Upstream resolver reported failure for question %s: %s%s%s",
-                                       key_str, dns_ede_rcode_to_string(t->answer_ede_rcode),
-                                       isempty(t->answer_ede_msg) ? "" : ": ", t->answer_ede_msg),
-                           "DNS_TRANSACTION=%" PRIu16, t->id,
-                           "DNS_QUESTION=%s", key_str,
-                           "DNS_EDE_RCODE=%s", dns_ede_rcode_to_string(t->answer_ede_rcode),
-                           "DNS_SERVER=%s", strna(dns_server_string_full(t->server)),
-                           "DNS_SERVER_FEATURE_LEVEL=%s", dns_server_feature_level_to_string(t->server->possible_feature_level));
-        }
-
         /* Note that this call might invalidate the query. Callers
          * should hence not attempt to access the query or transaction
          * after calling this function. */
@@ -653,9 +638,20 @@ static int on_stream_complete(DnsStream *s, int error) {
                 }
         }
 
-        if (error != 0)
-                LIST_FOREACH(transactions_by_stream, t, s->transactions)
+        if (error != 0) {
+                /* First, detach the stream from the server. Otherwise, transactions attached to this stream
+                 * may be restarted by on_transaction_stream_error() below with this stream. */
+                dns_stream_detach(s);
+
+                /* Do not use LIST_FOREACH() here, as
+                 *     on_transaction_stream_error()
+                 *         -> dns_transaction_complete_errno()
+                 *             -> dns_transaction_free()
+                 * may free multiple transactions in the list. */
+                DnsTransaction *t;
+                while ((t = s->transactions))
                         on_transaction_stream_error(t, error);
+        }
 
         return 0;
 }
@@ -903,8 +899,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;
 
@@ -1223,44 +1232,37 @@ void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p, bool encrypt
         switch (t->scope->protocol) {
 
         case DNS_PROTOCOL_DNS: {
-                int ede_rcode;
-                _cleanup_free_ char *ede_msg = NULL;
-
                 assert(t->server);
 
-                ede_rcode = dns_packet_ede_rcode(p, &ede_msg);
-                if (ede_rcode < 0 && ede_rcode != -EINVAL)
-                        log_debug_errno(ede_rcode, "Unable to extract EDE error code from packet, ignoring: %m");
-                else {
-                        t->answer_ede_rcode = ede_rcode;
-                        t->answer_ede_msg = TAKE_PTR(ede_msg);
-                }
+                (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 (ede_rcode >= 0 && DNS_PACKET_RCODE(p) == DNS_RCODE_SERVFAIL) {
+                        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(ede_rcode)) {
+                                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(ede_rcode),
-                                                        isempty(t->answer_ede_msg) ? "" : ": ",
-                                                        t->answer_ede_msg);
-                                        dns_transaction_complete(t, DNS_TRANSACTION_UPSTREAM_DNSSEC_FAILURE);
+                                                  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(ede_rcode, DNS_EDE_RCODE_NOT_READY, DNS_EDE_RCODE_NET_ERROR)) {
+                                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(ede_rcode),
-                                                        isempty(t->answer_ede_msg) ? "" : ": ",
-                                                        t->answer_ede_msg);
+                                                  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;
                                 }
@@ -1268,11 +1270,12 @@ void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p, bool encrypt
                                 /* 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(ede_rcode),
-                                                isempty(t->answer_ede_msg) ? "" : ": ", t->answer_ede_msg);
+                                          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;
-                        } /* No EDE rcode, or EDE rcode we don't understand */
+                        }
 
                         /* Request failed, immediately try again with reduced features */
 
@@ -1329,9 +1332,9 @@ 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 */
-                        if (ede_rcode > 0)
+                        if (t->answer_ede_rcode >= 0)
                                 log_debug("Server returned REFUSED (%s), switching servers, and retrying.",
-                                                FORMAT_DNS_EDE_RCODE(ede_rcode));
+                                          FORMAT_DNS_EDE_RCODE(t->answer_ede_rcode));
                         else
                                 log_debug("Server returned REFUSED, switching servers, and retrying.");
 
@@ -1829,8 +1832,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 1188708d8f5f41c4c1f902a41fd649f0ab1fa729..6de4cdd749122848d2382287604fa29ca6eed7df 100644 (file)
@@ -20,7 +20,6 @@ enum DnsTransactionState {
         DNS_TRANSACTION_PENDING,
         DNS_TRANSACTION_VALIDATING,
         DNS_TRANSACTION_RCODE_FAILURE,
-        DNS_TRANSACTION_UPSTREAM_DNSSEC_FAILURE,
         DNS_TRANSACTION_SUCCESS,
         DNS_TRANSACTION_NO_SERVERS,
         DNS_TRANSACTION_TIMEOUT,
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 a0251b4b97b37f511294074ad34f05dfd3aeaf9d..5a14e64fe5c4a1f5a74b08fab71bf767e8741242 100644 (file)
@@ -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..74e39a2ac1d3dd018e80421c14a7fe817cbd1b9a 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();
         }
@@ -1821,23 +1822,36 @@ static int start_transient_service(sd_bus *bus) {
                                 log_info("CPU time consumed: %s",
                                          FORMAT_TIMESPAN(DIV_ROUND_UP(c.cpu_usage_nsec, NSEC_PER_USEC), USEC_PER_MSEC));
 
-                        if (c.memory_peak != UINT64_MAX)
-                                log_info("Memory peak: %s", FORMAT_BYTES(c.memory_peak));
+                        if (c.memory_peak != UINT64_MAX) {
+                                const char *swap;
 
-                        if (c.memory_swap_peak != UINT64_MAX)
-                                log_info("Memory swap peak: %s", FORMAT_BYTES(c.memory_swap_peak));
+                                if (c.memory_swap_peak != UINT64_MAX)
+                                        swap = strjoina(" (swap: ", FORMAT_BYTES(c.memory_swap_peak), ")");
+                                else
+                                        swap = "";
 
-                        if (c.ip_ingress_bytes != UINT64_MAX)
-                                log_info("IP traffic received: %s", FORMAT_BYTES(c.ip_ingress_bytes));
+                                log_info("Memory peak: %s%s", FORMAT_BYTES(c.memory_peak), swap);
+                        }
+
+                        const char *ip_ingress = NULL, *ip_egress = NULL;
+
+                        if (!IN_SET(c.ip_ingress_bytes, 0, UINT64_MAX))
+                                ip_ingress = strjoina(" received: ", FORMAT_BYTES(c.ip_ingress_bytes));
+                        if (!IN_SET(c.ip_egress_bytes, 0, UINT64_MAX))
+                                ip_egress = strjoina(" sent: ", FORMAT_BYTES(c.ip_egress_bytes));
+
+                        if (ip_ingress || ip_egress)
+                                log_info("IP traffic%s%s", strempty(ip_ingress), strempty(ip_egress));
 
-                        if (c.ip_egress_bytes != UINT64_MAX)
-                                log_info("IP traffic sent: %s", FORMAT_BYTES(c.ip_egress_bytes));
+                        const char *io_read = NULL, *io_write = NULL;
 
-                        if (c.io_read_bytes != UINT64_MAX)
-                                log_info("IO bytes read: %s", FORMAT_BYTES(c.io_read_bytes));
+                        if (!IN_SET(c.io_read_bytes, 0, UINT64_MAX))
+                                io_read = strjoina(" read: ", FORMAT_BYTES(c.io_read_bytes));
+                        if (!IN_SET(c.io_write_bytes, 0, UINT64_MAX))
+                                io_write = strjoina(" written: ", FORMAT_BYTES(c.io_write_bytes));
 
-                        if (c.io_write_bytes != UINT64_MAX)
-                                log_info("IO bytes written: %s", FORMAT_BYTES(c.io_write_bytes));
+                        if (io_read || io_write)
+                                log_info("IO bytes%s%s", strempty(io_read), strempty(io_write));
                 }
 
                 /* Try to propagate the service's return value. But if the service defines
index 85cb067c8414f29f44612945389fc56113ebb858..37b3f6a6ea35767fa92c6920ddca4d593ba6b928 100644 (file)
@@ -241,13 +241,13 @@ int battery_is_discharging_and_low(void) {
 
         r = on_ac_power();
         if (r < 0)
-                log_debug_errno(r, "Failed to check if the system is running on AC, assuming it is not: %m");
+                log_warning_errno(r, "Failed to check if the system is running on AC, assuming it is not: %m");
         if (r > 0)
                 return false;
 
         r = battery_enumerator_new(&e);
         if (r < 0)
-                return log_debug_errno(r, "Failed to initialize battery enumerator: %m");
+                return log_error_errno(r, "Failed to initialize battery enumerator: %m");
 
         FOREACH_DEVICE(e, dev) {
                 int level;
index e726073b646a51f74f0a8c833c49c619f26066a3..72d3cbea987d5b08a10afff12c5c9c7c4a36e47f 100644 (file)
@@ -15,20 +15,18 @@ bool boot_entry_token_valid(const char *p) {
         return utf8_is_valid(p) && string_is_safe(p) && filename_is_valid(p);
 }
 
-static int entry_token_load(int rfd, const char *etc_kernel, BootEntryTokenType *type, char **token) {
+static int entry_token_load_one(int rfd, const char *dir, BootEntryTokenType *type, char **token) {
         _cleanup_free_ char *buf = NULL, *p = NULL;
         _cleanup_fclose_ FILE *f = NULL;
         int r;
 
         assert(rfd >= 0 || rfd == AT_FDCWD);
+        assert(dir);
         assert(type);
         assert(*type == BOOT_ENTRY_TOKEN_AUTO);
         assert(token);
 
-        if (!etc_kernel)
-                return 0;
-
-        p = path_join(etc_kernel, "entry-token");
+        p = path_join(dir, "entry-token");
         if (!p)
                 return log_oom();
 
@@ -55,6 +53,26 @@ static int entry_token_load(int rfd, const char *etc_kernel, BootEntryTokenType
         return 1;
 }
 
+static int entry_token_load(int rfd, const char *conf_root, BootEntryTokenType *type, char **token) {
+        int r;
+
+        assert(rfd >= 0 || rfd == AT_FDCWD);
+        assert(type);
+        assert(*type == BOOT_ENTRY_TOKEN_AUTO);
+        assert(token);
+
+        if (conf_root)
+                return entry_token_load_one(rfd, conf_root, type, token);
+
+        FOREACH_STRING(path, "/etc/kernel", "/usr/lib/kernel") {
+                r = entry_token_load_one(rfd, path, type, token);
+                if (r != 0)
+                        return r;
+        }
+
+        return 0;
+}
+
 static int entry_token_from_machine_id(sd_id128_t machine_id, BootEntryTokenType *type, char **token) {
         char *p;
 
@@ -123,7 +141,7 @@ static int entry_token_from_os_release(int rfd, BootEntryTokenType *type, char *
 
 int boot_entry_token_ensure_at(
                 int rfd,
-                const char *etc_kernel,
+                const char *conf_root,
                 sd_id128_t machine_id,
                 bool machine_id_is_random,
                 BootEntryTokenType *type,
@@ -141,7 +159,7 @@ int boot_entry_token_ensure_at(
         switch (*type) {
 
         case BOOT_ENTRY_TOKEN_AUTO:
-                r = entry_token_load(rfd, etc_kernel, type, token);
+                r = entry_token_load(rfd, conf_root, type, token);
                 if (r != 0)
                         return r;
 
@@ -198,7 +216,7 @@ int boot_entry_token_ensure_at(
 
 int boot_entry_token_ensure(
                 const char *root,
-                const char *etc_kernel,
+                const char *conf_root,
                 sd_id128_t machine_id,
                 bool machine_id_is_random,
                 BootEntryTokenType *type,
@@ -215,7 +233,7 @@ int boot_entry_token_ensure(
         if (rfd < 0)
                 return -errno;
 
-        return boot_entry_token_ensure_at(rfd, etc_kernel, machine_id, machine_id_is_random, type, token);
+        return boot_entry_token_ensure_at(rfd, conf_root, machine_id, machine_id_is_random, type, token);
 }
 
 int parse_boot_entry_token_type(const char *s, BootEntryTokenType *type, char **token) {
index f3a6f28417668520fa01094c83571bd448db9b09..836b63733a9b5494440e0f4873467b4b4f46eb37 100644 (file)
@@ -17,14 +17,14 @@ bool boot_entry_token_valid(const char *p);
 
 int boot_entry_token_ensure(
                 const char *root,
-                const char *etc_kernel,   /* will be prefixed with root, typically /etc/kernel. */
+                const char *conf_root,   /* will be prefixed with root, typically /etc/kernel. */
                 sd_id128_t machine_id,
                 bool machine_id_is_random,
                 BootEntryTokenType *type, /* input and output */
                 char **token);            /* output, but do not pass uninitialized value. */
 int boot_entry_token_ensure_at(
                 int rfd,
-                const char *etc_kernel,
+                const char *conf_root,
                 sd_id128_t machine_id,
                 bool machine_id_is_random,
                 BootEntryTokenType *type,
index fe9363aad4466312f3c602aee2d9e0cacee818c7..d05c474e7f06bf7a061c719390a0d5806a057174 100644 (file)
@@ -192,9 +192,9 @@ typedef struct AsyncPolkitQuery {
         AsyncPolkitQueryAction *action;
 
         sd_bus *bus;
-        sd_bus_message *request;
+        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;
+        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;
@@ -211,8 +211,12 @@ 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);
 
@@ -276,8 +280,13 @@ static int async_polkit_read_reply(sd_bus_message *reply, AsyncPolkitQuery *q) {
 
                 e = sd_bus_message_get_error(reply);
 
-                if (bus_error_is_unknown_service(e))
-                        /* Treat no PK available as access denied */
+                if (bus_error_is_unknown_service(e) ||
+                    sd_bus_error_has_names(
+                                    e,
+                                    "org.freedesktop.PolicyKit1.Error.Failed",
+                                    "org.freedesktop.PolicyKit1.Error.Cancelled",
+                                    "org.freedesktop.PolicyKit1.Error.NotAuthorized"))
+                        /* Treat no PK available as access denied. Also treat some of the well-known PK errors as such. */
                         q->denied_action = TAKE_PTR(a);
                 else {
                         /* Save error from polkit reply, so it can be returned when the same authorization
@@ -377,7 +386,7 @@ static int async_polkit_callback(sd_bus_message *reply, void *userdata, sd_bus_e
                 if (q->request)
                         (void) sd_bus_reply_method_errno(q->request, r, NULL);
                 if (q->link)
-                        varlink_error_errno(q->link, r);
+                        (void) varlink_error_errno(q->link, r);
                 async_polkit_query_unref(q);
         }
         return r;
@@ -738,10 +747,9 @@ int varlink_verify_polkit_async(
                 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);
+                                (void) varlink_error(link, VARLINK_ERROR_INTERACTIVE_AUTHENTICATION_REQUIRED, NULL);
+                        else if (ERRNO_IS_NEG_PRIVILEGE(r))
+                                (void) varlink_error(link, VARLINK_ERROR_PERMISSION_DENIED, NULL);
 
                         return r;
                 }
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 1d201528ace4bc50a4256bbb5f27c652a983d219..f5efc7618a819b98b96bd05df1acadfdeb4f53f7 100644 (file)
@@ -598,10 +598,11 @@ static int config_parse_many_files(
         return 0;
 }
 
-/* Parse one main config file located in /etc/systemd and its drop-ins, which is what all systemd daemons
+/* Parse one main config file located in /etc/$pkgdir and its drop-ins, which is what all systemd daemons
  * do. */
-int config_parse_config_file(
+int config_parse_config_file_full(
                 const char *conf_file,
+                const char *pkgdir,
                 const char *sections,
                 ConfigItemLookup lookup,
                 const void *table,
@@ -613,6 +614,7 @@ int config_parse_config_file(
         int r;
 
         assert(conf_file);
+        assert(pkgdir);
 
         /* build the dropin dir list */
         dropin_dirs = new0(char*, strv_length(conf_paths) + 1);
@@ -626,7 +628,7 @@ int config_parse_config_file(
         STRV_FOREACH(p, conf_paths) {
                 char *d;
 
-                d = strjoin(*p, "systemd/", conf_file, ".d");
+                d = strjoin(*p, pkgdir, "/", conf_file, ".d");
                 if (!d) {
                         if (flags & CONFIG_PARSE_WARN)
                                 return log_oom();
@@ -640,7 +642,7 @@ int config_parse_config_file(
         if (r < 0)
                 return r;
 
-        const char *sysconf_file = strjoina(PKGSYSCONFDIR, "/", conf_file);
+        const char *sysconf_file = strjoina(SYSCONF_DIR, "/", pkgdir, "/", conf_file);
 
         return config_parse_many_files(STRV_MAKE_CONST(sysconf_file), dropins,
                                        sections, lookup, table, flags, userdata, NULL);
@@ -795,12 +797,12 @@ bool stats_by_path_equal(Hashmap *a, Hashmap *b) {
         return true;
 }
 
-static void config_section_hash_func(const ConfigSection *c, struct siphash *state) {
+void config_section_hash_func(const ConfigSection *c, struct siphash *state) {
         siphash24_compress_string(c->filename, state);
         siphash24_compress_typesafe(c->line, state);
 }
 
-static int config_section_compare_func(const ConfigSection *x, const ConfigSection *y) {
+int config_section_compare_func(const ConfigSection *x, const ConfigSection *y) {
         int r;
 
         r = strcmp(x->filename, y->filename);
@@ -1062,7 +1064,7 @@ int config_parse_tristate(
 
         if (isempty(rvalue)) {
                 *t = -1;
-                return 0;
+                return 1;
         }
 
         r = parse_tristate(rvalue, t);
@@ -1072,7 +1074,7 @@ int config_parse_tristate(
                 return 0;
         }
 
-        return 0;
+        return 1;
 }
 
 int config_parse_string(
@@ -1088,6 +1090,7 @@ int config_parse_string(
                 void *userdata) {
 
         char **s = ASSERT_PTR(data);
+        int r;
 
         assert(filename);
         assert(lvalue);
@@ -1095,7 +1098,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 +1119,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(
index a1768cd9198e9c511f1075bf84c4a6ebd2431b5c..30573564791672731e2fd66776dcab3e05c73700 100644 (file)
@@ -93,14 +93,25 @@ int config_parse(
                 void *userdata,
                 struct stat *ret_stat);     /* possibly NULL */
 
-int config_parse_config_file(
+int config_parse_config_file_full(
                 const char *conf_file,
+                const char *pkgdir,
                 const char *sections,       /* nulstr */
                 ConfigItemLookup lookup,
                 const void *table,
                 ConfigParseFlags flags,
                 void *userdata);
 
+static inline int config_parse_config_file(
+                const char *conf_file,
+                const char *sections,       /* nulstr */
+                ConfigItemLookup lookup,
+                const void *table,
+                ConfigParseFlags flags,
+                void *userdata) {
+        return config_parse_config_file_full(conf_file, "systemd", sections, lookup, table, flags, userdata);
+}
+
 int config_parse_many(
                 const char* const* conf_files,  /* possibly empty */
                 const char* const* conf_file_dirs,
@@ -137,7 +148,11 @@ static inline ConfigSection* config_section_free(ConfigSection *cs) {
 DEFINE_TRIVIAL_CLEANUP_FUNC(ConfigSection*, config_section_free);
 
 int config_section_new(const char *filename, unsigned line, ConfigSection **ret);
+
+void config_section_hash_func(const ConfigSection *c, struct siphash *state);
+int config_section_compare_func(const ConfigSection *x, const ConfigSection *y);
 extern const struct hash_ops config_section_hash_ops;
+
 int _hashmap_by_section_find_unused_line(
                 HashmapBase *entries_by_section,
                 const char *filename,
index 0026da5b48352fcabc7ebd73caeb0245989fad5a..0325f6e129387c9b59c52f71efdf74d051b2d471 100644 (file)
@@ -28,7 +28,6 @@
 #include "sparse-endian.h"
 #include "stat-util.h"
 #include "tpm2-util.h"
-#include "virt.h"
 
 #define PUBLIC_KEY_MAX (UINT32_C(1024) * UINT32_C(1024))
 
@@ -138,14 +137,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 +191,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 +218,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 +234,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 +253,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 +355,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 +422,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 +431,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 +443,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 +511,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 +578,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 +679,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 +699,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 +723,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 +750,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 +780,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 +813,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 +822,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 +844,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 +854,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 +865,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 +878,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 +924,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 +942,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 +1012,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 +1066,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 +1104,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 +1125,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 +1140,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 +1152,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,8 +1168,8 @@ 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 <
-                    ALIGN8(offsetof(struct encrypted_credential_header, iv) + le32toh(h->iv_size)) +
+                if (input->iov_len <
+                    p +
                     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) +
                     ALIGN8(offsetof(struct metadata_credential_header, name)) +
@@ -1197,16 +1181,15 @@ 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 <
-                            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)) +
+                        if (input->iov_len <
+                            p +
                             ALIGN8(offsetof(struct tpm2_public_key_credential_header, data) + le32toh(z->size)) +
                             ALIGN8(offsetof(struct metadata_credential_header, name)) +
                             le32toh(h->tag_size))
@@ -1226,21 +1209,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 +1227,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 +1262,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 +1307,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 +1349,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 ed89580d822b8909c1f7c5cc1113f23722355d06..6d4f7612caac15c1ff3e4c00c01816817d7c8026 100644 (file)
@@ -1281,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;
@@ -1356,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) {
@@ -1531,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);
@@ -1569,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 4cd1091e9ac37a0c816760a47e6d0be10daeb7bd..eb84e16dae4db1566578bfc09636bd5c81d72e56 100644 (file)
@@ -453,13 +453,13 @@ int efi_get_boot_options(uint16_t **ret_options) {
         FOREACH_DIRENT(de, dir, return -errno) {
                 int id;
 
-                if (strncmp(de->d_name, "Boot", 4) != 0)
+                if (!startswith(de->d_name, "Boot"))
                         continue;
 
                 if (strlen(de->d_name) != 45)
                         continue;
 
-                if (strcmp(de->d_name + 8, EFI_GLOBAL_VARIABLE_STR("")) != 0)  /* generate variable suffix using macro */
+                if (!streq(de->d_name + 8, EFI_GLOBAL_VARIABLE_STR(""))) /* generate variable suffix using macro */
                         continue;
 
                 id = boot_id_hex(de->d_name + 4);
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 ef3dae5b6242fe1b01957846c0823d0f9f6ebea5..bfb0f05c4fe4424db2c1de97424e4d32f9c78d42 100644 (file)
@@ -232,23 +232,21 @@ int fstab_filter_options(
                         }
 
                         NULSTR_FOREACH(name, names) {
-                                if (end < word + strlen(name))
-                                        continue;
-                                if (!strneq(word, name, strlen(name)))
+                                x = startswith(word, name);
+                                if (!x || x > end)
                                         continue;
 
                                 /* We know that the string is NUL terminated, so *x is valid */
-                                x = word + strlen(name);
                                 if (IN_SET(*x, '\0', '=', ',')) {
                                         namefound = name;
                                         break;
                                 }
                         }
 
-                        if (*end)
-                                word = end + 1;
-                        else
+                        if (*end == '\0')
                                 break;
+
+                        word = end + 1;
                 }
 
 answer:
index fe58021f000d9c4cebc2bff798b4c7d63697d778..b96715c59cc7c91fe7c37e5a02b66055d65a8001 100644 (file)
@@ -29,6 +29,7 @@ int generator_open_unit_file_full(
                 const char *source,
                 const char *fn,
                 FILE **ret_file,
+                char **ret_final_path,
                 char **ret_temp_path) {
 
         _cleanup_free_ char *p = NULL;
@@ -72,10 +73,13 @@ int generator_open_unit_file_full(
                 program_invocation_short_name);
 
         *ret_file = f;
+
+        if (ret_final_path)
+                *ret_final_path = TAKE_PTR(p);
+
         return 0;
 }
 
-
 int generator_add_symlink_full(
                 const char *dir,
                 const char *dst,
@@ -88,11 +92,13 @@ int generator_add_symlink_full(
 
         assert(dir);
         assert(dst);
-        assert(dep_type);
         assert(src);
 
-        /* Adds a symlink from <dst>.<dep_type>/ to <src> (if src is absolute) or ../<src> (otherwise). If
-         * <instance> is specified, then <src> must be a template unit name, and we'll instantiate it. */
+        /* If 'dep_type' is specified adds a symlink from <dst>.<dep_type>/ to <src> (if src is absolute) or ../<src> (otherwise).
+         *
+         * If 'dep_type' is NULL, it will create a symlink to <src> (i.e. create an alias.
+         *
+         * If <instance> is specified, then <src> must be a template unit name, and we'll instantiate it. */
 
         r = path_extract_directory(src, &dn);
         if (r < 0 && r != -EDESTADDRREQ) /* EDESTADDRREQ → just a file name was passed */
@@ -110,11 +116,19 @@ int generator_add_symlink_full(
                         return log_error_errno(r, "Failed to instantiate '%s' for '%s': %m", fn, instance);
         }
 
-        from = path_join(dn ?: "..", fn);
-        if (!from)
-                return log_oom();
+        if (dep_type) { /* Create a .wants/ style dep */
+                from = path_join(dn ?: "..", fn);
+                if (!from)
+                        return log_oom();
+
+                to = strjoin(dir, "/", dst, ".", dep_type, "/", instantiated ?: fn);
+        } else { /* or create an alias */
+                from = dn ? path_join(dn, fn) : strdup(fn);
+                if (!from)
+                        return log_oom();
 
-        to = strjoin(dir, "/", dst, ".", dep_type, "/", instantiated ?: fn);
+                to = strjoin(dir, "/", dst);
+        }
         if (!to)
                 return log_oom();
 
index d97d6edc676cf6ef26aa23eb068d42d7ae56c564..c17feafacc257673f4e479f1cd807a862729fd57 100644 (file)
@@ -6,10 +6,10 @@
 #include "macro.h"
 #include "main-func.h"
 
-int generator_open_unit_file_full(const char *dest, const char *source, const char *name, FILE **ret_file, char **ret_temp_path);
+int generator_open_unit_file_full(const char *dest, const char *source, const char *name, FILE **ret_file, char **ret_final_path, char **ret_temp_path);
 
 static inline int generator_open_unit_file(const char *dest, const char *source, const char *name, FILE **ret_file) {
-        return generator_open_unit_file_full(dest, source, name, ret_file, NULL);
+        return generator_open_unit_file_full(dest, source, name, ret_file, NULL, NULL);
 }
 
 int generator_add_symlink_full(const char *dir, const char *dst, const char *dep_type, const char *src, const char *instance);
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 795b3a2d56f915b4bdadb61d9ed320dca5b3ae07..3547476c85141522c0fc130d0ed55f7306caa0dc 100644 (file)
@@ -152,7 +152,8 @@ static int read_resume_config(dev_t *ret_devno, uint64_t *ret_offset) {
 
         if (devno == 0 && offset > 0 && offset != UINT64_MAX)
                 return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
-                                       "Found resume_offset=%" PRIu64 " but resume= is unset, refusing.", offset);
+                                       "Found populated /sys/power/resume_offset (%" PRIu64 ") but /sys/power/resume is not set, refusing.",
+                                       offset);
 
         *ret_devno = devno;
         *ret_offset = offset;
index ad30e9b49c1f6d7a068bc47b94aa517356e5f3c9..fabf5db7ed2e139548ff30073c96cf9addd442ac 100644 (file)
@@ -3142,8 +3142,10 @@ int unit_file_get_state(
         return unit_file_lookup_state(scope, &lp, name, ret);
 }
 
-int unit_file_exists(RuntimeScope scope, const LookupPaths *lp, const char *name) {
-        _cleanup_(install_context_done) InstallContext c = { .scope = scope };
+int unit_file_exists_full(RuntimeScope scope, const LookupPaths *lp, const char *name, char **ret_path) {
+        _cleanup_(install_context_done) InstallContext c = {
+                .scope = scope,
+        };
         int r;
 
         assert(lp);
@@ -3152,12 +3154,33 @@ int unit_file_exists(RuntimeScope scope, const LookupPaths *lp, const char *name
         if (!unit_name_is_valid(name, UNIT_NAME_ANY))
                 return -EINVAL;
 
-        r = install_info_discover(&c, lp, name, 0, NULL, NULL, NULL);
-        if (r == -ENOENT)
+        InstallInfo *info = NULL;
+        r = install_info_discover(
+                        &c,
+                        lp,
+                        name,
+                        /* flags= */ 0,
+                        ret_path ? &info : NULL,
+                        /* changes= */ NULL,
+                        /* n_changes= */ NULL);
+        if (r == -ENOENT) {
+                if (ret_path)
+                        *ret_path = NULL;
                 return 0;
+        }
         if (r < 0)
                 return r;
 
+        if (ret_path) {
+                assert(info);
+
+                _cleanup_free_ char *p = strdup(info->path);
+                if (!p)
+                        return -ENOMEM;
+
+                *ret_path = TAKE_PTR(p);
+        }
+
         return 1;
 }
 
index bc0c6db828ddebcfb63e63e595239fb23111def0..3e2ada45f495f5a697ebdd25d53675041c388183 100644 (file)
@@ -193,7 +193,11 @@ int unit_file_lookup_state(
                 UnitFileState *ret);
 
 int unit_file_get_state(RuntimeScope scope, const char *root_dir, const char *filename, UnitFileState *ret);
-int unit_file_exists(RuntimeScope scope, const LookupPaths *paths, const char *name);
+
+int unit_file_exists_full(RuntimeScope scope, const LookupPaths *paths, const char *name, char **ret_path);
+static inline int unit_file_exists(RuntimeScope scope, const LookupPaths *paths, const char *name) {
+        return unit_file_exists_full(scope, paths, name, NULL);
+}
 
 int unit_file_get_list(RuntimeScope scope, const char *root_dir, Hashmap *h, char **states, char **patterns);
 
index 15675a7046ea84b19658ece1b68c1ad61e4f4384..7ec6368e1eccbc1e36f1cea920514b490aafc24b 100644 (file)
@@ -14,7 +14,9 @@
 #include "fd-util.h"
 #include "fileio.h"
 #include "float.h"
+#include "glyph-util.h"
 #include "hexdecoct.h"
+#include "iovec-util.h"
 #include "json-internal.h"
 #include "json.h"
 #include "macro.h"
@@ -87,6 +89,9 @@ struct JsonVariant {
         /* Erase from memory when freeing */
         bool sensitive:1;
 
+        /* True if we know that any referenced json object is marked sensitive */
+        bool recursive_sensitive:1;
+
         /* If this is an object the fields are strictly ordered by name */
         bool sorted:1;
 
@@ -1451,6 +1456,33 @@ bool json_variant_is_sensitive(JsonVariant *v) {
         return v->sensitive;
 }
 
+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 (v->recursive_sensitive) /* Already checked this before */
+                return true;
+        if (!IN_SET(v->type, JSON_VARIANT_ARRAY, JSON_VARIANT_OBJECT))
+                return false;
+        if (v->is_reference) {
+                if (!json_variant_is_sensitive_recursive(v->reference))
+                        return false;
+
+                return (v->recursive_sensitive = true);
+        }
+
+        for (size_t i = 0; i < json_variant_elements(v); i++)
+                if (json_variant_is_sensitive_recursive(json_variant_by_index(v, i)))
+                        return (v->recursive_sensitive = true);
+
+        /* Note: we only cache the result here in case true, since we allow all elements down the tree to
+         * have their sensitive flag toggled later on (but never off) */
+        return false;
+}
+
 static void json_variant_propagate_sensitive(JsonVariant *from, JsonVariant *to) {
         if (json_variant_is_sensitive(from))
                 json_variant_sensitive(to);
@@ -1580,6 +1612,15 @@ static int json_format(FILE *f, JsonVariant *v, JsonFormatFlags flags, const cha
         assert(f);
         assert(v);
 
+        if (FLAGS_SET(flags, JSON_FORMAT_CENSOR_SENSITIVE) && json_variant_is_sensitive(v)) {
+                if (flags & JSON_FORMAT_COLOR)
+                        fputs(ansi_red(), f);
+                fputs("\"<sensitive data>\"", f);
+                if (flags & JSON_FORMAT_COLOR)
+                        fputs(ANSI_NORMAL, f);
+                return 0;
+        }
+
         switch (json_variant_type(v)) {
 
         case JSON_VARIANT_REAL: {
@@ -1771,28 +1812,6 @@ 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 (v == JSON_VARIANT_MAGIC_EMPTY_ARRAY ||
-            v == JSON_VARIANT_MAGIC_EMPTY_OBJECT)
-                return false;
-        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;
@@ -1808,10 +1827,6 @@ 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;
@@ -3816,7 +3831,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)) {
@@ -3824,10 +3840,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;
                         }
@@ -4618,8 +4638,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|JSON_DEBUG, 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;
 
@@ -4987,6 +5011,63 @@ int json_dispatch_unbase64_iovec(const char *name, JsonVariant *variant, JsonDis
         return 0;
 }
 
+int json_dispatch_byte_array_iovec(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
+        _cleanup_free_ uint8_t *buffer = NULL;
+        struct iovec *iov = ASSERT_PTR(userdata);
+        size_t sz, k = 0;
+
+        assert(variant);
+
+        if (!json_variant_is_array(variant))
+                return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an array.", strna(name));
+
+        sz = json_variant_elements(variant);
+
+        buffer = new(uint8_t, sz + 1);
+        if (!buffer)
+                return json_log(variant, flags, SYNTHETIC_ERRNO(ENOMEM), "Out of memory.");
+
+        JsonVariant *i;
+        JSON_VARIANT_ARRAY_FOREACH(i, variant) {
+                uint64_t b;
+
+                if (!json_variant_is_unsigned(i))
+                        return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "Element %zu of JSON field '%s' is not an unsigned integer.", k, strna(name));
+
+                b = json_variant_unsigned(i);
+                if (b > 0xff)
+                        return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL),
+                                        "Element %zu of JSON field '%s' is out of range 0%s255.",
+                                        k, strna(name), special_glyph(SPECIAL_GLYPH_ELLIPSIS));
+
+                buffer[k++] = (uint8_t) b;
+        }
+        assert(k == sz);
+
+        /* Append a NUL byte for safety, like we do in memdup_suffix0() and others. */
+        buffer[sz] = 0;
+
+        free_and_replace(iov->iov_base, buffer);
+        iov->iov_len = sz;
+        return 0;
+}
+
+int json_dispatch_in_addr(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
+        struct in_addr *address = ASSERT_PTR(userdata);
+        _cleanup_(iovec_done) struct iovec iov = {};
+        int r;
+
+        r = json_dispatch_byte_array_iovec(name, variant, flags, &iov);
+        if (r < 0)
+                return r;
+
+        if (iov.iov_len != sizeof(struct in_addr))
+                return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is array of unexpected size.", strna(name));
+
+        memcpy(address, iov.iov_base, iov.iov_len);
+        return 0;
+}
+
 static int json_cmp_strings(const void *x, const void *y) {
         JsonVariant *const *a = x, *const *b = y;
 
@@ -5133,14 +5214,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 975cf562a9640421bc5baa7fddb0d8e658138fd3..9c8448f728b82e450de8034c1c2c7d5e18cf832c 100644 (file)
@@ -155,6 +155,7 @@ bool json_variant_equal(JsonVariant *a, JsonVariant *b);
 
 void json_variant_sensitive(JsonVariant *v);
 bool json_variant_is_sensitive(JsonVariant *v);
+bool json_variant_is_sensitive_recursive(JsonVariant *v);
 
 struct json_variant_foreach_state {
         JsonVariant *variant;
@@ -196,7 +197,7 @@ typedef enum JsonFormatFlags {
         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 */
+        JSON_FORMAT_CENSOR_SENSITIVE = 1 << 11, /* Replace all sensitive elements with the string "<sensitive data>" */
 } JsonFormatFlags;
 
 int json_variant_format(JsonVariant *v, JsonFormatFlags flags, char **ret);
@@ -272,6 +273,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,
@@ -316,6 +318,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) }
@@ -346,6 +349,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))
@@ -376,15 +380,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);
@@ -421,6 +426,8 @@ int json_dispatch_user_group_name(const char *name, JsonVariant *variant, JsonDi
 int json_dispatch_id128(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata);
 int json_dispatch_unsupported(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata);
 int json_dispatch_unbase64_iovec(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata);
+int json_dispatch_byte_array_iovec(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata);
+int json_dispatch_in_addr(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata);
 
 assert_cc(sizeof(uint32_t) == sizeof(unsigned));
 #define json_dispatch_uint json_dispatch_uint32
@@ -428,6 +435,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
@@ -471,5 +500,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 1b95430f8843f703696c3a488b520515fc89765d..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',
index 19b119b4afaa18e7ebcd9c75e8ebe6dc33c8f8eb..4d440127e57ff942a51d9a0b7cbcc6c9a47d0675 100644 (file)
@@ -425,7 +425,7 @@ int make_filesystem(
                                 "-T", "default",
                                 node);
 
-                if (root && strv_extend_strv(&argv, STRV_MAKE("-d", root), false) < 0)
+                if (root && strv_extend_many(&argv, "-d", root) < 0)
                         return log_oom();
 
                 if (quiet && strv_extend(&argv, "-q") < 0)
@@ -450,7 +450,7 @@ int make_filesystem(
                 if (!discard && strv_extend(&argv, "--nodiscard") < 0)
                         return log_oom();
 
-                if (root && strv_extend_strv(&argv, STRV_MAKE("-r", root), false) < 0)
+                if (root && strv_extend_many(&argv, "-r", root) < 0)
                         return log_oom();
 
                 if (quiet && strv_extend(&argv, "-q") < 0)
@@ -514,7 +514,7 @@ int make_filesystem(
                         if (!protofile_with_opt)
                                 return -ENOMEM;
 
-                        if (strv_extend_strv(&argv, STRV_MAKE("-p", protofile_with_opt), false) < 0)
+                        if (strv_extend_many(&argv, "-p", protofile_with_opt) < 0)
                                 return log_oom();
                 }
 
index 951701d497ef40320d634c238873bdc22f328563..907990d96e4d0a573f8b340ede06fbc40cd1b8f6 100644 (file)
@@ -8,6 +8,7 @@
 
 static int denylist_modules(const char *p, char ***denylist) {
         _cleanup_strv_free_ char **k = NULL;
+        int r;
 
         assert(p);
         assert(denylist);
@@ -16,8 +17,9 @@ static int denylist_modules(const char *p, char ***denylist) {
         if (!k)
                 return -ENOMEM;
 
-        if (strv_extend_strv(denylist, k, true) < 0)
-                return -ENOMEM;
+        r = strv_extend_strv(denylist, k, /* filter_duplicates= */ true);
+        if (r < 0)
+                return r;
 
         return 0;
 }
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 59437ae0bb9fb6c58a4034fcdd9aecf5806f4728..13b68234c9bf5b838b66d5a5d3758b2395a7968d 100644 (file)
@@ -226,3 +226,45 @@ int pam_get_item_many_internal(pam_handle_t *handle, ...) {
 
         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 9c40ba2dded7189f685e897381954ba548d59dfe..3439d4246e1636edb6064ac281e0c03869e8f43e 100644 (file)
@@ -43,3 +43,5 @@ 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 2fef29c67fe625bb08607366ed33a249637741ce..d2b1a3e554330c94b1c3e797d3cbeeb026a61aca 100644 (file)
@@ -39,8 +39,6 @@ typedef enum Initialized {
         LAZY_INITIALIZED,
 } Initialized;
 
-static int mac_selinux_reload(int seqno);
-
 static int cached_use = -1;
 static Initialized initialized = UNINITIALIZED;
 static int last_policyload = 0;
@@ -214,6 +212,16 @@ int mac_selinux_init_lazy(void) {
         return 0;
 }
 
+#if HAVE_SELINUX
+static int mac_selinux_reload(int seqno) {
+        log_debug("SELinux reload %d", seqno);
+
+        (void) open_label_db();
+
+        return 0;
+}
+#endif
+
 void mac_selinux_maybe_reload(void) {
 #if HAVE_SELINUX
         int policyload;
@@ -256,16 +264,6 @@ void mac_selinux_finish(void) {
 #endif
 }
 
-#if HAVE_SELINUX
-static int mac_selinux_reload(int seqno) {
-        log_debug("SELinux reload %d", seqno);
-
-        (void) open_label_db();
-
-        return 0;
-}
-#endif
-
 #if HAVE_SELINUX
 static int selinux_fix_fd(
                 int fd,
index 0ba57627615d89b773d51e613d4e6348bd232170..94b699a5ee6ae251abac21dbd2ccb12cccc8ad61 100644 (file)
@@ -2,14 +2,17 @@
 
 #include <arpa/inet.h>
 #include <errno.h>
+#include <linux/net_namespace.h>
 #include <net/if.h>
 #include <string.h>
 
 #include "alloc-util.h"
 #include "errno-util.h"
 #include "extract-word.h"
+#include "fd-util.h"
 #include "log.h"
 #include "memory-util.h"
+#include "namespace-util.h"
 #include "netlink-util.h"
 #include "parse-util.h"
 #include "socket-netlink.h"
@@ -407,3 +410,69 @@ const char *in_addr_full_to_string(struct in_addr_full *a) {
 
         return a->cached_server_string;
 }
+
+int netns_get_nsid(int netnsfd, uint32_t *ret) {
+        _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL;
+        _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
+        _cleanup_close_ int _netns_fd = -EBADF;
+        int r;
+
+        if (netnsfd < 0) {
+                r = namespace_open(
+                                0,
+                                /* pidns_fd= */ NULL,
+                                /* mntns_fd= */ NULL,
+                                &_netns_fd,
+                                /* userns_fd= */ NULL,
+                                /* root_fd= */ NULL);
+                if (r < 0)
+                        return r;
+
+                netnsfd = _netns_fd;
+        }
+
+        r = sd_netlink_open(&rtnl);
+        if (r < 0)
+                return r;
+
+        r = sd_rtnl_message_new_nsid(rtnl, &req, RTM_GETNSID);
+        if (r < 0)
+                return r;
+
+        r = sd_netlink_message_append_s32(req, NETNSA_FD, netnsfd);
+        if (r < 0)
+                return r;
+
+        r = sd_netlink_call(rtnl, req, 0, &reply);
+        if (r < 0)
+                return r;
+
+        for (sd_netlink_message *m = reply; m; m = sd_netlink_message_next(m)) {
+                uint16_t type;
+
+                r = sd_netlink_message_get_errno(m);
+                if (r < 0)
+                        return r;
+
+                r = sd_netlink_message_get_type(m, &type);
+                if (r < 0)
+                        return r;
+                if (type != RTM_NEWNSID)
+                        continue;
+
+                uint32_t u;
+                r = sd_netlink_message_read_u32(m, NETNSA_NSID, &u);
+                if (r < 0)
+                        return r;
+
+                if (u == UINT32_MAX) /* no NSID assigned yet */
+                        return -ENODATA;
+
+                if (ret)
+                        *ret = u;
+
+                return 0;
+        }
+
+        return -ENXIO;
+}
index 6256a831bac3fefb09cd00e440ca16776b971549..2c06fbe3a7d56fa3dc78a3b90a83a2a7c5ab2330 100644 (file)
@@ -42,3 +42,5 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(struct in_addr_full*, in_addr_full_free);
 int in_addr_full_new(int family, const union in_addr_union *a, uint16_t port, int ifindex, const char *server_name, struct in_addr_full **ret);
 int in_addr_full_new_from_string(const char *s, struct in_addr_full **ret);
 const char *in_addr_full_to_string(struct in_addr_full *a);
+
+int netns_get_nsid(int netnsfd, uint32_t *ret);
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..770a1e2b1429322566620a22c40e8d4ccc0dc838 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);
 
@@ -4196,6 +4196,11 @@ int tpm2_tpm2b_public_to_openssl_pkey(const TPM2B_PUBLIC *public, EVP_PKEY **ret
         }
 }
 
+/* Be careful before changing anything in this function, as the TPM key "name" is calculated using the entire
+ * TPMT_PUBLIC (after marshalling), and that "name" is used (for example) to calculate the policy hash for
+ * the Authorize policy. So we must ensure this conversion of a PEM to TPM2B_PUBLIC does not change the
+ * "name", because it would break unsealing of previously-sealed objects that used (for example)
+ * tpm2_calculate_policy_authorize(). See bug #30546. */
 int tpm2_tpm2b_public_from_openssl_pkey(const EVP_PKEY *pkey, TPM2B_PUBLIC *ret) {
         int key_id, r;
 
@@ -4274,8 +4279,11 @@ int tpm2_tpm2b_public_from_openssl_pkey(const EVP_PKEY *pkey, TPM2B_PUBLIC *ret)
                 uint32_t exponent = 0;
                 memcpy(&exponent, e, e_size);
                 exponent = be32toh(exponent) >> (32 - e_size * 8);
-                if (exponent == TPM2_RSA_DEFAULT_EXPONENT)
-                        exponent = 0;
+
+                /* TPM specification Part 2 ("Structures") section for TPMS_RSA_PARAMS states "An exponent of
+                 * zero indicates that the exponent is the default of 2^16 + 1". However, we have no reason
+                 * to special case it in our PEM->TPM2B_PUBLIC conversion, and doing so could break backwards
+                 * compatibility, so even if it is the "default" value of 0x10001, we do not set it to 0. */
                 public.parameters.rsaDetail.exponent = exponent;
 
                 break;
@@ -5007,7 +5015,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 +5052,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 +5075,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 +5111,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 +5140,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 +5150,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 +5164,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 +5189,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 +5218,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 +5262,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 +5300,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 +5344,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 +5396,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 +5431,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 +5444,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 +5501,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 +5545,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,11 +5557,25 @@ 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) {
+#if HAVE_OPENSSL
+                        if (iovec_is_set(pubkey) &&
+                            pubkey_tpm2b.publicArea.type == TPM2_ALG_RSA &&
+                            pubkey_tpm2b.publicArea.parameters.rsaDetail.exponent == TPM2_RSA_DEFAULT_EXPONENT) {
+                                /* Due to bug #30546, if using RSA pubkey with the default exponent, we may
+                                 * need to set the exponent to the TPM special-case value of 0 and retry. */
+                                log_debug("Policy hash mismatch, retrying with RSA pubkey exponent set to 0.");
+                                pubkey_tpm2b.publicArea.parameters.rsaDetail.exponent = 0;
+                                continue;
+                        } else
+#endif
                                 return log_debug_errno(SYNTHETIC_ERRNO(EPERM),
                                                        "Current policy digest does not match stored policy digest, cancelling "
                                                        "TPM2 authentication attempt.");
+                }
 
                 log_debug("Unsealing HMAC key.");
 
@@ -5589,17 +5594,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 +6963,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 +6977,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 +7002,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 +7027,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 +7101,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 +7109,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 +7131,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 +7145,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 +7153,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 +7165,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 cf28ba864796d20704b9b70cd567e3851a73071d..7a86289d166df9ca0a33b2d24eb33e4d6564b42b 100644 (file)
 #include "udev-util.h"
 #include "utf8.h"
 
-int udev_set_max_log_level(char *str) {
-        size_t n;
+int udev_parse_config_full(const ConfigTableItem config_table[]) {
+        int r;
 
-        /* This may modify input string. */
+        assert(config_table);
 
-        if (isempty(str))
+        r = config_parse_config_file_full(
+                        "udev.conf",
+                        "udev",
+                        /* sections = */ NULL,
+                        config_item_table_lookup,
+                        config_table,
+                        CONFIG_PARSE_WARN,
+                        /* userdata = */ NULL);
+        if (r == -ENOENT)
                 return 0;
-
-        /* unquote */
-        n = strlen(str);
-        if (n >= 2 &&
-            ((str[0] == '"' && str[n - 1] == '"') ||
-             (str[0] == '\'' && str[n - 1] == '\''))) {
-                str[n - 1] = '\0';
-                str++;
-        }
-
-        /* we set the udev log level here explicitly, this is supposed
-         * to regulate the code in libudev/ and udev/. */
-        return log_set_max_level_from_string(str);
+        return r;
 }
 
 int udev_parse_config(void) {
-        _cleanup_free_ char *log_val = NULL;
-        int r;
+        int r, log_val = -1;
+        const ConfigTableItem config_table[] = {
+                { NULL, "udev_log", config_parse_log_level, 0, &log_val },
+                {}
+        };
 
-        r = parse_env_file(NULL, "/etc/udev/udev.conf",
-                           "udev_log", &log_val);
-        if (r == -ENOENT)
-                return 0;
+        r = udev_parse_config_full(config_table);
         if (r < 0)
                 return r;
 
-        r = udev_set_max_log_level(log_val);
-        if (r < 0)
-                log_syntax(NULL, LOG_WARNING, "/etc/udev/udev.conf", 0, r,
-                           "Failed to set udev log level '%s', ignoring: %m", log_val);
+        if (log_val >= 0)
+                log_set_max_level(log_val);
 
         return 0;
 }
index 651d335b9698611f31a3c696032d64dc3f1d8a4f..5f49e87116179db7989374a1fec74491de4adc3f 100644 (file)
@@ -3,10 +3,11 @@
 
 #include "sd-device.h"
 
+#include "conf-parser.h"
 #include "hashmap.h"
 #include "time-util.h"
 
-int udev_set_max_log_level(char *str);
+int udev_parse_config_full(const ConfigTableItem config_table[]);
 int udev_parse_config(void);
 
 int device_wait_for_initialization(sd_device *device, const char *subsystem, usec_t timeout_usec, sd_device **ret);
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;
 
index 858b493d2f60c0dbb580a06a9ee02bb8b0064ab2..ad0c66d9695ab9bec08c14cfefa3661528c19f90 100644 (file)
@@ -17,6 +17,9 @@ enum {
         _COLOR_MAX,
 };
 
+#define varlink_idl_log(error, format, ...) log_debug_errno(error, "Varlink-IDL: " format, ##__VA_ARGS__)
+#define varlink_idl_log_full(level, error, format, ...) log_full_errno(level, error, "Varlink-IDL: " format, ##__VA_ARGS__)
+
 static int varlink_idl_format_all_fields(FILE *f, const VarlinkSymbol *symbol, VarlinkFieldDirection direction, const char *indent, const char *const colors[static _COLOR_MAX]);
 
 static int varlink_idl_format_enum_values(
@@ -512,7 +515,7 @@ static int varlink_idl_subparse_token(
 
                 l = token_match(*p, allowed_delimiters, allowed_chars);
                 if (l == 0)
-                        return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "Couldn't find token of allowed chars '%s' or allowed delimiters '%s'.", strempty(allowed_chars), strempty(allowed_delimiters));
+                        return varlink_idl_log(SYNTHETIC_ERRNO(EBADMSG), "Couldn't find token of allowed chars '%s' or allowed delimiters '%s'.", strempty(allowed_chars), strempty(allowed_delimiters));
         }
 
         t = strndup(*p, l);
@@ -662,7 +665,7 @@ static int varlink_idl_subparse_field_type(
                 if (r < 0)
                         return r;
                 if (!token)
-                        return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "%u:%u: Premature EOF.", *line, *column);
+                        return varlink_idl_log(SYNTHETIC_ERRNO(EBADMSG), "%u:%u: Premature EOF.", *line, *column);
 
                 field->named_type = TAKE_PTR(token);
                 field->field_type = VARLINK_NAMED_TYPE;
@@ -704,7 +707,7 @@ static int varlink_idl_subparse_struct_or_enum(
         assert(n_fields);
 
         if (depth > DEPTH_MAX)
-                return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "%u:%u: Maximum nesting depth reached (%u).", *line, *column, DEPTH_MAX);
+                return varlink_idl_log(SYNTHETIC_ERRNO(EBADMSG), "%u:%u: Maximum nesting depth reached (%u).", *line, *column, DEPTH_MAX);
 
         while (state != STATE_DONE) {
                 _cleanup_free_ char *token = NULL;
@@ -723,9 +726,9 @@ static int varlink_idl_subparse_struct_or_enum(
 
                 case STATE_OPEN:
                         if (!token)
-                                return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "%u:%u: Premature EOF.", *line, *column);
+                                return varlink_idl_log(SYNTHETIC_ERRNO(EBADMSG), "%u:%u: Premature EOF.", *line, *column);
                         if (!streq(token, "("))
-                                return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "%u:%u: Unexpected token '%s'.", *line, *column, token);
+                                return varlink_idl_log(SYNTHETIC_ERRNO(EBADMSG), "%u:%u: Unexpected token '%s'.", *line, *column, token);
 
                         state = STATE_NAME;
                         allowed_delimiters = ")";
@@ -736,7 +739,7 @@ static int varlink_idl_subparse_struct_or_enum(
                         assert(!field_name);
 
                         if (!token)
-                                return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "%u:%u: Premature EOF.", *line, *column);
+                                return varlink_idl_log(SYNTHETIC_ERRNO(EBADMSG), "%u:%u: Premature EOF.", *line, *column);
                         if (streq(token, ")"))
                                 state = STATE_DONE;
                         else {
@@ -752,7 +755,7 @@ static int varlink_idl_subparse_struct_or_enum(
                         assert(field_name);
 
                         if (!token)
-                                return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "%u:%u: Premature EOF.", *line, *column);
+                                return varlink_idl_log(SYNTHETIC_ERRNO(EBADMSG), "%u:%u: Premature EOF.", *line, *column);
 
                         if (streq(token, ":")) {
                                 VarlinkField *field;
@@ -760,7 +763,7 @@ static int varlink_idl_subparse_struct_or_enum(
                                 if ((*symbol)->symbol_type < 0)
                                         (*symbol)->symbol_type = VARLINK_STRUCT_TYPE;
                                 if ((*symbol)->symbol_type == VARLINK_ENUM_TYPE)
-                                        return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "%u:%u: Enum with struct fields, refusing.", *line, *column);
+                                        return varlink_idl_log(SYNTHETIC_ERRNO(EBADMSG), "%u:%u: Enum with struct fields, refusing.", *line, *column);
 
                                 r = varlink_symbol_realloc(symbol, *n_fields + 1);
                                 if (r < 0)
@@ -787,7 +790,7 @@ static int varlink_idl_subparse_struct_or_enum(
                                 if ((*symbol)->symbol_type < 0)
                                         (*symbol)->symbol_type = VARLINK_ENUM_TYPE;
                                 if ((*symbol)->symbol_type != VARLINK_ENUM_TYPE)
-                                        return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "%u:%u: Struct with enum fields, refusing.", *line, *column);
+                                        return varlink_idl_log(SYNTHETIC_ERRNO(EBADMSG), "%u:%u: Struct with enum fields, refusing.", *line, *column);
 
                                 r = varlink_symbol_realloc(symbol, *n_fields + 1);
                                 if (r < 0)
@@ -808,7 +811,7 @@ static int varlink_idl_subparse_struct_or_enum(
                                         state = STATE_DONE;
                                 }
                         } else
-                                return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "%u:%u: Unexpected token '%s'.", *line, *column, token);
+                                return varlink_idl_log(SYNTHETIC_ERRNO(EBADMSG), "%u:%u: Unexpected token '%s'.", *line, *column, token);
 
                         break;
 
@@ -816,7 +819,7 @@ static int varlink_idl_subparse_struct_or_enum(
                         assert(!field_name);
 
                         if (!token)
-                                return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "%u:%u: Premature EOF.", *line, *column);
+                                return varlink_idl_log(SYNTHETIC_ERRNO(EBADMSG), "%u:%u: Premature EOF.", *line, *column);
                         if (streq(token, ",")) {
                                 state = STATE_NAME;
                                 allowed_delimiters = NULL;
@@ -824,7 +827,7 @@ static int varlink_idl_subparse_struct_or_enum(
                         } else if (streq(token, ")"))
                                 state = STATE_DONE;
                         else
-                                return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "%u:%u: Unexpected token '%s'.", *line, *column, token);
+                                return varlink_idl_log(SYNTHETIC_ERRNO(EBADMSG), "%u:%u: Unexpected token '%s'.", *line, *column, token);
                         break;
 
                 default:
@@ -835,7 +838,7 @@ static int varlink_idl_subparse_struct_or_enum(
         /* If we don't know the type of the symbol by now it was an empty () which doesn't allow us to
          * determine if we look at an enum or a struct */
         if ((*symbol)->symbol_type < 0)
-                return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "%u:%u: Ambiguous empty () enum/struct is not permitted.", *line, *column);
+                return varlink_idl_log(SYNTHETIC_ERRNO(EBADMSG), "%u:%u: Ambiguous empty () enum/struct is not permitted.", *line, *column);
 
         return 0;
 }
@@ -854,14 +857,14 @@ static int varlink_idl_resolve_symbol_types(VarlinkInterface *interface, Varlink
                         continue;
 
                 if (!field->named_type)
-                        return log_debug_errno(SYNTHETIC_ERRNO(ENETUNREACH), "Named type field lacking a type name.");
+                        return varlink_idl_log(SYNTHETIC_ERRNO(ENETUNREACH), "Named type field lacking a type name.");
 
                 found = varlink_idl_find_symbol(interface, _VARLINK_SYMBOL_TYPE_INVALID, field->named_type);
                 if (!found)
-                        return log_debug_errno(SYNTHETIC_ERRNO(ENETUNREACH), "Failed to find type '%s'.", field->named_type);
+                        return varlink_idl_log(SYNTHETIC_ERRNO(ENETUNREACH), "Failed to find type '%s'.", field->named_type);
 
                 if (!IN_SET(found->symbol_type, VARLINK_STRUCT_TYPE, VARLINK_ENUM_TYPE))
-                        return log_debug_errno(SYNTHETIC_ERRNO(ENETUNREACH), "Symbol '%s' is referenced as type but is not a type.", field->named_type);
+                        return varlink_idl_log(SYNTHETIC_ERRNO(ENETUNREACH), "Symbol '%s' is referenced as type but is not a type.", field->named_type);
 
                 field->symbol = found;
         }
@@ -932,7 +935,7 @@ int varlink_idl_parse(
 
                 case STATE_PRE_INTERFACE:
                         if (!token)
-                                return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "%u:%u: Premature EOF.", *line, *column);
+                                return varlink_idl_log(SYNTHETIC_ERRNO(EBADMSG), "%u:%u: Premature EOF.", *line, *column);
                         if (streq(token, "#")) {
                                 r = varlink_idl_subparse_comment(&text, line, column);
                                 if (r < 0)
@@ -942,7 +945,7 @@ int varlink_idl_parse(
                                 allowed_delimiters = NULL;
                                 allowed_chars = VALID_CHARS_INTERFACE_NAME;
                         } else
-                                return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "%u:%u: Unexpected token '%s'.", *line, *column, token);
+                                return varlink_idl_log(SYNTHETIC_ERRNO(EBADMSG), "%u:%u: Unexpected token '%s'.", *line, *column, token);
                         break;
 
                 case STATE_INTERFACE:
@@ -950,7 +953,7 @@ int varlink_idl_parse(
                         assert(n_symbols == 0);
 
                         if (!token)
-                                return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "%u:%u: Premature EOF.", *line, *column);
+                                return varlink_idl_log(SYNTHETIC_ERRNO(EBADMSG), "%u:%u: Premature EOF.", *line, *column);
 
                         r = varlink_interface_realloc(&interface, n_symbols);
                         if (r < 0)
@@ -982,7 +985,7 @@ int varlink_idl_parse(
                                 state = STATE_ERROR;
                                 allowed_chars = VALID_CHARS_IDENTIFIER;
                         } else
-                                return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "%u:%u: Unexpected token '%s'.", *line, *column, token);
+                                return varlink_idl_log(SYNTHETIC_ERRNO(EBADMSG), "%u:%u: Unexpected token '%s'.", *line, *column, token);
 
                         break;
 
@@ -991,7 +994,7 @@ int varlink_idl_parse(
                         n_fields = 0;
 
                         if (!token)
-                                return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "%u:%u: Premature EOF.", *line, *column);
+                                return varlink_idl_log(SYNTHETIC_ERRNO(EBADMSG), "%u:%u: Premature EOF.", *line, *column);
 
                         r = varlink_symbol_realloc(&symbol, n_fields);
                         if (r < 0)
@@ -1012,10 +1015,10 @@ int varlink_idl_parse(
                         assert(symbol);
 
                         if (!token)
-                                return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "%u:%u: Premature EOF.", *line, *column);
+                                return varlink_idl_log(SYNTHETIC_ERRNO(EBADMSG), "%u:%u: Premature EOF.", *line, *column);
 
                         if (!streq(token, "->"))
-                                return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "%u:%u: Unexpected token '%s'.", *line, *column, token);
+                                return varlink_idl_log(SYNTHETIC_ERRNO(EBADMSG), "%u:%u: Unexpected token '%s'.", *line, *column, token);
 
                         r = varlink_idl_subparse_struct_or_enum(&text, line, column, &symbol, &n_fields, VARLINK_OUTPUT, 0);
                         if (r < 0)
@@ -1036,7 +1039,7 @@ int varlink_idl_parse(
                         n_fields = 0;
 
                         if (!token)
-                                return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "%u:%u: Premature EOF.", *line, *column);
+                                return varlink_idl_log(SYNTHETIC_ERRNO(EBADMSG), "%u:%u: Premature EOF.", *line, *column);
 
                         r = varlink_symbol_realloc(&symbol, n_fields);
                         if (r < 0)
@@ -1064,7 +1067,7 @@ int varlink_idl_parse(
                         n_fields = 0;
 
                         if (!token)
-                                return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "%u:%u: Premature EOF.", *line, *column);
+                                return varlink_idl_log(SYNTHETIC_ERRNO(EBADMSG), "%u:%u: Premature EOF.", *line, *column);
 
                         r = varlink_symbol_realloc(&symbol, n_fields);
                         if (r < 0)
@@ -1213,48 +1216,48 @@ static int varlink_idl_field_consistent(
         symbol_name = symbol->name ?: "<anonymous>";
 
         if (field->field_type <= 0 || field->field_type >= _VARLINK_FIELD_TYPE_MAX)
-                return log_full_errno(level, SYNTHETIC_ERRNO(EUCLEAN), "Field type for '%s' in symbol '%s' is not valid, refusing.", field->name, symbol_name);
+                return varlink_idl_log_full(level, SYNTHETIC_ERRNO(EUCLEAN), "Field type for '%s' in symbol '%s' is not valid, refusing.", field->name, symbol_name);
 
         if (field->field_type == VARLINK_ENUM_VALUE) {
 
                 if (symbol->symbol_type != VARLINK_ENUM_TYPE)
-                        return log_full_errno(level, SYNTHETIC_ERRNO(EUCLEAN), "Enum field type for '%s' in non-enum symbol '%s', refusing.", field->name, symbol_name);
+                        return varlink_idl_log_full(level, SYNTHETIC_ERRNO(EUCLEAN), "Enum field type for '%s' in non-enum symbol '%s', refusing.", field->name, symbol_name);
 
                 if (field->field_flags != 0)
-                        return log_full_errno(level, SYNTHETIC_ERRNO(EUCLEAN), "Enum field '%s' in symbol '%s' has non-zero flags set, refusing.", field->name, symbol_name);
+                        return varlink_idl_log_full(level, SYNTHETIC_ERRNO(EUCLEAN), "Enum field '%s' in symbol '%s' has non-zero flags set, refusing.", field->name, symbol_name);
         } else {
                 if (symbol->symbol_type == VARLINK_ENUM_TYPE)
-                        return log_full_errno(level, SYNTHETIC_ERRNO(EUCLEAN), "Non-enum field type for '%s' in enum symbol '%s', refusing.", field->name, symbol_name);
+                        return varlink_idl_log_full(level, SYNTHETIC_ERRNO(EUCLEAN), "Non-enum field type for '%s' in enum symbol '%s', refusing.", field->name, symbol_name);
 
                 if (!IN_SET(field->field_flags & ~VARLINK_NULLABLE, 0, VARLINK_ARRAY, VARLINK_MAP))
-                        return log_full_errno(level, SYNTHETIC_ERRNO(EUCLEAN), "Flags of field '%s' in symbol '%s' is invalid, refusing.", field->name, symbol_name);
+                        return varlink_idl_log_full(level, SYNTHETIC_ERRNO(EUCLEAN), "Flags of field '%s' in symbol '%s' is invalid, refusing.", field->name, symbol_name);
         }
 
         if (symbol->symbol_type != VARLINK_METHOD) {
                 if (field->field_direction != VARLINK_REGULAR)
-                        return log_full_errno(level, SYNTHETIC_ERRNO(EUCLEAN), "Direction of '%s' in non-method symbol '%s' not regular, refusing.", field->name, symbol_name);
+                        return varlink_idl_log_full(level, SYNTHETIC_ERRNO(EUCLEAN), "Direction of '%s' in non-method symbol '%s' not regular, refusing.", field->name, symbol_name);
         } else {
                 if (!IN_SET(field->field_direction, VARLINK_INPUT, VARLINK_OUTPUT))
-                        return log_full_errno(level, SYNTHETIC_ERRNO(EUCLEAN), "Direction of '%s' in method symbol '%s' is not input or output, refusing.", field->name, symbol_name);
+                        return varlink_idl_log_full(level, SYNTHETIC_ERRNO(EUCLEAN), "Direction of '%s' in method symbol '%s' is not input or output, refusing.", field->name, symbol_name);
         }
 
         if (field->symbol) {
                 if (!IN_SET(field->field_type, VARLINK_STRUCT, VARLINK_ENUM, VARLINK_NAMED_TYPE))
-                        return log_full_errno(level, SYNTHETIC_ERRNO(EUCLEAN), "Target symbol for field '%s' in symbol '%s' defined for elemental field, refusing.", field->name, symbol_name);
+                        return varlink_idl_log_full(level, SYNTHETIC_ERRNO(EUCLEAN), "Target symbol for field '%s' in symbol '%s' defined for elemental field, refusing.", field->name, symbol_name);
 
                 if (field->field_type == VARLINK_NAMED_TYPE) {
                         const VarlinkSymbol *found;
 
                         if (!field->symbol->name || !field->named_type || !streq(field->symbol->name, field->named_type))
-                                return log_full_errno(level, SYNTHETIC_ERRNO(EUCLEAN), "Resolved symbol name and named type of field '%s' in symbol '%s' do do not match, refusing.", field->name, symbol_name);
+                                return varlink_idl_log_full(level, SYNTHETIC_ERRNO(EUCLEAN), "Resolved symbol name and named type of field '%s' in symbol '%s' do do not match, refusing.", field->name, symbol_name);
 
                         /* If this is a named type, then check if it's properly part of the interface */
                         found = varlink_idl_find_symbol(interface, _VARLINK_SYMBOL_TYPE_INVALID, field->symbol->name);
                         if (!found)
-                                return log_full_errno(level, SYNTHETIC_ERRNO(EUCLEAN), "Resolved symbol of named type of field '%s' in symbol '%s' is not part of the interface, refusing.", field->name, symbol_name);
+                                return varlink_idl_log_full(level, SYNTHETIC_ERRNO(EUCLEAN), "Resolved symbol of named type of field '%s' in symbol '%s' is not part of the interface, refusing.", field->name, symbol_name);
 
                         if (!IN_SET(found->symbol_type, VARLINK_ENUM_TYPE, VARLINK_STRUCT_TYPE))
-                                return log_full_errno(level, SYNTHETIC_ERRNO(EUCLEAN), "Resolved symbol of named type of field '%s' in symbol '%s' is not a type, refusing.", field->name, symbol_name);
+                                return varlink_idl_log_full(level, SYNTHETIC_ERRNO(EUCLEAN), "Resolved symbol of named type of field '%s' in symbol '%s' is not a type, refusing.", field->name, symbol_name);
                 } else {
                         /* If this is an anonymous type, then we recursively check if it's consistent, since
                          * it's not part of the interface, and hence we won't validate it from there. */
@@ -1266,18 +1269,18 @@ static int varlink_idl_field_consistent(
 
         } else {
                 if (IN_SET(field->field_type, VARLINK_STRUCT, VARLINK_ENUM, VARLINK_NAMED_TYPE))
-                        return log_full_errno(level, SYNTHETIC_ERRNO(EUCLEAN), "No target symbol for field '%s' in symbol '%s' defined for elemental field, refusing.", field->name, symbol_name);
+                        return varlink_idl_log_full(level, SYNTHETIC_ERRNO(EUCLEAN), "No target symbol for field '%s' in symbol '%s' defined for elemental field, refusing.", field->name, symbol_name);
 
                 if (field->named_type)
-                        return log_full_errno(level, SYNTHETIC_ERRNO(EUCLEAN), "Unresolved symbol in field '%s' in symbol '%s', refusing.", field->name, symbol_name);
+                        return varlink_idl_log_full(level, SYNTHETIC_ERRNO(EUCLEAN), "Unresolved symbol in field '%s' in symbol '%s', refusing.", field->name, symbol_name);
         }
 
         if (field->named_type) {
                 if (field->field_type != VARLINK_NAMED_TYPE)
-                        return log_full_errno(level, SYNTHETIC_ERRNO(EUCLEAN), "Named type set for field '%s' in symbol '%s' but not a named type field, refusing.", field->name, symbol_name);
+                        return varlink_idl_log_full(level, SYNTHETIC_ERRNO(EUCLEAN), "Named type set for field '%s' in symbol '%s' but not a named type field, refusing.", field->name, symbol_name);
         } else {
                 if (field->field_type == VARLINK_NAMED_TYPE)
-                        return log_full_errno(level, SYNTHETIC_ERRNO(EUCLEAN), "No named type set for field '%s' in symbol '%s' but field is a named type field, refusing.", field->name, symbol_name);
+                        return varlink_idl_log_full(level, SYNTHETIC_ERRNO(EUCLEAN), "No named type set for field '%s' in symbol '%s' but field is a named type field, refusing.", field->name, symbol_name);
         }
 
         return 0;
@@ -1304,19 +1307,19 @@ static int varlink_idl_symbol_consistent(
         symbol_name = symbol->name ?: "<anonymous>";
 
         if (symbol->symbol_type < 0 || symbol->symbol_type >= _VARLINK_SYMBOL_TYPE_MAX)
-                return log_full_errno(level, SYNTHETIC_ERRNO(EUCLEAN), "Symbol type for '%s' is not valid, refusing.", symbol_name);
+                return varlink_idl_log_full(level, SYNTHETIC_ERRNO(EUCLEAN), "Symbol type for '%s' is not valid, refusing.", symbol_name);
 
         if (IN_SET(symbol->symbol_type, VARLINK_STRUCT_TYPE, VARLINK_ENUM_TYPE) && varlink_symbol_is_empty(symbol))
-                return log_full_errno(level, SYNTHETIC_ERRNO(EUCLEAN), "Symbol '%s' is empty, refusing.", symbol_name);
+                return varlink_idl_log_full(level, SYNTHETIC_ERRNO(EUCLEAN), "Symbol '%s' is empty, refusing.", symbol_name);
 
         for (const VarlinkField *field = symbol->fields; field->field_type != _VARLINK_FIELD_TYPE_END_MARKER; field++) {
                 Set **name_set = field->field_direction == VARLINK_OUTPUT ? &output_set : &input_set; /* for the method case we need two separate sets, otherwise we use the same */
 
                 if (!varlink_idl_field_name_is_valid(field->name))
-                        return log_full_errno(level, SYNTHETIC_ERRNO(EUCLEAN), "Field name '%s' in symbol '%s' not valid, refusing.", field->name, symbol_name);
+                        return varlink_idl_log_full(level, SYNTHETIC_ERRNO(EUCLEAN), "Field name '%s' in symbol '%s' not valid, refusing.", field->name, symbol_name);
 
                 if (set_contains(*name_set, field->name))
-                        return log_full_errno(level, SYNTHETIC_ERRNO(ENOTUNIQ), "Field '%s' defined twice in symbol '%s', refusing.", field->name, symbol_name);
+                        return varlink_idl_log_full(level, SYNTHETIC_ERRNO(ENOTUNIQ), "Field '%s' defined twice in symbol '%s', refusing.", field->name, symbol_name);
 
                 if (set_ensure_put(name_set, &string_hash_ops, field->name) < 0)
                         return log_oom();
@@ -1336,15 +1339,15 @@ int varlink_idl_consistent(const VarlinkInterface *interface, int level) {
         assert(interface);
 
         if (!varlink_idl_interface_name_is_valid(interface->name))
-                return log_full_errno(level, SYNTHETIC_ERRNO(EUCLEAN), "Interface name '%s' is not valid, refusing.", interface->name);
+                return varlink_idl_log_full(level, SYNTHETIC_ERRNO(EUCLEAN), "Interface name '%s' is not valid, refusing.", interface->name);
 
         for (const VarlinkSymbol *const *symbol = interface->symbols; *symbol; symbol++) {
 
                 if (!varlink_idl_symbol_name_is_valid((*symbol)->name))
-                        return log_full_errno(level, SYNTHETIC_ERRNO(EUCLEAN), "Symbol name '%s' is not valid, refusing.", strempty((*symbol)->name));
+                        return varlink_idl_log_full(level, SYNTHETIC_ERRNO(EUCLEAN), "Symbol name '%s' is not valid, refusing.", strempty((*symbol)->name));
 
                 if (set_contains(name_set, (*symbol)->name))
-                        return log_full_errno(level, SYNTHETIC_ERRNO(ENOTUNIQ), "Symbol '%s' defined twice in interface, refusing.", (*symbol)->name);
+                        return varlink_idl_log_full(level, SYNTHETIC_ERRNO(ENOTUNIQ), "Symbol '%s' defined twice in interface, refusing.", (*symbol)->name);
 
                 if (set_ensure_put(&name_set, &string_hash_ops, (*symbol)->name) < 0)
                         return log_oom();
@@ -1371,31 +1374,31 @@ static int varlink_idl_validate_field_element_type(const VarlinkField *field, Js
 
         case VARLINK_BOOL:
                 if (!json_variant_is_boolean(v))
-                        return log_debug_errno(SYNTHETIC_ERRNO(EMEDIUMTYPE), "Field '%s' should be a bool, but it is not, refusing.", strna(field->name));
+                        return varlink_idl_log(SYNTHETIC_ERRNO(EMEDIUMTYPE), "Field '%s' should be a bool, but it is not, refusing.", strna(field->name));
 
                 break;
 
         case VARLINK_INT:
                 if (!json_variant_is_integer(v) && !json_variant_is_unsigned(v))
-                        return log_debug_errno(SYNTHETIC_ERRNO(EMEDIUMTYPE), "Field '%s' should be an int, but it is not, refusing.", strna(field->name));
+                        return varlink_idl_log(SYNTHETIC_ERRNO(EMEDIUMTYPE), "Field '%s' should be an int, but it is not, refusing.", strna(field->name));
 
                 break;
 
         case VARLINK_FLOAT:
                 if (!json_variant_is_number(v))
-                        return log_debug_errno(SYNTHETIC_ERRNO(EMEDIUMTYPE), "Field '%s' should be a float, but it is not, refusing.", strna(field->name));
+                        return varlink_idl_log(SYNTHETIC_ERRNO(EMEDIUMTYPE), "Field '%s' should be a float, but it is not, refusing.", strna(field->name));
 
                 break;
 
         case VARLINK_STRING:
                 if (!json_variant_is_string(v))
-                        return log_debug_errno(SYNTHETIC_ERRNO(EMEDIUMTYPE), "Field '%s' should be a string, but it is not, refusing.", strna(field->name));
+                        return varlink_idl_log(SYNTHETIC_ERRNO(EMEDIUMTYPE), "Field '%s' should be a string, but it is not, refusing.", strna(field->name));
 
                 break;
 
         case VARLINK_OBJECT:
                 if (!json_variant_is_object(v))
-                        return log_debug_errno(SYNTHETIC_ERRNO(EMEDIUMTYPE), "Field '%s' should be an object, but it is not, refusing.", strna(field->name));
+                        return varlink_idl_log(SYNTHETIC_ERRNO(EMEDIUMTYPE), "Field '%s' should be an object, but it is not, refusing.", strna(field->name));
 
                 break;
 
@@ -1414,13 +1417,13 @@ static int varlink_idl_validate_field(const VarlinkField *field, JsonVariant *v)
         if (!v || json_variant_is_null(v)) {
 
                 if (!FLAGS_SET(field->field_flags, VARLINK_NULLABLE))
-                        return log_debug_errno(SYNTHETIC_ERRNO(ENOANO), "Mandatory field '%s' is null or missing on object, refusing.", strna(field->name));
+                        return varlink_idl_log(SYNTHETIC_ERRNO(ENOANO), "Mandatory field '%s' is null or missing on object, refusing.", strna(field->name));
 
         } else if (FLAGS_SET(field->field_flags, VARLINK_ARRAY)) {
                 JsonVariant *i;
 
                 if (!json_variant_is_array(v))
-                        return log_debug_errno(SYNTHETIC_ERRNO(EMEDIUMTYPE), "Field '%s' should be an array, but it is not, refusing.", strna(field->name));
+                        return varlink_idl_log(SYNTHETIC_ERRNO(EMEDIUMTYPE), "Field '%s' should be an array, but it is not, refusing.", strna(field->name));
 
                 JSON_VARIANT_ARRAY_FOREACH(i, v) {
                         r = varlink_idl_validate_field_element_type(field, i);
@@ -1433,7 +1436,7 @@ static int varlink_idl_validate_field(const VarlinkField *field, JsonVariant *v)
                 JsonVariant *e;
 
                 if (!json_variant_is_object(v))
-                        return log_debug_errno(SYNTHETIC_ERRNO(EMEDIUMTYPE), "Field '%s' should be an object, but it is not, refusing.", strna(field->name));
+                        return varlink_idl_log(SYNTHETIC_ERRNO(EMEDIUMTYPE), "Field '%s' should be an object, but it is not, refusing.", strna(field->name));
 
                 JSON_VARIANT_OBJECT_FOREACH(k, e, v) {
                         r = varlink_idl_validate_field_element_type(field, e);
@@ -1458,7 +1461,7 @@ static int varlink_idl_validate_symbol(const VarlinkSymbol *symbol, JsonVariant
         if (!v) {
                 if (bad_field)
                         *bad_field = NULL;
-                return log_debug_errno(SYNTHETIC_ERRNO(EMEDIUMTYPE), "Null object passed, refusing.");
+                return varlink_idl_log(SYNTHETIC_ERRNO(EMEDIUMTYPE), "Null object passed, refusing.");
         }
 
         switch (symbol->symbol_type) {
@@ -1470,7 +1473,7 @@ static int varlink_idl_validate_symbol(const VarlinkSymbol *symbol, JsonVariant
                 if (!json_variant_is_string(v)) {
                         if (bad_field)
                                 *bad_field = symbol->name;
-                        return log_debug_errno(SYNTHETIC_ERRNO(EMEDIUMTYPE), "Passed non-string to enum field '%s', refusing.", strna(symbol->name));
+                        return varlink_idl_log(SYNTHETIC_ERRNO(EMEDIUMTYPE), "Passed non-string to enum field '%s', refusing.", strna(symbol->name));
                 }
 
                 assert_se(s = json_variant_string(v));
@@ -1488,7 +1491,7 @@ static int varlink_idl_validate_symbol(const VarlinkSymbol *symbol, JsonVariant
                 if (!found) {
                         if (bad_field)
                                 *bad_field = s;
-                        return log_debug_errno(SYNTHETIC_ERRNO(EMEDIUMTYPE), "Passed unrecognized string '%s' to enum field '%s', refusing.", s, strna(symbol->name));
+                        return varlink_idl_log(SYNTHETIC_ERRNO(EMEDIUMTYPE), "Passed unrecognized string '%s' to enum field '%s', refusing.", s, strna(symbol->name));
                 }
 
                 break;
@@ -1500,7 +1503,7 @@ static int varlink_idl_validate_symbol(const VarlinkSymbol *symbol, JsonVariant
                 if (!json_variant_is_object(v)) {
                         if (bad_field)
                                 *bad_field = symbol->name;
-                        return log_debug_errno(SYNTHETIC_ERRNO(EMEDIUMTYPE), "Passed non-object to field '%s', refusing.", strna(symbol->name));
+                        return varlink_idl_log(SYNTHETIC_ERRNO(EMEDIUMTYPE), "Passed non-object to field '%s', refusing.", strna(symbol->name));
                 }
 
                 for (const VarlinkField *field = symbol->fields; field->field_type != _VARLINK_FIELD_TYPE_END_MARKER; field++) {
@@ -1522,7 +1525,7 @@ static int varlink_idl_validate_symbol(const VarlinkSymbol *symbol, JsonVariant
                         if (!varlink_idl_find_field(symbol, name)) {
                                 if (bad_field)
                                         *bad_field = name;
-                                return log_debug_errno(SYNTHETIC_ERRNO(EBUSY), "Field '%s' not defined for object, refusing.", name);
+                                return varlink_idl_log(SYNTHETIC_ERRNO(EBUSY), "Field '%s' not defined for object, refusing.", name);
                         }
                 }
 
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 ec25e26e29591ce8b4994c2bbf5094dc82f3cb71..7477d42de863a54c097e0abe94acd6974920acc9 100644 (file)
@@ -7,11 +7,12 @@ static VARLINK_DEFINE_METHOD(GetStates,
                              VARLINK_DEFINE_OUTPUT(IPv4AddressState, VARLINK_STRING, 0),
                              VARLINK_DEFINE_OUTPUT(IPv6AddressState, VARLINK_STRING, 0),
                              VARLINK_DEFINE_OUTPUT(CarrierState, VARLINK_STRING, 0),
-                             VARLINK_DEFINE_OUTPUT(OnlineState, VARLINK_STRING, 0),
+                             VARLINK_DEFINE_OUTPUT(OnlineState, VARLINK_STRING, VARLINK_NULLABLE),
                              VARLINK_DEFINE_OUTPUT(OperationalState, VARLINK_STRING, 0));
 
 static VARLINK_DEFINE_METHOD(GetNamespaceId,
-                             VARLINK_DEFINE_OUTPUT(NamespaceId, VARLINK_INT, 0));
+                             VARLINK_DEFINE_OUTPUT(NamespaceId, VARLINK_INT, 0),
+                             VARLINK_DEFINE_OUTPUT(NamespaceNSID, VARLINK_INT, VARLINK_NULLABLE));
 
 VARLINK_DEFINE_INTERFACE(
                 io_systemd_Network,
index 303a2ffaacaa8db5dd943707dfbb770c7a4e8046..e46d1975d7bf0997b6386a76ac804a859633e560 100644 (file)
@@ -80,8 +80,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 2b40c7f3bd1fa17fe5bdff20f93078fa8539a9f1..84497c1a06e38cf7f76af61e7dfec9b71dc24e74 100644 (file)
@@ -184,6 +184,7 @@ struct Varlink {
         bool allow_fd_passing_output:1;
 
         bool output_buffer_sensitive:1; /* whether to erase the output buffer after writing it to the socket */
+        bool input_sensitive:1; /* Whether incoming messages might be sensitive */
 
         int af; /* address family if socket; AF_UNSPEC if not socket; negative if not known */
 
@@ -450,6 +451,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,
@@ -507,39 +512,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.");
@@ -552,7 +638,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);
@@ -618,7 +704,7 @@ static void varlink_clear(Varlink *v) {
 
         varlink_clear_current(v);
 
-        v->input_buffer = mfree(v->input_buffer);
+        v->input_buffer = v->input_sensitive ? erase_and_free(v->input_buffer) : mfree(v->input_buffer);
         v->output_buffer = v->output_buffer_sensitive ? erase_and_free(v->output_buffer) : mfree(v->output_buffer);
 
         v->input_control_buffer = mfree(v->input_control_buffer);
@@ -937,7 +1023,8 @@ static int varlink_read(Varlink *v) {
 }
 
 static int varlink_parse_message(Varlink *v) {
-        const char *e, *begin;
+        const char *e;
+        char *begin;
         size_t sz;
         int r;
 
@@ -962,6 +1049,8 @@ static int varlink_parse_message(Varlink *v) {
         sz = e - begin + 1;
 
         r = json_parse(begin, 0, &v->current, NULL, NULL);
+        if (v->input_sensitive)
+                explicit_bzero_safe(begin, sz);
         if (r < 0) {
                 /* If we encounter a parse failure flush all data. We cannot possibly recover from this,
                  * hence drop all buffered data now. */
@@ -969,6 +1058,24 @@ static int varlink_parse_message(Varlink *v) {
                 return varlink_log_errno(v, r, "Failed to parse JSON: %m");
         }
 
+        if (v->input_sensitive) {
+                /* Mark the parameters subfield as sensitive right-away, if that's requested */
+                JsonVariant *parameters = json_variant_by_key(v->current, "parameters");
+                if (parameters)
+                        json_variant_sensitive(parameters);
+        }
+
+        if (DEBUG_LOGGING) {
+                _cleanup_(erase_and_freep) char *censored_text = NULL;
+
+                /* Suppress sensitive fields in the debug output */
+                r = json_variant_format(v->current, /* flags= */ JSON_FORMAT_CENSOR_SENSITIVE, &censored_text);
+                if (r < 0)
+                        return r;
+
+                varlink_log(v, "Received message: %s", censored_text);
+        }
+
         v->input_buffer_size -= sz;
 
         if (v->input_buffer_size == 0)
@@ -1764,56 +1871,60 @@ 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;
+        int sz, r;
 
         assert(v);
         assert(m);
 
-        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');
+        sz = json_variant_format(m, /* flags= */ 0, &text);
+        if (sz < 0)
+                return sz;
+        assert(text[sz] == '\0');
 
-        if (v->output_buffer_size + r + 1 > VARLINK_BUFFER_MAX)
+        if (v->output_buffer_size + sz + 1 > VARLINK_BUFFER_MAX)
                 return -ENOBUFS;
 
-        varlink_log(v, "Sending message: %s", sensitive ? "<sensitive data>" : text);
+        if (DEBUG_LOGGING) {
+                _cleanup_(erase_and_freep) char *censored_text = NULL;
+
+                /* Suppress sensitive fields in the debug output */
+                r = json_variant_format(m, /* flags= */ JSON_FORMAT_CENSOR_SENSITIVE, &censored_text);
+                if (r < 0)
+                        return r;
+
+                varlink_log(v, "Sending message: %s", censored_text);
+        }
 
         if (v->output_buffer_size == 0) {
 
                 free_and_replace(v->output_buffer, text);
 
-                v->output_buffer_size = r + 1;
+                v->output_buffer_size = sz + 1;
                 v->output_buffer_index = 0;
 
         } else if (v->output_buffer_index == 0) {
 
-                if (!GREEDY_REALLOC(v->output_buffer, v->output_buffer_size + r + 1))
+                if (!GREEDY_REALLOC(v->output_buffer, v->output_buffer_size + sz + 1))
                         return -ENOMEM;
 
-                memcpy(v->output_buffer + v->output_buffer_size, text, r + 1);
-                v->output_buffer_size += r + 1;
+                memcpy(v->output_buffer + v->output_buffer_size, text, sz + 1);
+                v->output_buffer_size += sz + 1;
         } else {
                 char *n;
-                const size_t new_size = v->output_buffer_size + r + 1;
+                const size_t new_size = v->output_buffer_size + sz + 1;
 
                 n = new(char, new_size);
                 if (!n)
                         return -ENOMEM;
 
-                memcpy(mempcpy(n, v->output_buffer + v->output_buffer_index, v->output_buffer_size), text, r + 1);
+                memcpy(mempcpy(n, v->output_buffer + v->output_buffer_index, v->output_buffer_size), text, sz + 1);
 
                 free_and_replace(v->output_buffer, n);
                 v->output_buffer_size = new_size;
                 v->output_buffer_index = 0;
         }
 
-        if (sensitive)
+        if (json_variant_is_sensitive_recursive(m))
                 v->output_buffer_sensitive = true; /* Propagate sensitive flag */
         else
                 text = mfree(text); /* No point in the erase_and_free() destructor declared above */
@@ -3012,6 +3123,13 @@ int varlink_set_allow_fd_passing_output(Varlink *v, bool b) {
         return 0;
 }
 
+int varlink_set_input_sensitive(Varlink *v) {
+        assert_return(v, -EINVAL);
+
+        v->input_sensitive = true;
+        return 0;
+}
+
 int varlink_server_new(VarlinkServer **ret, VarlinkServerFlags flags) {
         _cleanup_(varlink_server_unrefp) VarlinkServer *s = NULL;
         int r;
@@ -3240,6 +3358,9 @@ static int connect_callback(sd_event_source *source, int fd, uint32_t revents, v
 
         TAKE_FD(cfd);
 
+        if (FLAGS_SET(ss->server->flags, VARLINK_SERVER_INPUT_SENSITIVE))
+                varlink_set_input_sensitive(v);
+
         if (ss->server->connect_callback) {
                 r = ss->server->connect_callback(ss->server, v, ss->server->userdata);
                 if (r < 0) {
@@ -3392,6 +3513,14 @@ int varlink_server_listen_auto(VarlinkServer *s) {
                 n++;
         }
 
+        /* For debug purposes let's listen on an explicitly specified address */
+        const char *e = secure_getenv("SYSTEMD_VARLINK_LISTEN");
+        if (e) {
+                r = varlink_server_listen_address(s, e, FLAGS_SET(s->flags, VARLINK_SERVER_ROOT_ONLY) ? 0600 : 0666);
+                if (r < 0)
+                        return r;
+        }
+
         return n;
 }
 
@@ -3837,6 +3966,10 @@ int varlink_invocation(VarlinkInvocationFlags flags) {
 
         /* Returns true if this is a "pure" varlink server invocation, i.e. with one fd passed. */
 
+        const char *e = secure_getenv("SYSTEMD_VARLINK_LISTEN"); /* Permit a manual override for testing purposes */
+        if (e)
+                return true;
+
         r = sd_listen_fds_with_names(/* unset_environment= */ false, &names);
         if (r < 0)
                 return r;
index a971762a511c65ed9b8af5c4f80e4e3804886180..418ba49d1a039199e4788588a3540417cb4cc197 100644 (file)
@@ -47,7 +47,8 @@ typedef enum VarlinkServerFlags {
         VARLINK_SERVER_MYSELF_ONLY      = 1 << 1, /* Only accessible by our own UID */
         VARLINK_SERVER_ACCOUNT_UID      = 1 << 2, /* Do per user accounting */
         VARLINK_SERVER_INHERIT_USERDATA = 1 << 3, /* Initialize Varlink connection userdata from VarlinkServer userdata */
-        _VARLINK_SERVER_FLAGS_ALL = (1 << 4) - 1,
+        VARLINK_SERVER_INPUT_SENSITIVE  = 1 << 4, /* Automatically mark al connection input as sensitive */
+        _VARLINK_SERVER_FLAGS_ALL = (1 << 5) - 1,
 } VarlinkServerFlags;
 
 typedef int (*VarlinkMethod)(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata);
@@ -154,6 +155,9 @@ VarlinkServer* varlink_get_server(Varlink *v);
 
 int varlink_set_description(Varlink *v, const char *d);
 
+/* Automatically mark the parameters part of incoming messages as security sensitive */
+int varlink_set_input_sensitive(Varlink *v);
+
 /* Create a varlink server */
 int varlink_server_new(VarlinkServer **ret, VarlinkServerFlags flags);
 VarlinkServer *varlink_server_ref(VarlinkServer *s);
index 4a4d1eb408010e2007e91fd41b468494c43759cc..ab1f4289fcce0ac10249bdae70fc4c5f3f38570e 100644 (file)
@@ -570,8 +570,8 @@ int path_pick(const char *toplevel_path,
                 if (!wildcard)
                         goto bypass; /* Not a pattern, then bypass */
 
-                /* We found the '___' wildcard, hence evertyhing after it is our filter suffix, and
-                 * evertyhing before is our filter basename */
+                /* 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);
 
index 0e0d41e5db4ade1331556e0244ecb84952ef3fdf..591852e986fc27299865eec5c2a83b33f8358497 100644 (file)
@@ -13,10 +13,10 @@ typedef enum PickFlags {
 
 typedef struct PickFilter {
         uint32_t type_mask;           /* A mask of 1U << DT_REG, 1U << DT_DIR, … */
-        const char *basename;         /* Can be overriden by search pattern */
+        const char *basename;         /* Can be overridden by search pattern */
         const char *version;
         Architecture architecture;
-        const char *suffix;           /* Can be overriden by search pattern */
+        const char *suffix;           /* Can be overridden by search pattern */
 } PickFilter;
 
 typedef struct PickResult {
@@ -38,7 +38,7 @@ typedef struct PickResult {
                 .tries_done = UINT_MAX,                 \
         }
 
-#define TAKE_PICK_RESULT(ptr) TAKE_GENERIC(ptr, PickResult, PICK_RESULT_NULL)
+#define TAKE_PICK_RESULT(pick) TAKE_GENERIC(pick, PickResult, PICK_RESULT_NULL)
 
 void pick_result_done(PickResult *p);
 
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 d4e6dcac07265f3266486dd53386c44b3550c42c..052f5606e5368a74e8e98903862524afe6d13b98 100644 (file)
@@ -55,7 +55,7 @@ int wifi_get_interface(sd_netlink *genl, int ifindex, enum nl80211_iftype *ret_i
         if (r < 0)
                 return log_debug_errno(r, "Failed to get NL80211_ATTR_IFTYPE attribute: %m");
 
-        r = sd_netlink_message_read_data_suffix0(reply, NL80211_ATTR_SSID, &len, (void**) &ssid);
+        r = sd_netlink_message_read_data(reply, NL80211_ATTR_SSID, &len, (void**) &ssid);
         if (r < 0 && r != -ENODATA)
                 return log_debug_errno(r, "Failed to get NL80211_ATTR_SSID attribute: %m");
         if (r >= 0) {
diff --git a/src/ssh-generator/20-systemd-ssh-proxy.conf.in b/src/ssh-generator/20-systemd-ssh-proxy.conf.in
new file mode 100644 (file)
index 0000000..b97e0f5
--- /dev/null
@@ -0,0 +1,18 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+#
+# Make sure unix/* and vsock/* can be used to connect to AF_UNIX and AF_VSOCK paths
+#
+Host unix/* vsock/*
+        ProxyCommand {{LIBEXECDIR}}/systemd-ssh-proxy %h %p
+        ProxyUseFdpass yes
+        CheckHostIP no
+
+        # Disable all kinds of host identity checks, since these addresses are generally ephemeral.
+        StrictHostKeyChecking no
+        UserKnownHostsFile /dev/null
+
+# Allow connecting to the local host directly via ".host"
+Host .host
+        ProxyCommand {{LIBEXECDIR}}/systemd-ssh-proxy unix/run/ssh-unix-local/socket %p
+        ProxyUseFdpass yes
+        CheckHostIP no
diff --git a/src/ssh-generator/meson.build b/src/ssh-generator/meson.build
new file mode 100644 (file)
index 0000000..70a706f
--- /dev/null
@@ -0,0 +1,25 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+executables += [
+        generator_template + {
+                'name' : 'systemd-ssh-generator',
+                'sources' : files('ssh-generator.c'),
+        },
+        libexec_template + {
+                'name' : 'systemd-ssh-proxy',
+                'sources' : files('ssh-proxy.c'),
+        },
+]
+
+custom_target(
+        '20-systemd-ssh-proxy.conf',
+        input : '20-systemd-ssh-proxy.conf.in',
+        output : '20-systemd-ssh-proxy.conf',
+        command : [jinja2_cmdline, '@INPUT@', '@OUTPUT@'],
+        install : true,
+        install_dir : libexecdir / 'ssh_config.d')
+
+install_emptydir(sshconfdir)
+
+meson.add_install_script(sh, '-c',
+        ln_s.format(libexecdir / 'ssh_config.d' / '20-systemd-ssh-proxy.conf', sshconfdir / '20-systemd-ssh-proxy.conf'))
diff --git a/src/ssh-generator/ssh-generator.c b/src/ssh-generator/ssh-generator.c
new file mode 100644 (file)
index 0000000..2c4cf5a
--- /dev/null
@@ -0,0 +1,476 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
+
+#include "creds-util.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "generator.h"
+#include "install.h"
+#include "missing_socket.h"
+#include "parse-util.h"
+#include "path-util.h"
+#include "proc-cmdline.h"
+#include "socket-netlink.h"
+#include "socket-util.h"
+#include "special.h"
+#include "virt.h"
+
+/* A small generator binding potentially five or more SSH sockets:
+ *
+ *     1. Listen on AF_VSOCK port 22 if we run in a VM with AF_VSOCK enabled
+ *     2. Listen on AF_UNIX socket /run/host/unix-export/ssh if we run in a container with /run/host/ support
+ *     3. Listen on AF_UNIX socket /run/ssh-unix-local/socket (always)
+ *     4. Listen on any socket specified via kernel command line option systemd.ssh_listen=
+ *     5. Similar, but from system credential ssh.listen
+ *
+ * The first two provide a nice way for hosts to connect to containers and VMs they invoke via the usual SSH
+ * logic, but without waiting for networking or suchlike. The third allows the same for local clients. */
+
+static const char *arg_dest = NULL;
+static bool arg_auto = true;
+static char **arg_listen_extra = NULL;
+
+static int parse_proc_cmdline_item(const char *key, const char *value, void *data) {
+        int r;
+
+        assert(key);
+
+        if (proc_cmdline_key_streq(key, "systemd.ssh_auto")) {
+                r = value ? parse_boolean(value) : 1;
+                if (r < 0)
+                        log_warning_errno(r, "Failed to parse systemd.ssh_auto switch \"%s\", ignoring: %m", value);
+                else
+                        arg_auto = r;
+
+        } else if (proc_cmdline_key_streq(key, "systemd.ssh_listen")) {
+
+                if (proc_cmdline_value_missing(key, value))
+                        return 0;
+
+                SocketAddress sa;
+                r = socket_address_parse(&sa, value);
+                if (r < 0)
+                        log_warning_errno(r, "Failed to parse systemd.ssh_listen= expression, ignoring: %s", value);
+                else {
+                        _cleanup_free_ char *s = NULL;
+                        r = socket_address_print(&sa, &s);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to format socket address: %m");
+
+                        if (strv_consume(&arg_listen_extra, TAKE_PTR(s)) < 0)
+                                return log_oom();
+                }
+        }
+
+        return 0;
+}
+
+static int make_sshd_template_unit(
+                const char *dest,
+                const char *template,
+                const char *sshd_binary,
+                const char *found_sshd_template_service,
+                char **generated_sshd_template_unit) {
+
+        int r;
+
+        assert(dest);
+        assert(template);
+        assert(sshd_binary);
+        assert(generated_sshd_template_unit);
+
+        /* If the system has a suitable template already, symlink it to the name we want to reuse it */
+        if (found_sshd_template_service)
+                return generator_add_symlink(
+                                dest,
+                                template,
+                                /* dep_type= */ NULL,
+                                found_sshd_template_service);
+
+        if (!*generated_sshd_template_unit) {
+                _cleanup_fclose_ FILE *f = NULL;
+
+                r = generator_open_unit_file_full(
+                                dest,
+                                /* source= */ NULL,
+                                "sshd-generated@.service", /* Give this generated unit a generic name, since we want to use it for both AF_UNIX and AF_VSOCK */
+                                &f,
+                                generated_sshd_template_unit,
+                                /* ret_temp_path= */ NULL);
+                if (r < 0)
+                        return r;
+
+                fprintf(f,
+                        "[Unit]\n"
+                        "Description=OpenSSH Per-Connection Server Daemon\n"
+                        "Documentation=man:systemd-ssh-generator(8) man:sshd(8)\n"
+                        "[Service]\n"
+                        "ExecStart=-%s -i\n"
+                        "StandardInput=socket",
+                        sshd_binary);
+
+                r = fflush_and_check(f);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to write sshd template: %m");
+        }
+
+        return generator_add_symlink(
+                        dest,
+                        template,
+                        /* dep_type= */ NULL,
+                        *generated_sshd_template_unit);
+}
+
+static int write_socket_unit(
+                const char *dest,
+                const char *unit,
+                const char *listen_stream,
+                const char *comment) {
+
+        int r;
+
+        assert(dest);
+        assert(unit);
+        assert(listen_stream);
+        assert(comment);
+
+        _cleanup_fclose_ FILE *f = NULL;
+        r = generator_open_unit_file(
+                        dest,
+                        /* source= */ NULL,
+                        unit,
+                        &f);
+        if (r < 0)
+                return r;
+
+        fprintf(f,
+                "[Unit]\n"
+                "Description=OpenSSH Server Socket (systemd-ssh-generator, %s)\n"
+                "Documentation=man:systemd-ssh-generator(8)\n"
+                "\n[Socket]\n"
+                "ListenStream=%s\n"
+                "Accept=yes\n"
+                "PollLimitIntervalSec=30s\n"
+                "PollLimitBurst=50\n",
+                comment,
+                listen_stream);
+
+        r = fflush_and_check(f);
+        if (r < 0)
+                return log_error_errno(r, "Failed to write %s SSH socket unit: %m", comment);
+
+        r = generator_add_symlink(
+                        dest,
+                        SPECIAL_SOCKETS_TARGET,
+                        "wants",
+                        unit);
+        if (r < 0)
+                return r;
+
+        return 0;
+}
+
+static int add_vsock_socket(
+                const char *dest,
+                const char *sshd_binary,
+                const char *found_sshd_template_unit,
+                char **generated_sshd_template_unit) {
+
+        int r;
+
+        assert(dest);
+        assert(generated_sshd_template_unit);
+
+        Virtualization v = detect_vm();
+        if (v < 0)
+                return log_error_errno(v, "Failed to detect if we run in a VM: %m");
+        if (v == VIRTUALIZATION_NONE) {
+                log_debug("Not running in a VM, not listening on AF_VSOCK.");
+                return 0;
+        }
+
+        _cleanup_close_ int vsock_fd = socket(AF_VSOCK, SOCK_STREAM|SOCK_CLOEXEC, 0);
+        if (vsock_fd < 0) {
+                if (ERRNO_IS_NOT_SUPPORTED(errno)) {
+                        log_debug("Not creating AF_VSOCK ssh listener, since AF_VSOCK is not available.");
+                        return 0;
+                }
+
+                return log_error_errno(errno, "Unable to test if AF_VSOCK is available: %m");
+        }
+
+        vsock_fd = safe_close(vsock_fd);
+
+        /* Determine the local CID so that we can log it to help users to connect to this VM */
+        unsigned local_cid;
+        r = vsock_get_local_cid(&local_cid);
+        if (r < 0)
+                return log_error_errno(r, "Failed to query local AF_VSOCK CID: %m");
+
+        r = make_sshd_template_unit(
+                        dest,
+                        "sshd-vsock@.service",
+                        sshd_binary,
+                        found_sshd_template_unit,
+                        generated_sshd_template_unit);
+        if (r < 0)
+                return r;
+
+        r = write_socket_unit(
+                        dest,
+                        "sshd-vsock.socket",
+                        "vsock::22",
+                        "AF_VSOCK");
+        if (r < 0)
+                return r;
+
+        log_info("Binding SSH to AF_VSOCK vsock::22.\n"
+                 "→ connect via 'ssh vsock/%u' from host", local_cid);
+        return 0;
+}
+
+static int add_local_unix_socket(
+                const char *dest,
+                const char *sshd_binary,
+                const char *found_sshd_template_unit,
+                char **generated_sshd_template_unit) {
+
+        int r;
+
+        assert(dest);
+        assert(sshd_binary);
+        assert(generated_sshd_template_unit);
+
+        r = make_sshd_template_unit(
+                        dest,
+                        "sshd-unix-local@.service",
+                        sshd_binary,
+                        found_sshd_template_unit,
+                        generated_sshd_template_unit);
+        if (r < 0)
+                return r;
+
+        r = write_socket_unit(
+                        dest,
+                        "sshd-unix-local.socket",
+                        "/run/ssh-unix-local/socket",
+                        "AF_UNIX Local");
+        if (r < 0)
+                return r;
+
+
+        log_info("Binding SSH to AF_UNIX socket /run/ssh-unix-local/socket.\n"
+                 "→ connect via 'ssh .host' locally");
+        return 0;
+}
+
+static int add_export_unix_socket(
+                const char *dest,
+                const char *sshd_binary,
+                const char *found_sshd_template_unit,
+                char **generated_sshd_template_unit) {
+
+        int r;
+
+        assert(dest);
+        assert(sshd_binary);
+        assert(generated_sshd_template_unit);
+
+        Virtualization v = detect_container();
+        if (v < 0)
+                return log_error_errno(v, "Failed to detect if we run in a container: %m");
+        if (v == VIRTUALIZATION_NONE) {
+                log_debug("Not running in container, not listening on /run/host/unix-export/ssh");
+                return 0;
+        }
+
+        if (access("/run/host/unix-export/", W_OK) < 0) {
+                if (errno == ENOENT) {
+                        log_debug("Container manager does not provide /run/host/unix-export/ mount, not binding AF_UNIX socket there.");
+                        return 0;
+                }
+                if (errno == EROFS || ERRNO_IS_PRIVILEGE(errno)) {
+                        log_debug("Container manager does not provide write access to /run/host/unix-export/, not binding AF_UNIX socket there.");
+                        return 0;
+                }
+
+                return log_error_errno(errno, "Unable to check if /run/host/unix-export exists: %m");
+        }
+
+        r = make_sshd_template_unit(
+                        dest,
+                        "sshd-unix-export@.service",
+                        sshd_binary,
+                        found_sshd_template_unit,
+                        generated_sshd_template_unit);
+        if (r < 0)
+                return r;
+
+        r = write_socket_unit(
+                        dest,
+                        "sshd-unix-export.socket",
+                        "/run/host/unix-export/ssh",
+                        "AF_UNIX Export");
+        if (r < 0)
+                return r;
+
+        log_info("Binding SSH to AF_UNIX socket /run/host/unix-export/ssh\n"
+                 "→ connect via 'ssh unix/run/systemd/nspawn/unix-export/\?\?\?/ssh' from host");
+
+        return 0;
+}
+
+static int add_extra_sockets(
+                const char *dest,
+                const char *sshd_binary,
+                const char *found_sshd_template_unit,
+                char **generated_sshd_template_unit) {
+
+        unsigned n = 1;
+        int r;
+
+        assert(dest);
+        assert(sshd_binary);
+        assert(generated_sshd_template_unit);
+
+        if (strv_isempty(arg_listen_extra))
+                return 0;
+
+        STRV_FOREACH(i, arg_listen_extra) {
+                _cleanup_free_ char *service = NULL, *socket = NULL;
+
+                if (n > 1) {
+                        if (asprintf(&service, "sshd-extra-%u@.service", n) < 0)
+                                return log_oom();
+
+                        if (asprintf(&socket, "sshd-extra-%u.socket", n) < 0)
+                                return log_oom();
+                }
+
+                r = make_sshd_template_unit(
+                                dest,
+                                service ?: "sshd-extra@.service",
+                                sshd_binary,
+                                found_sshd_template_unit,
+                                generated_sshd_template_unit);
+                if (r < 0)
+                        return r;
+
+                r = write_socket_unit(
+                                dest,
+                                socket ?: "sshd-extra.socket",
+                                *i,
+                                *i);
+                if (r < 0)
+                        return r;
+
+                log_info("Binding SSH to socket %s.", *i);
+                n++;
+        }
+
+        return 0;
+}
+
+static int parse_credentials(void) {
+        _cleanup_free_ char *b = NULL;
+        size_t sz = 0;
+        int r;
+
+        r = read_credential_with_decryption("ssh.listen", (void*) &b, &sz);
+        if (r < 0)
+                return r;
+        if (r == 0)
+                return 0;
+
+        _cleanup_fclose_ FILE *f = NULL;
+        f = fmemopen_unlocked(b, sz, "r");
+        if (!f)
+                return log_oom();
+
+        for (;;) {
+                _cleanup_free_ char *item = NULL;
+
+                r = read_stripped_line(f, LINE_MAX, &item);
+                if (r == 0)
+                        break;
+                if (r < 0) {
+                        log_error_errno(r, "Failed to parse credential 'ssh.listen': %m");
+                        break;
+                }
+
+                if (startswith(item, "#"))
+                        continue;
+
+                SocketAddress sa;
+                r = socket_address_parse(&sa, item);
+                if (r < 0) {
+                        log_warning_errno(r, "Failed to parse systemd.ssh_listen= expression, ignoring: %s", item);
+                        continue;
+                }
+
+                _cleanup_free_ char *s = NULL;
+                r = socket_address_print(&sa, &s);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to format socket address: %m");
+
+                if (strv_consume(&arg_listen_extra, TAKE_PTR(s)) < 0)
+                        return log_oom();
+        }
+
+        return 0;
+}
+
+static int run(const char *dest, const char *dest_early, const char *dest_late) {
+        int r;
+
+        assert_se(arg_dest = dest);
+
+        r = proc_cmdline_parse(parse_proc_cmdline_item, /* userdata= */ NULL, /* flags= */ 0);
+        if (r < 0)
+                log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m");
+
+        (void) parse_credentials();
+
+        strv_sort(arg_listen_extra);
+        strv_uniq(arg_listen_extra);
+
+        if (!arg_auto && strv_isempty(arg_listen_extra)) {
+                log_debug("Disabling SSH generator logic, because as it has been turned off explicitly.");
+                return 0;
+        }
+
+        _cleanup_free_ char *sshd_binary = NULL;
+        r = find_executable("sshd", &sshd_binary);
+        if (r == -ENOENT) {
+                log_info("Disabling SSH generator logic, since sshd is not installed.");
+                return 0;
+        }
+        if (r < 0)
+                return log_error_errno(r, "Failed to determine if sshd is installed: %m");
+
+        _cleanup_(lookup_paths_free) LookupPaths lp = {};
+        r = lookup_paths_init_or_warn(&lp, RUNTIME_SCOPE_SYSTEM, LOOKUP_PATHS_EXCLUDE_GENERATED, /* root_dir= */ NULL);
+        if (r < 0)
+                return r;
+
+        _cleanup_free_ char *found_sshd_template_unit = NULL;
+        r = unit_file_exists_full(RUNTIME_SCOPE_SYSTEM, &lp, "sshd@.service", &found_sshd_template_unit);
+        if (r < 0)
+                return log_error_errno(r, "Unable to detect if sshd@.service exists: %m");
+
+        _cleanup_free_ char *generated_sshd_template_unit = NULL;
+        RET_GATHER(r, add_extra_sockets(dest, sshd_binary, found_sshd_template_unit, &generated_sshd_template_unit));
+
+        if (arg_auto) {
+                RET_GATHER(r, add_vsock_socket(dest, sshd_binary, found_sshd_template_unit, &generated_sshd_template_unit));
+                RET_GATHER(r, add_local_unix_socket(dest, sshd_binary, found_sshd_template_unit, &generated_sshd_template_unit));
+                RET_GATHER(r, add_export_unix_socket(dest, sshd_binary, found_sshd_template_unit, &generated_sshd_template_unit));
+        }
+
+        return r;
+}
+
+DEFINE_MAIN_GENERATOR_FUNCTION(run);
diff --git a/src/ssh-generator/ssh-proxy.c b/src/ssh-generator/ssh-proxy.c
new file mode 100644 (file)
index 0000000..4884c93
--- /dev/null
@@ -0,0 +1,102 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <net/if_arp.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include "fd-util.h"
+#include "iovec-util.h"
+#include "log.h"
+#include "main-func.h"
+#include "missing_socket.h"
+#include "parse-util.h"
+#include "socket-util.h"
+#include "string-util.h"
+#include "strv.h"
+
+static int process_vsock(const char *host, const char *port) {
+        int r;
+
+        assert(host);
+        assert(port);
+
+        union sockaddr_union sa = {
+                .vm.svm_family = AF_VSOCK,
+        };
+
+        r = vsock_parse_cid(host, &sa.vm.svm_cid);
+        if (r < 0)
+                return log_error_errno(r, "Failed to parse vsock cid: %s", host);
+
+        r = vsock_parse_port(port, &sa.vm.svm_port);
+        if (r < 0)
+                return log_error_errno(r, "Failed to parse vsock port: %s", port);
+
+        _cleanup_close_ int fd = socket(AF_VSOCK, SOCK_STREAM|SOCK_CLOEXEC, 0);
+        if (fd < 0)
+                return log_error_errno(errno, "Failed to allocate AF_VSOCK socket: %m");
+
+        if (connect(fd, &sa.sa, SOCKADDR_LEN(sa)) < 0)
+                return log_error_errno(errno, "Failed to connect to vsock:%u:%u: %m", sa.vm.svm_cid, sa.vm.svm_port);
+
+        /* OpenSSH wants us to send a single byte along with the file descriptor, hence do so */
+        r = send_one_fd_iov(STDOUT_FILENO, fd, &IOVEC_NUL_BYTE, /* n_iovec= */ 1, /* flags= */ 0);
+        if (r < 0)
+                return log_error_errno(r, "Failed to send socket via STDOUT: %m");
+
+        log_debug("Successfully sent AF_VSOCK socket via STDOUT.");
+        return 0;
+}
+
+static int process_unix(const char *path) {
+        int r;
+
+        assert(path);
+
+        /* We assume the path is absolute unless it starts with a dot (or is already explicitly absolute) */
+        _cleanup_free_ char *prefixed = NULL;
+        if (!STARTSWITH_SET(path, "/", "./")) {
+                prefixed = strjoin("/", path);
+                if (!prefixed)
+                        return log_oom();
+
+                path = prefixed;
+        }
+
+        _cleanup_close_ int fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0);
+        if (fd < 0)
+                return log_error_errno(errno, "Failed to allocate AF_UNIX socket: %m");
+
+        r = connect_unix_path(fd, AT_FDCWD, path);
+        if (r < 0)
+                return log_error_errno(r, "Failed to connect to AF_UNIX socket %s: %m", path);
+
+        r = send_one_fd_iov(STDOUT_FILENO, fd, &IOVEC_NUL_BYTE, /* n_iovec= */ 1, /* flags= */ 0);
+        if (r < 0)
+                return log_error_errno(r, "Failed to send socket via STDOUT: %m");
+
+        log_debug("Successfully sent AF_UNIX socket via STDOUT.");
+        return 0;
+}
+
+static int run(int argc, char* argv[]) {
+
+        log_setup();
+
+        if (argc != 3)
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Expected two arguments: host and port.");
+
+        const char *host = argv[1], *port = argv[2];
+
+        const char *p = startswith(host, "vsock/");
+        if (p)
+                return process_vsock(p, port);
+
+        p = startswith(host, "unix/");
+        if (p)
+                return process_unix(p);
+
+        return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Don't know how to parse host name specification: %s", host);
+}
+
+DEFINE_MAIN_FUNCTION(run);
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 6b521c93476bfe80aad169d922f65f0d0ec76c07..59be6a7a7ecfef7a78520d9ff1958888afa4c88c 100644 (file)
@@ -29,7 +29,7 @@ int verb_is_system_running(int argc, char *argv[], void *userdata) {
         sd_bus *bus;
         int r;
 
-        if (running_in_chroot() > 0 || (arg_transport == BUS_TRANSPORT_LOCAL && !sd_booted())) {
+        if (!isempty(arg_root) || running_in_chroot() > 0 || (arg_transport == BUS_TRANSPORT_LOCAL && !sd_booted())) {
                 if (!arg_quiet)
                         puts("offline");
                 return EXIT_FAILURE;
index c74bcf2642e2b1b2700e8dec253d69a67fc9eac7..5bb8ad9fa07b5f4a841fdf27e195393deb8ee5f1 100644 (file)
@@ -932,11 +932,7 @@ static int map_listen(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus
 
         while ((r = sd_bus_message_read(m, "(ss)", &type, &path)) > 0) {
 
-                r = strv_extend(p, type);
-                if (r < 0)
-                        return r;
-
-                r = strv_extend(p, path);
+                r = strv_extend_many(p, type, path);
                 if (r < 0)
                         return r;
         }
index c35bfc47c96d9981b530db58bd4cc9317ae78a1a..34db2bbf1cc9376addbaa7193552980c80b69102 100644 (file)
@@ -106,7 +106,6 @@ int sd_netlink_message_cancel_array(sd_netlink_message *m);
 /* Reading messages */
 int sd_netlink_message_read(sd_netlink_message *m, uint16_t attr_type, size_t size, void *data);
 int sd_netlink_message_read_data(sd_netlink_message *m, uint16_t attr_type, size_t *ret_size, void **ret_data);
-int sd_netlink_message_read_data_suffix0(sd_netlink_message *m, uint16_t attr_type, size_t *ret_size, void **ret_data);
 int sd_netlink_message_read_string_strdup(sd_netlink_message *m, uint16_t attr_type, char **data);
 int sd_netlink_message_read_string(sd_netlink_message *m, uint16_t attr_type, const char **data);
 int sd_netlink_message_read_strv(sd_netlink_message *m, uint16_t container_type, uint16_t attr_type, char ***ret);
@@ -217,6 +216,8 @@ int sd_rtnl_message_traffic_control_get_parent(sd_netlink_message *m, uint32_t *
 
 int sd_rtnl_message_new_mdb(sd_netlink *rtnl, sd_netlink_message **ret, uint16_t nlmsg_type, int mdb_ifindex);
 
+int sd_rtnl_message_new_nsid(sd_netlink *rtnl, sd_netlink_message **ret, uint16_t nlmsg_type);
+
 /* genl */
 int sd_genl_socket_open(sd_netlink **ret);
 int sd_genl_message_new(sd_netlink *genl, const char *family_name, uint8_t cmd, sd_netlink_message **ret);
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..4d5d61c691513652091d233e7935d66bd4d7d8b0 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;
@@ -347,6 +347,7 @@ static int putgrent_with_members(
                 FILE *group) {
 
         char **a;
+        int r;
 
         assert(c);
         assert(gr);
@@ -365,15 +366,15 @@ static int putgrent_with_members(
                         if (strv_contains(l, *i))
                                 continue;
 
-                        if (strv_extend(&l, *i) < 0)
-                                return -ENOMEM;
+                        r = strv_extend(&l, *i);
+                        if (r < 0)
+                                return r;
 
                         added = true;
                 }
 
                 if (added) {
                         struct group t;
-                        int r;
 
                         strv_uniq(l);
                         strv_sort(l);
@@ -396,6 +397,7 @@ static int putsgent_with_members(
                 FILE *gshadow) {
 
         char **a;
+        int r;
 
         assert(sg);
         assert(gshadow);
@@ -413,15 +415,15 @@ static int putsgent_with_members(
                         if (strv_contains(l, *i))
                                 continue;
 
-                        if (strv_extend(&l, *i) < 0)
-                                return -ENOMEM;
+                        r = strv_extend(&l, *i);
+                        if (r < 0)
+                                return r;
 
                         added = true;
                 }
 
                 if (added) {
                         struct sgrp t;
-                        int r;
 
                         strv_uniq(l);
                         strv_sort(l);
index 4a50fc9b5b723a5740685c0796af944813bb54ef..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',
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 4ceb084c0c81fdcfa822aaa8e46d7a0309ea8713..cb0adc2643774d90640ff6bdb5168661bacbd765 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"
@@ -105,9 +107,9 @@ static void test_variant_one(const char *data, Test test) {
         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);
+        r = json_variant_format(w, JSON_FORMAT_CENSOR_SENSITIVE, &s);
+        assert_se(s);
+        assert_se(streq_ptr(s, "\"<sensitive data>\""));
 
         s = mfree(s);
         r = json_variant_format(w, JSON_FORMAT_PRETTY, &s);
@@ -824,6 +826,56 @@ 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;
@@ -834,10 +886,11 @@ TEST(json_sensitive) {
 
         json_variant_sensitive(a);
 
-        assert_se(json_variant_format(a, JSON_FORMAT_REFUSE_SENSITIVE, &s) == -EPERM);
-        assert_se(!s);
+        assert_se(json_variant_format(a, JSON_FORMAT_CENSOR_SENSITIVE, &s) >= 0);
+        assert_se(streq_ptr(s, "\"<sensitive data>\""));
+        s = mfree(s);
 
-        r = json_variant_format(b, JSON_FORMAT_REFUSE_SENSITIVE, &s);
+        r = json_variant_format(b, JSON_FORMAT_CENSOR_SENSITIVE, &s);
         assert_se(r >= 0);
         assert_se(s);
         assert_se((size_t) r == strlen(s));
@@ -849,7 +902,7 @@ TEST(json_sensitive) {
                                              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);
+        r = json_variant_format(v, JSON_FORMAT_CENSOR_SENSITIVE, &s);
         assert_se(r >= 0);
         assert_se(s);
         assert_se((size_t) r == strlen(s));
@@ -863,7 +916,7 @@ TEST(json_sensitive) {
                                              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);
+        r = json_variant_format(v, JSON_FORMAT_CENSOR_SENSITIVE, &s);
         assert_se(r >= 0);
         assert_se(s);
         assert_se((size_t) r == strlen(s));
@@ -878,8 +931,9 @@ TEST(json_sensitive) {
                                              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);
+        assert_se(json_variant_format(v, JSON_FORMAT_CENSOR_SENSITIVE, &s) >= 0);
+        assert_se(streq_ptr(s, "{\"b\":[\"foo\",\"bar\",\"baz\",\"qux\"],\"a\":\"<sensitive data>\",\"c\":-9223372036854775808,\"d\":\"-9223372036854775808\",\"e\":{}}"));
+        s = mfree(s);
         v = json_variant_unref(v);
 
         assert_se(json_build(&v, JSON_BUILD_OBJECT(
@@ -890,8 +944,9 @@ TEST(json_sensitive) {
                                              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);
+        assert_se(json_variant_format(v, JSON_FORMAT_CENSOR_SENSITIVE, &s) >= 0);
+        assert_se(streq_ptr(s, "{\"b\":[\"foo\",\"bar\",\"baz\",\"qux\"],\"c\":-9223372036854775808,\"a\":\"<sensitive data>\",\"d\":\"-9223372036854775808\",\"e\":{}}"));
+        s = mfree(s);
         v = json_variant_unref(v);
 
         assert_se(json_build(&v, JSON_BUILD_OBJECT(
@@ -902,8 +957,9 @@ TEST(json_sensitive) {
                                              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);
+        assert_se(json_variant_format(v, JSON_FORMAT_CENSOR_SENSITIVE, &s) >= 0);
+        assert_se(streq_ptr(s, "{\"b\":[\"foo\",\"bar\",\"baz\",\"qux\"],\"c\":-9223372036854775808,\"d\":\"-9223372036854775808\",\"a\":\"<sensitive data>\",\"e\":{}}"));
+        s = mfree(s);
         v = json_variant_unref(v);
 
         assert_se(json_build(&v, JSON_BUILD_OBJECT(
@@ -914,8 +970,28 @@ TEST(json_sensitive) {
                                              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);
+        assert_se(json_variant_format(v, JSON_FORMAT_CENSOR_SENSITIVE, &s) >= 0);
+        assert_se(streq_ptr(s, "{\"b\":[\"foo\",\"bar\",\"baz\",\"qux\"],\"c\":-9223372036854775808,\"d\":\"-9223372036854775808\",\"e\":{},\"a\":\"<sensitive data>\"}"));
+}
+
+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 6dbd50f4a1bf83bde1c0a1a1b4176293b6dbf6ca..ad642ca4b1facfe47b902bf95c15e8da500385c8 100644 (file)
@@ -369,4 +369,16 @@ TEST(in_addr_port_ifindex_name_from_string_auto) {
         test_in_addr_port_ifindex_name_from_string_auto_one("[fe80::18]:53%lo#hoge.com", AF_INET6, 53, 1, "hoge.com", "[fe80::18]:53%1#hoge.com");
 }
 
+TEST(netns_get_nsid) {
+        uint32_t u;
+        int r;
+
+        r = netns_get_nsid(-EBADF, &u);
+        assert_se(r == -ENODATA || r >= 0);
+        if (r == -ENODATA)
+                log_info("Our network namespace has no NSID assigned.");
+        else
+                log_info("Our NSID is %" PRIu32, u);
+}
+
 DEFINE_TEST_MAIN(LOG_DEBUG);
index f70e2aa862d3d1f607b8fc91975a7831aab320cb..da8721214a2f42ce9f8b97029032e50c691e9310 100644 (file)
@@ -1014,4 +1014,29 @@ TEST(endswith_strv) {
         assert_se(streq_ptr(endswith_strv("waldo", STRV_MAKE("knurz", "", "waldo")), ""));
 }
 
+TEST(strv_extend_many) {
+        _cleanup_strv_free_ char **l = NULL;
+
+        assert_se(strv_extend_many(&l, NULL) >= 0);
+        assert_se(strv_isempty(l));
+
+        assert_se(strv_extend_many(&l, NULL, NULL, NULL) >= 0);
+        assert_se(strv_isempty(l));
+
+        assert_se(strv_extend_many(&l, "foo") >= 0);
+        assert_se(strv_equal(l, STRV_MAKE("foo")));
+
+        assert_se(strv_extend_many(&l, NULL, "bar", NULL) >= 0);
+        assert_se(strv_equal(l, STRV_MAKE("foo", "bar")));
+
+        assert_se(strv_extend_many(&l, "waldo", "quux") >= 0);
+        assert_se(strv_equal(l, STRV_MAKE("foo", "bar", "waldo", "quux")));
+
+        assert_se(strv_extend_many(&l, "1", "2", "3", "4") >= 0);
+        assert_se(strv_equal(l, STRV_MAKE("foo", "bar", "waldo", "quux", "1", "2", "3", "4")));
+
+        assert_se(strv_extend_many(&l, "yes", NULL, "no") >= 0);
+        assert_se(strv_equal(l, STRV_MAKE("foo", "bar", "waldo", "quux", "1", "2", "3", "4", "yes", "no")));
+}
+
 DEFINE_TEST_MAIN(LOG_INFO);
index 19881c6e91f447bbd8dbef3de8316bb389a49836..254c4c5e8bc2218a1a1baf4e44a3f7bc84d569db 100644 (file)
@@ -819,36 +819,75 @@ static void check_tpm2b_public_fingerprint(const TPM2B_PUBLIC *public, const cha
         assert_se(memcmp_nn(fp, fp_size, expected, expected_len) == 0);
 }
 
-TEST(tpm2b_public_from_openssl_pkey) {
-        TPM2B_PUBLIC public;
+static void check_tpm2b_public_name(const TPM2B_PUBLIC *public, const char *hexname) {
+        DEFINE_HEX_PTR(expected, hexname);
+        TPM2B_NAME name = {};
+
+        assert_se(tpm2_calculate_pubkey_name(&public->publicArea, &name) >= 0);
+        assert_se(memcmp_nn(name.name, name.size, expected, expected_len) == 0);
+}
+
+static void check_tpm2b_public_from_ecc_pem(const char *pem, const char *hexx, const char *hexy, const char *hexfp, const char *hexname) {
+        TPM2B_PUBLIC public = {};
         TPMT_PUBLIC *p = &public.publicArea;
 
-        DEFINE_HEX_PTR(key_ecc, "2d2d2d2d2d424547494e205055424c4943204b45592d2d2d2d2d0a4d466b77457759484b6f5a497a6a3043415159494b6f5a497a6a30444151634451674145726a6e4575424c73496c3972687068777976584e50686a346a426e500a44586e794a304b395579724e6764365335413532542b6f5376746b436a365a726c34685847337741515558706f426c532b7448717452714c35513d3d0a2d2d2d2d2d454e44205055424c4943204b45592d2d2d2d2d0a");
-        get_tpm2b_public_from_pem(key_ecc, key_ecc_len, &public);
+        DEFINE_HEX_PTR(key, pem);
+        get_tpm2b_public_from_pem(key, key_len, &public);
 
         assert_se(p->type == TPM2_ALG_ECC);
         assert_se(p->parameters.eccDetail.curveID == TPM2_ECC_NIST_P256);
 
-        DEFINE_HEX_PTR(expected_x, "ae39c4b812ec225f6b869870caf5cd3e18f88c19cf0d79f22742bd532acd81de");
+        DEFINE_HEX_PTR(expected_x, hexx);
         assert_se(memcmp_nn(p->unique.ecc.x.buffer, p->unique.ecc.x.size, expected_x, expected_x_len) == 0);
 
-        DEFINE_HEX_PTR(expected_y, "92e40e764fea12bed9028fa66b9788571b7c004145e9a01952fad1eab51a8be5");
+        DEFINE_HEX_PTR(expected_y, hexy);
         assert_se(memcmp_nn(p->unique.ecc.y.buffer, p->unique.ecc.y.size, expected_y, expected_y_len) == 0);
 
-        check_tpm2b_public_fingerprint(&public, "cd3373293b62a52b48c12100e80ea9bfd806266ce76893a5ec31cb128052d97c");
+        check_tpm2b_public_fingerprint(&public, hexfp);
+        check_tpm2b_public_name(&public, hexname);
+}
+
+static void check_tpm2b_public_from_rsa_pem(const char *pem, const char *hexn, uint32_t exponent, const char *hexfp, const char *hexname) {
+        TPM2B_PUBLIC public = {};
+        TPMT_PUBLIC *p = &public.publicArea;
+
+        DEFINE_HEX_PTR(key, pem);
+        get_tpm2b_public_from_pem(key, key_len, &public);
 
-        DEFINE_HEX_PTR(key_rsa, "2d2d2d2d2d424547494e205055424c4943204b45592d2d2d2d2d0a4d494942496a414e42676b71686b6947397730424151454641414f43415138414d49494243674b4341514541795639434950652f505852337a436f63787045300a6a575262546c3568585844436b472f584b79374b6d2f4439584942334b734f5a31436a5937375571372f674359363170697838697552756a73413464503165380a593445336c68556d374a332b6473766b626f4b64553243626d52494c2f6675627771694c4d587a41673342575278747234547545443533527a373634554650640a307a70304b68775231496230444c67772f344e67566f314146763378784b4d6478774d45683567676b73733038326332706c354a504e32587677426f744e6b4d0a5471526c745a4a35355244436170696e7153334577376675646c4e735851357746766c7432377a7637344b585165616d704c59433037584f6761304c676c536b0a79754774586b6a50542f735542544a705374615769674d5a6f714b7479563463515a58436b4a52684459614c47587673504233687a766d5671636e6b47654e540a65774944415141420a2d2d2d2d2d454e44205055424c4943204b45592d2d2d2d2d0a");
-        get_tpm2b_public_from_pem(key_rsa, key_rsa_len, &public);
+        assert_se(p->type == TPM2_ALG_RSA);
 
-        DEFINE_HEX_PTR(expected_n, "c95f4220f7bf3d7477cc2a1cc691348d645b4e5e615d70c2906fd72b2eca9bf0fd5c80772ac399d428d8efb52aeff80263ad698b1f22b91ba3b00e1d3f57bc638137961526ec9dfe76cbe46e829d53609b99120bfdfb9bc2a88b317cc0837056471b6be13b840f9dd1cfbeb85053ddd33a742a1c11d486f40cb830ff8360568d4016fdf1c4a31dc7030487982092cb34f36736a65e493cdd97bf0068b4d90c4ea465b59279e510c26a98a7a92dc4c3b7ee76536c5d0e7016f96ddbbcefef829741e6a6a4b602d3b5ce81ad0b8254a4cae1ad5e48cf4ffb140532694ad6968a0319a2a2adc95e1c4195c29094610d868b197bec3c1de1cef995a9c9e419e3537b");
-        assert_se(p->unique.rsa.size == expected_n_len);
-        assert_se(memcmp(p->unique.rsa.buffer, expected_n, expected_n_len) == 0);
+        DEFINE_HEX_PTR(expected_n, hexn);
+        assert_se(memcmp_nn(p->unique.rsa.buffer, p->unique.rsa.size, expected_n, expected_n_len) == 0);
 
         assert_se(p->parameters.rsaDetail.keyBits == expected_n_len * 8);
 
-        assert_se(p->parameters.rsaDetail.exponent == 0);
+        assert_se(p->parameters.rsaDetail.exponent == exponent);
 
-        check_tpm2b_public_fingerprint(&public, "d9186d13a7fd5b3644cee05448f49ad3574e82a2942ff93cf89598d36cca78a9");
+        check_tpm2b_public_fingerprint(&public, hexfp);
+        check_tpm2b_public_name(&public, hexname);
+}
+
+TEST(tpm2b_public_from_openssl_pkey) {
+        /* standard ECC key */
+        check_tpm2b_public_from_ecc_pem("2d2d2d2d2d424547494e205055424c4943204b45592d2d2d2d2d0a4d466b77457759484b6f5a497a6a3043415159494b6f5a497a6a30444151634451674145726a6e4575424c73496c3972687068777976584e50686a346a426e500a44586e794a304b395579724e6764365335413532542b6f5376746b436a365a726c34685847337741515558706f426c532b7448717452714c35513d3d0a2d2d2d2d2d454e44205055424c4943204b45592d2d2d2d2d0a",
+                                        "ae39c4b812ec225f6b869870caf5cd3e18f88c19cf0d79f22742bd532acd81de",
+                                        "92e40e764fea12bed9028fa66b9788571b7c004145e9a01952fad1eab51a8be5",
+                                        "cd3373293b62a52b48c12100e80ea9bfd806266ce76893a5ec31cb128052d97c",
+                                        "000b5c127e4dbaf8fb7bac641e8db25a84a48db876ca7ee3bd317ae1a4554ff72f17");
+
+        /* standard RSA key */
+        check_tpm2b_public_from_rsa_pem("2d2d2d2d2d424547494e205055424c4943204b45592d2d2d2d2d0a4d494942496a414e42676b71686b6947397730424151454641414f43415138414d49494243674b4341514541795639434950652f505852337a436f63787045300a6a575262546c3568585844436b472f584b79374b6d2f4439584942334b734f5a31436a5937375571372f674359363170697838697552756a73413464503165380a593445336c68556d374a332b6473766b626f4b64553243626d52494c2f6675627771694c4d587a41673342575278747234547545443533527a373634554650640a307a70304b68775231496230444c67772f344e67566f314146763378784b4d6478774d45683567676b73733038326332706c354a504e32587677426f744e6b4d0a5471526c745a4a35355244436170696e7153334577376675646c4e735851357746766c7432377a7637344b585165616d704c59433037584f6761304c676c536b0a79754774586b6a50542f735542544a705374615769674d5a6f714b7479563463515a58436b4a52684459614c47587673504233687a766d5671636e6b47654e540a65774944415141420a2d2d2d2d2d454e44205055424c4943204b45592d2d2d2d2d0a",
+                                        "c95f4220f7bf3d7477cc2a1cc691348d645b4e5e615d70c2906fd72b2eca9bf0fd5c80772ac399d428d8efb52aeff80263ad698b1f22b91ba3b00e1d3f57bc638137961526ec9dfe76cbe46e829d53609b99120bfdfb9bc2a88b317cc0837056471b6be13b840f9dd1cfbeb85053ddd33a742a1c11d486f40cb830ff8360568d4016fdf1c4a31dc7030487982092cb34f36736a65e493cdd97bf0068b4d90c4ea465b59279e510c26a98a7a92dc4c3b7ee76536c5d0e7016f96ddbbcefef829741e6a6a4b602d3b5ce81ad0b8254a4cae1ad5e48cf4ffb140532694ad6968a0319a2a2adc95e1c4195c29094610d868b197bec3c1de1cef995a9c9e419e3537b",
+                                        0x10001,
+                                        "d9186d13a7fd5b3644cee05448f49ad3574e82a2942ff93cf89598d36cca78a9",
+                                        "000be1bd75c7976e7a30e9e82223b81a9eff0d42c30618e588db592ed5da94455e81");
+
+        /* RSA key with non-default (i.e. not 0x10001) exponent */
+        check_tpm2b_public_from_rsa_pem("2d2d2d2d2d424547494e205055424c4943204b45592d2d2d2d2d0a4d494942496a414e42676b71686b6947397730424151454641414f43415138414d49494243674b434151454179566c7551664b75565171596a5a71436a657a760a364e4a6f58654c736f702f72765375666330773769544d4f73566741557462515452505451725874397065537a4370524467634378656b6a544144577279304b0a6d59786a7a3634776c6a7030463959383068636a6b6b4b3759414d333054664c4648656c2b377574427370777142467a6e2b385a6659567353434b397354706f0a316c61376e5347514e7451576f36444a366c525a336a676d6d584f61544654416145304a432b7046584273564471736d46326438362f314e51714a755a5154520a575852636954704e58357649792f37766b6c5a6a685569526c78764e594f4e3070636476534a37364e74496e447a3048506f775a38705a454f4d2f4a454f59780a617a4c4a6a644936446b355279593578325a7949375074566a3057537242524f4d696f2b674c6556457a43343456336438315a38445138564e334c69625130330a70514944415141460a2d2d2d2d2d454e44205055424c4943204b45592d2d2d2d2d0a",
+                                        "c9596e41f2ae550a988d9a828decefe8d2685de2eca29febbd2b9f734c3b89330eb1580052d6d04d13d342b5edf69792cc2a510e0702c5e9234c00d6af2d0a998c63cfae30963a7417d63cd217239242bb600337d137cb1477a5fbbbad06ca70a811739fef197d856c4822bdb13a68d656bb9d219036d416a3a0c9ea5459de382699739a4c54c0684d090bea455c1b150eab2617677cebfd4d42a26e6504d159745c893a4d5f9bc8cbfeef925663854891971bcd60e374a5c76f489efa36d2270f3d073e8c19f2964438cfc910e6316b32c98dd23a0e4e51c98e71d99c88ecfb558f4592ac144e322a3e80b7951330b8e15dddf3567c0d0f153772e26d0d37a5",
+                                        0x10005,
+                                        "c8ca80a687d5972e1d961aaa2cfde2ff2e7a20d85e3ea0382804e70e013d65af",
+                                        "000beb8974d36d8cf58fdc87460dda00319e10c94c1b9f222ac9ce29d1c4776246cc");
 }
 #endif
 
@@ -1100,42 +1139,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 +1222,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);
index b4a58fc6923961d00129bc6d6303ddd60f521e02..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"
@@ -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 6ed15aa402570c6365d60bb0f3aecce24b87c058..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);
 }
index 681e773cba14322a4a1441e44f0bc50690156919..9601c538bcb6c8bfc21479f0f517606bac4b0b63 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"
@@ -218,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);
@@ -355,15 +355,11 @@ static int user_config_paths(char*** ret) {
         if (r < 0)
                 return r;
 
-        r = strv_extend(&res, persistent_config);
-        if (r < 0)
-                return r;
-
-        r = strv_extend(&res, runtime_config);
-        if (r < 0)
-                return r;
-
-        r = strv_extend(&res, data_home);
+        r = strv_extend_many(
+                        &res,
+                        persistent_config,
+                        runtime_config,
+                        data_home);
         if (r < 0)
                 return r;
 
@@ -400,20 +396,20 @@ static bool needs_purge(ItemType t) {
 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) {
@@ -421,7 +417,6 @@ static bool takes_ownership(ItemType t) {
                       CREATE_FILE,
                       TRUNCATE_FILE,
                       CREATE_DIRECTORY,
-                      EMPTY_DIRECTORY,
                       TRUNCATE_DIRECTORY,
                       CREATE_SUBVOLUME,
                       CREATE_SUBVOLUME_INHERIT_QUOTA,
@@ -432,6 +427,7 @@ static bool takes_ownership(ItemType t) {
                       CREATE_BLOCK_DEVICE,
                       COPY_FILES,
                       WRITE_FILE,
+                      EMPTY_DIRECTORY,
                       IGNORE_PATH,
                       IGNORE_DIRECTORY_PATH,
                       REMOVE_PATH,
@@ -556,18 +552,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,
@@ -710,10 +694,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) {
@@ -3034,8 +3018,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,
@@ -3051,16 +3035,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);
 
@@ -3807,7 +3791,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);
 
@@ -3994,16 +3979,16 @@ static int exclude_default_prefixes(void) {
          * likely over-mounted if the root directory is actually used, and it wouldbe less than ideal to have
          * all kinds of files created/adjusted underneath these mount points. */
 
-        r = strv_extend_strv(
+        r = strv_extend_many(
                         &arg_exclude_prefixes,
-                        STRV_MAKE("/dev",
-                                  "/proc",
-                                  "/run",
-                                  "/sys"),
-                                 true);
+                        "/dev",
+                        "/proc",
+                        "/run",
+                        "/sys");
         if (r < 0)
                 return log_oom();
 
+        strv_uniq(arg_exclude_prefixes);
         return 0;
 }
 
@@ -4141,12 +4126,12 @@ static int parse_argv(int argc, char *argv[]) {
                         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;
 
index 0b1f0b7157df4fe6861746c0ad0b4cc96886a9fc..4dd7e54973ab11c247448a87cffd1ec4a09d6718 100644 (file)
@@ -298,7 +298,8 @@ static void disk_identify_fixup_uint16(uint8_t identify[512], unsigned offset_wo
  * non-zero with errno set.
  */
 static int disk_identify(int fd,
-                         uint8_t out_identify[512]) {
+                         uint8_t out_identify[512],
+                         int *ret_peripheral_device_type) {
         uint8_t inquiry_buf[36];
         int peripheral_device_type, r;
 
@@ -358,6 +359,9 @@ static int disk_identify(int fd,
         if (all_nul_bytes)
                 return log_debug_errno(SYNTHETIC_ERRNO(EIO), "IDENTIFY data is all zeroes.");
 
+        if (ret_peripheral_device_type)
+                *ret_peripheral_device_type = peripheral_device_type;
+
         return 0;
 }
 
@@ -407,7 +411,7 @@ static int run(int argc, char *argv[]) {
         char model[41], model_enc[256], serial[21], revision[9];
         _cleanup_close_ int fd = -EBADF;
         uint16_t word;
-        int r;
+        int r, peripheral_device_type = -1;
 
         log_set_target(LOG_TARGET_AUTO);
         udev_parse_config();
@@ -422,7 +426,7 @@ static int run(int argc, char *argv[]) {
         if (fd < 0)
                 return log_error_errno(errno, "Cannot open %s: %m", arg_device);
 
-        if (disk_identify(fd, identify.byte) >= 0) {
+        if (disk_identify(fd, identify.byte, &peripheral_device_type) >= 0) {
                 /*
                  * fix up only the fields from the IDENTIFY data that we are going to
                  * use and copy it into the hd_driveid struct for convenience
@@ -615,6 +619,9 @@ static int run(int argc, char *argv[]) {
                 if (IN_SET(identify.wyde[0], 0x848a, 0x844a) ||
                     (identify.wyde[83] & 0xc004) == 0x4004)
                         printf("ID_ATA_CFA=1\n");
+
+                if (peripheral_device_type >= 0)
+                        printf("ID_ATA_PERIPHERAL_DEVICE_TYPE=%d\n", peripheral_device_type);
         } else {
                 if (serial[0] != '\0')
                         printf("%s_%s\n", model, serial);
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 6308c52b7ec33d790cbb713c6a025c94aeec5628..d7d4380851d0736e93fd3fc57aba94df74c1212f 100644 (file)
@@ -84,6 +84,13 @@ static void set_type(unsigned type_num, char *to, size_t len) {
         case 0xf:
                 type = "optical";
                 break;
+        case 0x14:
+                /*
+                 * Use "zbc" here to be brief and consistent with "lsscsi" command.
+                 * Other tools, e.g., "sg3_utils" would say "host managed zoned block".
+                 */
+                type = "zbc";
+                break;
         default:
                 type = "generic";
                 break;
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..dc0630596b9eed98e9e98b965634eaa0e7601ba5 100644 (file)
@@ -186,7 +186,7 @@ static sd_device *handle_scsi_sas(sd_device *parent, char **path) {
                 return NULL;
 
         /* Check if we are simple disk */
-        if (strncmp(phy_count, "1", 2) != 0)
+        if (!streq(phy_count, "1"))
                 return handle_scsi_sas_wide_port(parent, path);
 
         /* Get connected phy */
@@ -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 5bf00bb26255abb1e53c89d12b719d9335453bc8..31944c224723126f77f462583389b40551d09f67 100644 (file)
@@ -335,6 +335,7 @@ static int on_event_timeout_warning(sd_event_source *s, uint64_t usec, void *use
 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;
 
@@ -347,10 +348,15 @@ static usec_t extra_timeout_usec(void) {
         if (!e)
                 return saved;
 
-        r = parse_sec(e, &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;
 }
 
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 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 6f8546385e295932187acbf5b0a6a1d31b567968..684ced1315f81dfa0c8f113e154439f4698aed5d 100644 (file)
@@ -10,6 +10,7 @@
 
 #include "sd-daemon.h"
 
+#include "conf-parser.h"
 #include "env-file.h"
 #include "errno-util.h"
 #include "fd-util.h"
@@ -65,70 +66,29 @@ static int listen_fds(int *ret_ctrl, int *ret_netlink) {
         return 0;
 }
 
+static DEFINE_CONFIG_PARSE_ENUM(config_parse_resolve_name_timing, resolve_name_timing, ResolveNameTiming, "Failed to parse resolve name timing");
+
 static int manager_parse_udev_config(Manager *manager) {
-        _cleanup_free_ char *log_val = NULL, *children_max = NULL, *exec_delay = NULL,
-                *event_timeout = NULL, *resolve_names = NULL, *timeout_signal = NULL;
-        int r;
+        int r, log_val = -1;
 
         assert(manager);
 
-        r = parse_env_file(NULL, "/etc/udev/udev.conf",
-                           "udev_log", &log_val,
-                           "children_max", &children_max,
-                           "exec_delay", &exec_delay,
-                           "event_timeout", &event_timeout,
-                           "resolve_names", &resolve_names,
-                           "timeout_signal", &timeout_signal);
-        if (r == -ENOENT)
-                return 0;
-        if (r < 0)
-                return r;
+        const ConfigTableItem config_table[] = {
+                { NULL, "udev_log",       config_parse_log_level,           0, &log_val                      },
+                { NULL, "children_max",   config_parse_unsigned,            0, &manager->children_max        },
+                { NULL, "exec_delay",     config_parse_sec,                 0, &manager->exec_delay_usec     },
+                { NULL, "event_timeout",  config_parse_sec,                 0, &manager->timeout_usec        },
+                { NULL, "resolve_names",  config_parse_resolve_name_timing, 0, &manager->resolve_name_timing },
+                { NULL, "timeout_signal", config_parse_signal,              0, &manager->timeout_signal      },
+                {}
+        };
 
-        r = udev_set_max_log_level(log_val);
+        r = udev_parse_config_full(config_table);
         if (r < 0)
-                log_syntax(NULL, LOG_WARNING, "/etc/udev/udev.conf", 0, r,
-                           "Failed to set udev log level '%s', ignoring: %m", log_val);
-
-        if (children_max) {
-                r = safe_atou(children_max, &manager->children_max);
-                if (r < 0)
-                        log_syntax(NULL, LOG_WARNING, "/etc/udev/udev.conf", 0, r,
-                                   "Failed to parse children_max=%s, ignoring: %m", children_max);
-        }
-
-        if (exec_delay) {
-                r = parse_sec(exec_delay, &manager->exec_delay_usec);
-                if (r < 0)
-                        log_syntax(NULL, LOG_WARNING, "/etc/udev/udev.conf", 0, r,
-                                   "Failed to parse exec_delay=%s, ignoring: %m", exec_delay);
-        }
-
-        if (event_timeout) {
-                r = parse_sec(event_timeout, &manager->timeout_usec);
-                if (r < 0)
-                        log_syntax(NULL, LOG_WARNING, "/etc/udev/udev.conf", 0, r,
-                                   "Failed to parse event_timeout=%s, ignoring: %m", event_timeout);
-        }
-
-        if (resolve_names) {
-                ResolveNameTiming t;
-
-                t = resolve_name_timing_from_string(resolve_names);
-                if (t < 0)
-                        log_syntax(NULL, LOG_WARNING, "/etc/udev/udev.conf", 0, r,
-                                   "Failed to parse resolve_names=%s, ignoring.", resolve_names);
-                else
-                        manager->resolve_name_timing = t;
-        }
+                return r;
 
-        if (timeout_signal) {
-                r = signal_from_string(timeout_signal);
-                if (r < 0)
-                        log_syntax(NULL, LOG_WARNING, "/etc/udev/udev.conf", 0, r,
-                                   "Failed to parse timeout_signal=%s, ignoring: %m", timeout_signal);
-                else
-                        manager->timeout_signal = r;
-        }
+        if (log_val >= 0)
+                log_set_max_level(log_val);
 
         return 0;
 }
index 6e9d86b783dee4b849054ca27a4225e4d161a2a8..98739969aeff576f259a0b8353825c6e6c95e191 100755 (executable)
@@ -871,7 +871,7 @@ def generate_key_cert_pair(
     # supported/expected:
     # https://learn.microsoft.com/en-us/windows-hardware/manufacture/desktop/windows-secure-boot-key-creation-and-management-guidance?view=windows-11#12-public-key-cryptography
 
-    now = datetime.datetime.now(datetime.UTC)
+    now = datetime.datetime.now(datetime.timezone.utc)
 
     key = rsa.generate_private_key(
         public_exponent=65537,
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 {
index b5b5eafae6d0cf0c122e71b714c9b3a25a3f212e..16ece31809782aca18586c224f3dcd790daa5769 100644 (file)
@@ -100,7 +100,7 @@ static int firmware_executable(const char *name, JsonVariant *v, JsonDispatchFla
                 {}
         };
 
-        return json_dispatch(v, table, 0, userdata);
+        return json_dispatch(v, table, flags, userdata);
 }
 
 static int firmware_nvram_template(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) {
@@ -110,7 +110,7 @@ static int firmware_nvram_template(const char *name, JsonVariant *v, JsonDispatc
                 {}
         };
 
-        return json_dispatch(v, table, 0, userdata);
+        return json_dispatch(v, table, flags, userdata);
 }
 
 static int firmware_mapping(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) {
@@ -121,7 +121,7 @@ static int firmware_mapping(const char *name, JsonVariant *v, JsonDispatchFlags
                 {}
         };
 
-        return json_dispatch(v, table, 0, userdata);
+        return json_dispatch(v, table, flags, userdata);
 }
 
 int find_ovmf_config(int search_sb, OvmfConfig **ret) {
@@ -183,7 +183,7 @@ int find_ovmf_config(int search_sb, OvmfConfig **ret) {
                 if (!fwd)
                         return -ENOMEM;
 
-                r = json_dispatch(config_json, table, 0, fwd);
+                r = json_dispatch(config_json, table, JSON_ALLOW_EXTENSIONS, fwd);
                 if (r == -ENOMEM)
                         return r;
                 if (r < 0) {
@@ -191,7 +191,12 @@ int find_ovmf_config(int search_sb, OvmfConfig **ret) {
                         continue;
                 }
 
-                int sb_present = !!strv_find(fwd->features, "secure-boot");
+                if (strv_contains(fwd->features, "enrolled-keys")) {
+                        log_debug("Skipping %s, firmware has enrolled keys which has been known to cause issues", *file);
+                        continue;
+                }
+
+                bool sb_present = strv_contains(fwd->features, "secure-boot");
 
                 /* exclude firmware which doesn't match our Secure Boot requirements */
                 if (search_sb >= 0 && search_sb != sb_present) {
index 53ad7dd3d271363bb705e71c4e8a5a195b967b3c..c931e66aacddf996fe1eadb15e7a7da6bcceaea5 100644 (file)
 #define ARCHITECTURE_SUPPORTS_SMBIOS 0
 #endif
 
+#if defined(__arm__) || defined(__aarch64__)
+#define DEFAULT_SERIAL_TTY "ttyAMA0"
+#elif defined(__s390__) || defined(__s390x__)
+#define DEFAULT_SERIAL_TTY "ttysclp0"
+#elif defined(__powerpc__) || defined(__powerpc64__)
+#define DEFAULT_SERIAL_TTY "hvc0"
+#else
+#define DEFAULT_SERIAL_TTY "ttyS0"
+#endif
+
 typedef struct OvmfConfig {
         char *path;
         char *vars;
index 4ccda8bae8b8d8d1b5526c30ae8e961bdadd7d9c..e2d45d514aaa170777654449bd87304883b77560 100644 (file)
@@ -42,7 +42,7 @@ static char *arg_qemu_smp = NULL;
 static uint64_t arg_qemu_mem = 2ULL * 1024ULL * 1024ULL * 1024ULL;
 static int arg_qemu_kvm = -1;
 static int arg_qemu_vsock = -1;
-static uint64_t arg_vsock_cid = UINT64_MAX;
+static unsigned arg_vsock_cid = VMADDR_CID_ANY;
 static bool arg_qemu_gui = false;
 static int arg_secure_boot = -1;
 static MachineCredentialContext arg_credentials = {};
@@ -69,11 +69,11 @@ static int help(void) {
                "%5$sSpawn a command or OS in a virtual machine.%6$s\n\n"
                "  -h --help                 Show this help\n"
                "     --version              Print version string\n"
-               "     --no-pager             Do not pipe output into a pager\n\n"
-               "%3$sImage:%4$s\n"
+               "     --no-pager             Do not pipe output into a pager\n"
+               "\n%3$sImage:%4$s\n"
                "  -i --image=PATH           Root file system disk image (or device node) for\n"
-               "                            the virtual machine\n\n"
-               "%3$sHost Configuration:%4$s\n"
+               "                            the virtual machine\n"
+               "\n%3$sHost Configuration:%4$s\n"
                "     --qemu-smp=SMP         Configure guest's SMP settings\n"
                "     --qemu-mem=MEM         Configure guest's RAM size\n"
                "     --qemu-kvm=BOOL        Configure whether to use KVM or not\n"
@@ -81,10 +81,10 @@ static int help(void) {
                "     --vsock-cid=           Specify the CID to use for the qemu guest's vsock\n"
                "     --qemu-gui             Start QEMU in graphical mode\n"
                "     --secure-boot=BOOL     Configure whether to search for firmware which\n"
-               "                            supports Secure Boot\n\n"
-               "%3$sSystem Identity:%4$s\n"
+               "                            supports Secure Boot\n"
+               "\n%3$sSystem Identity:%4$s\n"
                "  -M --machine=NAME         Set the machine name for the container\n"
-               "%3$sCredentials:%4$s\n"
+               "\n%3$sCredentials:%4$s\n"
                "     --set-credential=ID:VALUE\n"
                "                            Pass a credential with literal value to container.\n"
                "     --load-credential=ID:PATH\n"
@@ -198,20 +198,21 @@ static int parse_argv(int argc, char *argv[]) {
                                 return log_error_errno(r, "Failed to parse --qemu-vsock=%s: %m", optarg);
                         break;
 
-                case ARG_VSOCK_CID: {
-                        unsigned cid;
+                case ARG_VSOCK_CID:
                         if (isempty(optarg))
-                                cid = VMADDR_CID_ANY;
+                                arg_vsock_cid = VMADDR_CID_ANY;
                         else {
-                                r = safe_atou_bounded(optarg, 3, UINT_MAX - 1, &cid);
-                                if (r == -ERANGE)
-                                        return log_error_errno(r, "Invalid value for --vsock-cid=: %m");
+                                unsigned cid;
+
+                                r = vsock_parse_cid(optarg, &cid);
                                 if (r < 0)
-                                        return log_error_errno(r, "Failed to parse --vsock-cid=%s: %m", optarg);
+                                        return log_error_errno(r, "Failed to parse --vsock-cid: %s", optarg);
+                                if (!VSOCK_CID_IS_REGULAR(cid))
+                                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Specified CID is not regular, refusing: %u", cid);
+
+                                arg_vsock_cid = cid;
                         }
-                        arg_vsock_cid = (uint64_t)cid;
                         break;
-                }
 
                 case ARG_QEMU_GUI:
                         arg_qemu_gui = true;
@@ -499,8 +500,7 @@ static int run_virtual_machine(void) {
         unsigned child_cid = VMADDR_CID_ANY;
         _cleanup_close_ int child_vsock_fd = -EBADF;
         if (use_vsock) {
-                if (arg_vsock_cid < UINT_MAX)
-                        child_cid = (unsigned)arg_vsock_cid;
+                child_cid = arg_vsock_cid;
 
                 r = vsock_fix_child_cid(&child_cid, arg_machine, &child_vsock_fd);
                 if (r < 0)
@@ -516,25 +516,25 @@ static int run_virtual_machine(void) {
                         return log_oom();
         }
 
-        r = strv_extend_strv(&cmdline, STRV_MAKE("-cpu", "max"), /* filter_duplicates= */ false);
+        r = strv_extend_many(&cmdline, "-cpu", "max");
         if (r < 0)
                 return log_oom();
 
-        if (arg_qemu_gui) {
-                r = strv_extend_strv(&cmdline, STRV_MAKE("-vga", "virtio"),  /* filter_duplicates= */ false);
-                if (r < 0)
-                        return log_oom();
-        } else {
-                r = strv_extend_strv(&cmdline, STRV_MAKE(
-                        "-nographic",
-                        "-nodefaults",
-                        "-chardev", "stdio,mux=on,id=console,signal=off",
-                        "-serial", "chardev:console",
-                        "-mon", "console"
-                ),  /* filter_duplicates= */ false);
-                if (r < 0)
-                        return log_oom();
-        }
+        if (arg_qemu_gui)
+                r = strv_extend_many(
+                                &cmdline,
+                                "-vga",
+                                "virtio");
+        else
+                r = strv_extend_many(
+                                &cmdline,
+                                "-nographic",
+                                "-nodefaults",
+                                "-chardev", "stdio,mux=on,id=console,signal=off",
+                                "-serial", "chardev:console",
+                                "-mon", "console");
+        if (r < 0)
+                return log_oom();
 
         if (ARCHITECTURE_SUPPORTS_SMBIOS)
                 FOREACH_ARRAY(cred, arg_credentials.credentials, arg_credentials.n_credentials) {
@@ -588,11 +588,11 @@ static int run_virtual_machine(void) {
                 (void) copy_access(source_fd, target_fd);
                 (void) copy_times(source_fd, target_fd, 0);
 
-                r = strv_extend_strv(&cmdline, STRV_MAKE(
-                        "-global", "ICH9-LPC.disable_s3=1",
-                        "-global", "driver=cfi.pflash01,property=secure,value=on",
-                        "-drive"
-                ),  /* filter_duplicates= */ false);
+                r = strv_extend_many(
+                                &cmdline,
+                                "-global", "ICH9-LPC.disable_s3=1",
+                                "-global", "driver=cfi.pflash01,property=secure,value=on",
+                                "-drive");
                 if (r < 0)
                         return log_oom();
 
@@ -609,29 +609,31 @@ static int run_virtual_machine(void) {
         if (r < 0)
                 return log_oom();
 
-        r = strv_extend_strv(&cmdline, STRV_MAKE(
-                "-device", "virtio-scsi-pci,id=scsi",
-                "-device", "scsi-hd,drive=mkosi,bootindex=1"
-        ),  /* filter_duplicates= */ false);
+        r = strv_extend_many(
+                        &cmdline,
+                        "-device", "virtio-scsi-pci,id=scsi",
+                        "-device", "scsi-hd,drive=mkosi,bootindex=1");
         if (r < 0)
                 return log_oom();
 
-        if (!strv_isempty(arg_parameters)) {
-                if (ARCHITECTURE_SUPPORTS_SMBIOS) {
-                        _cleanup_free_ char *kcl = strv_join(arg_parameters, " ");
-                        if (!kcl)
-                                return log_oom();
+        r = strv_prepend(&arg_parameters, "console=" DEFAULT_SERIAL_TTY);
+        if (r < 0)
+                return log_oom();
 
-                        r = strv_extend(&cmdline, "-smbios");
-                        if (r < 0)
-                                return log_oom();
+        if (ARCHITECTURE_SUPPORTS_SMBIOS) {
+                _cleanup_free_ char *kcl = strv_join(arg_parameters, " ");
+                if (!kcl)
+                        return log_oom();
 
-                        r = strv_extendf(&cmdline, "type=11,value=io.systemd.stub.kernel-cmdline-extra=%s", kcl);
-                        if (r < 0)
-                                return log_oom();
-                } else
-                        log_warning("Cannot append extra args to kernel cmdline, native architecture doesn't support SMBIOS");
-        }
+                r = strv_extend(&cmdline, "-smbios");
+                if (r < 0)
+                        return log_oom();
+
+                r = strv_extendf(&cmdline, "type=11,value=io.systemd.stub.kernel-cmdline-extra=%s", kcl);
+                if (r < 0)
+                        return log_oom();
+        } else
+                log_warning("Cannot append extra args to kernel cmdline, native architecture doesn't support SMBIOS");
 
         if (use_vsock) {
                 vsock_fd = open_vsock();
@@ -675,7 +677,6 @@ static int run_virtual_machine(void) {
                 _exit(EXIT_FAILURE);
         }
 
-
         int exit_status = INT_MAX;
         if (use_vsock) {
                 r = setup_notify_parent(event, vsock_fd, &exit_status, &notify_event_source);
@@ -684,10 +685,10 @@ static int run_virtual_machine(void) {
         }
 
         /* shutdown qemu when we are shutdown */
-        (void) sd_event_add_signal(event, NULL, SIGINT, on_orderly_shutdown, PID_TO_PTR(child_pid));
-        (void) sd_event_add_signal(event, NULL, SIGTERM, on_orderly_shutdown, PID_TO_PTR(child_pid));
+        (void) sd_event_add_signal(event, NULL, SIGINT | SD_EVENT_SIGNAL_PROCMASK, on_orderly_shutdown, PID_TO_PTR(child_pid));
+        (void) sd_event_add_signal(event, NULL, SIGTERM | SD_EVENT_SIGNAL_PROCMASK, on_orderly_shutdown, PID_TO_PTR(child_pid));
 
-        (void) sd_event_add_signal(event, NULL, SIGRTMIN+18, sigrtmin18_handler, NULL);
+        (void) sd_event_add_signal(event, NULL, (SIGRTMIN+18) | SD_EVENT_SIGNAL_PROCMASK, sigrtmin18_handler, NULL);
 
         /* Exit when the child exits */
         (void) sd_event_add_child(event, NULL, child_pid, WEXITED, on_child_exit, NULL);
@@ -713,7 +714,7 @@ static int determine_names(void) {
         int r;
 
         if (!arg_image)
-                return log_error_errno(SYNTHETIC_ERRNO(-EINVAL), "Missing required argument -i/--image=, quitting");
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Missing required argument -i/--image=, quitting");
 
         if (!arg_machine) {
                 char *e;
@@ -748,7 +749,7 @@ static int run(int argc, char *argv[]) {
         if (r < 0)
                 return r;
 
-        assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGCHLD, SIGTERM, SIGINT, SIGRTMIN+18, -1) >= 0);
+        assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGCHLD, -1) >= 0);
 
         return run_virtual_machine();
 }
index 0ae8fe322dd1cc0edf05afa9bec96a0bb26357d5..f3afc33a5897efb74e08c5780c59173ec88abc4b 100644 (file)
@@ -7,8 +7,8 @@
 #include "format-table.h"
 #include "fs-util.h"
 #include "main-func.h"
-#include "path-util.h"
 #include "parse-util.h"
+#include "path-util.h"
 #include "pretty-print.h"
 #include "stat-util.h"
 #include "string-util.h"
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 e3eb62f198cb7ee7d1655d57fd43c9190f41a32b..d870d57dccf104f123db7f92302fa75c6b624f7a 100755 (executable)
@@ -11,6 +11,10 @@ NSPAWN_ARGUMENTS="--private-network"
 # (Hopefully) a temporary workaround for https://github.com/systemd/systemd/issues/30573
 KERNEL_APPEND="${KERNEL_APPEND:-} SYSTEMD_DEFAULT_MOUNT_RATE_LIMIT_BURST=100"
 
+# Make sure vsock is available in the VM
+CID=$((RANDOM + 3))
+QEMU_OPTIONS+=" -device vhost-vsock-pci,guest-cid=$CID"
+
 test_append_files() {
     local workspace="${1:?}"
 
@@ -25,6 +29,16 @@ test_append_files() {
         install_mdadm
         generate_module_dependencies
     fi
+
+    inst_binary socat
+    inst_binary ssh
+    inst_binary sshd
+    inst_binary ssh-keygen
+    inst_binary usermod
+    instmods vmw_vsock_virtio_transport
+    instmods vsock_loopback
+    instmods vmw_vsock_vmci_transport
+    generate_module_dependencies
 }
 
 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 86a76a8d469a0d70d3db728b810b93fccd172071..92bb7d532baab1cb3ded39ee40d8c2280649be62 100644 (file)
@@ -120,6 +120,7 @@ SYSTEMD="${SYSTEMD:-$(command -v "$BUILD_DIR/systemd" || command -v "$ROOTLIBDIR
 SYSTEMD_NSPAWN="${SYSTEMD_NSPAWN:-$(command -v "$BUILD_DIR/systemd-nspawn" || command -v systemd-nspawn)}"
 JOURNALCTL="${JOURNALCTL:-$(command -v "$BUILD_DIR/journalctl" || command -v journalctl)}"
 SYSTEMCTL="${SYSTEMCTL:-$(command -v "$BUILD_DIR/systemctl" || command -v systemctl)}"
+SYSTEMD_ID128="${SYSTEMD_ID128:-$(command -v "$BUILD_DIR/systemd-id128" || command -v systemd-id128)}"
 
 TESTFILE="${BASH_SOURCE[1]}"
 if [ -z "$TESTFILE" ]; then
@@ -995,7 +996,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,9 +1152,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
-        # Note: try both 60-kpartx.rules (as seen on Debian Sid with 0.9.4-7) and 90-kpartx.rules (as seen on
+        # 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 90-kpartx.rules
+        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
@@ -1653,7 +1654,7 @@ create_empty_image() {
     sfdisk "$LOOPDEV" <<EOF
 label: gpt
 type=C12A7328-F81F-11D2-BA4B-00A0C93EC93B name=esp  size=${esp_size}M
-type=0FC63DAF-8483-4772-8E79-3D69D8477DE4 name=root size=${root_size}M bootable
+type=$("${SYSTEMD_ID128:?}" show root -Pu) name=root size=${root_size}M bootable
 type=BC13C2FF-59E6-4262-A352-B275FD6F7172 name=boot size=${boot_size}M
 type=0FC63DAF-8483-4772-8E79-3D69D8477DE4 name=data
 EOF
@@ -2196,14 +2197,14 @@ install_dbus() {
     # Newer Fedora versions use dbus-broker by default. Let's install it if it's available.
     if [ -f "$ROOTLIBDIR/system/dbus-broker.service" ]; then
         inst "$ROOTLIBDIR/system/dbus-broker.service"
-        inst_symlink /etc/systemd/system/dbus.service
         inst /usr/bin/dbus-broker
         inst /usr/bin/dbus-broker-launch
+        image_install -o {/etc,/usr/lib}/systemd/system/dbus.service
     elif [ -f "$ROOTLIBDIR/system/dbus-daemon.service" ]; then
         # Fedora rawhide replaced dbus.service with dbus-daemon.service
         inst "$ROOTLIBDIR/system/dbus-daemon.service"
         # Alias symlink
-        inst_symlink /etc/systemd/system/dbus.service
+        image_install -o {/etc,/usr/lib}/systemd/system/dbus.service
     else
         inst "$ROOTLIBDIR/system/dbus.service"
     fi
@@ -2262,12 +2263,12 @@ EOF
     # Newer Fedora versions use dbus-broker by default. Let's install it if it's available.
     if [ -f "$userunitdir/dbus-broker.service" ]; then
         inst "$userunitdir/dbus-broker.service"
-        inst_symlink /etc/systemd/user/dbus.service
+        image_install -o {/etc,/usr/lib}/systemd/user/dbus.service
     elif [ -f "${ROOTLIBDIR:?}/system/dbus-daemon.service" ]; then
         # Fedora rawhide replaced dbus.service with dbus-daemon.service
         inst "$userunitdir/dbus-daemon.service"
         # Alias symlink
-        inst_symlink /etc/systemd/user/dbus.service
+        image_install -o {/etc,/usr/lib}/systemd/user/dbus.service
     else
         inst "$userunitdir/dbus.service"
     fi
diff --git a/test/test-network/conf/25-agent-bridge-port.network b/test/test-network/conf/25-agent-bridge-port.network
new file mode 100644 (file)
index 0000000..709a783
--- /dev/null
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Match]
+Name=client-peer
+
+[Network]
+Bridge=bridge-relay
+IPv6AcceptRA=no
diff --git a/test/test-network/conf/25-agent-bridge.netdev b/test/test-network/conf/25-agent-bridge.netdev
new file mode 100644 (file)
index 0000000..a611337
--- /dev/null
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[NetDev]
+Name=bridge-relay
+Kind=bridge
diff --git a/test/test-network/conf/25-agent-bridge.network b/test/test-network/conf/25-agent-bridge.network
new file mode 100644 (file)
index 0000000..8383790
--- /dev/null
@@ -0,0 +1,12 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Match]
+Name=bridge-relay
+
+[Network]
+Address=192.168.2.1/24
+DHCPServer=yes
+IPv6AcceptRA=no
+
+[DHCPServer]
+RelayTarget=192.168.1.1
+RelayAgentRemoteId=string:aabbccdd
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-nexthop-test1.network b/test/test-network/conf/25-nexthop-test1.network
new file mode 100644 (file)
index 0000000..5a4c596
--- /dev/null
@@ -0,0 +1,12 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Match]
+Name=test1
+
+[Network]
+Address=192.168.20.21/24
+IPv6AcceptRA=no
+
+[Route]
+Destination=10.10.11.10
+# Nexthop 21 is configured as a group nexthop of 1 and 20
+NextHop=21
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..7bf2f5014530413d9e01c03dfd2b00046694b6c2 100755 (executable)
@@ -12,6 +12,7 @@ import itertools
 import json
 import os
 import pathlib
+import random
 import re
 import shutil
 import signal
@@ -27,6 +28,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 +300,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 +714,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 +734,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 +749,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 +926,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 +933,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 +964,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 +1005,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 +1106,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 +1116,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 +1168,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 +1178,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 +1191,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 +1229,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 +1255,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 +1263,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 +1276,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 +1290,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 +1302,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 +1329,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 +1339,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 +1347,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 +1356,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 +1364,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 +1443,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 +1476,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)
 
@@ -1739,6 +1772,8 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities):
         start_networkd()
 
         self.wait_online(['vcan99:carrier', 'vcan98:carrier'])
+        # For can devices, 'carrier' is the default required operational state.
+        self.wait_online(['vcan99', 'vcan98'])
 
         # https://github.com/systemd/systemd/issues/30140
         output = check_output('ip -d link show vcan99')
@@ -1755,9 +1790,15 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities):
         start_networkd()
 
         self.wait_online(['vxcan99:carrier', 'vxcan-peer:carrier'])
+        # For can devices, 'carrier' is the default required operational state.
+        self.wait_online(['vxcan99', 'vxcan-peer'])
 
     @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',
@@ -1787,7 +1828,7 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities):
 
         output = check_output('ip -4 route show dev wg99 table 1234')
         print(output)
-        self.assertIn('192.168.26.0/24 proto static metric 123', output)
+        self.assertIn('192.168.26.0/24 proto static scope link metric 123', output)
 
         output = check_output('ip -6 route show dev wg99 table 1234')
         print(output)
@@ -2323,7 +2364,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 +2605,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 +2931,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 +2955,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 +3000,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 +3128,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 +3231,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 +3355,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 +3486,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 +3515,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)
-
-    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'])
+        self.assertNotIn('00:00:5e:00:02:67', output)
 
-        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)
+        check_json(networkctl_json())
 
-        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 +3838,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 +3862,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 +3888,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 +3924,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 +4004,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-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.network')
+        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 +4027,42 @@ 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')
+        # Of course, networkctl_reconfigure() below is unnecessary in normal operation, but it is intentional
+        # here to test reconfiguring with different .network files does not trigger race.
+        # See also comments in link_drop_requests().
+        networkctl_reconfigure('dummy98') # reconfigured with 25-nexthop-dummy-2.network
+        networkctl_reload()               # reconfigured with 25-nexthop-dummy-1.network
+
+        self.check_nexthop(manage_foreign_nexthops, first=True)
+
+        # Remove nexthop with ID 20
+        check_output('ip nexthop del id 20')
+        copy_network_unit('11-dummy.netdev', '25-nexthop-test1.network')
         networkctl_reload()
 
-        self.check_nexthop(manage_foreign_nexthops)
+        # 25-nexthop-test1.network requests a route with nexthop ID 21,
+        # which is silently removed by the kernel when nexthop with ID 20 is removed in the above,
+        # hence test1 should be stuck in the configuring state.
+        self.wait_operstate('test1', operstate='routable', setup_state='configuring')
+
+        # Wait for a while, and check if the interface is still in the configuring state.
+        time.sleep(1)
+        output = networkctl_status('test1')
+        self.assertIn('State: routable (configuring)', output)
+
+        # Reconfigure the interface that has nexthop with ID 20 and 21,
+        # then the route requested by test1 can be configured.
+        networkctl_reconfigure('dummy98')
+        self.wait_online(['test1:routable'])
+
+        # Check if the requested route actually configured.
+        output = check_output('ip route show 10.10.11.10')
+        print(output)
+        self.assertIn('10.10.11.10 nhid 21 proto static', output)
+        self.assertIn('nexthop via 192.168.5.1 dev veth99 weight 3', output)
+        self.assertIn('nexthop via 192.168.20.1 dev dummy98 weight 1', output)
 
         remove_link('veth99')
         time.sleep(2)
@@ -4261,10 +4367,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 +4390,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 +4409,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 +4423,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 +4576,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 +4914,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 +5094,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 +5117,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')
 
@@ -5025,24 +5141,42 @@ class NetworkdRATests(unittest.TestCase, Utilities):
 
         self.teardown_nftset('addr6', 'network6', 'ifindex')
 
-    def test_ipv6_token_static(self):
-        copy_network_unit('25-veth.netdev', '25-ipv6-prefix.network', '25-ipv6-prefix-veth-token-static.network')
-        start_networkd()
+    def check_ipv6_token_static(self):
         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')
         self.assertRegex(output, '2002:da8:2:0:1a:2b:3c:4d')
         self.assertRegex(output, '2002:da8:2:0:fa:de:ca:fe')
 
+    def test_ipv6_token_static(self):
+        copy_network_unit('25-veth.netdev', '25-ipv6-prefix.network', '25-ipv6-prefix-veth-token-static.network')
+        start_networkd()
+
+        self.check_ipv6_token_static()
+
+        for _ in range(20):
+            check_output('ip link set veth99 down')
+            check_output('ip link set veth99 up')
+
+        self.check_ipv6_token_static()
+
+        for _ in range(20):
+            check_output('ip link set veth99 down')
+            time.sleep(random.uniform(0, 0.1))
+            check_output('ip link set veth99 up')
+            time.sleep(random.uniform(0, 0.1))
+
+        self.check_ipv6_token_static()
+
     def test_ipv6_token_prefixstable(self):
         copy_network_unit('25-veth.netdev', '25-ipv6-prefix.network', '25-ipv6-prefix-veth-token-prefixstable.network')
         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 +5186,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 +5258,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 +5294,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 +5311,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 +5334,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 +5350,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 +5362,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 +5373,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 +5383,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,10 +5407,28 @@ 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\)')
 
+    def test_replay_agent_on_bridge(self):
+        copy_network_unit('25-agent-bridge.netdev',
+                          '25-agent-veth-client.netdev',
+                          '25-agent-bridge.network',
+                          '25-agent-bridge-port.network',
+                          '25-agent-client.network')
+        start_networkd()
+        self.wait_online(['bridge-relay:routable', 'client-peer:enslaved'])
+
+        # For issue #30763.
+        expect = 'bridge-relay: DHCPv4 server: STARTED'
+        for _ in range(20):
+            if expect in read_networkd_log():
+                break
+            time.sleep(0.5)
+        else:
+            self.fail()
+
 class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
 
     def setUp(self):
@@ -5332,8 +5484,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 +5511,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 +5538,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 +5564,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 +5591,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 +5631,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 +5738,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 +5833,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 +6055,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 +6232,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 +6296,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 +6309,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 +6340,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 +6378,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 +6941,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 +6987,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)
 
index bdb1c8e2d362480db2a8a93ec1fd719fc6f46c06..8427f6849bfd9e7921bf9a9a2af40678c9ab1415 100644 (file)
@@ -20,10 +20,10 @@ islink() {
     test "$(readlink "$1")" = "$2" || return 2
 }
 
-: '------enable nonexistent------------------------------------'
+: '-------enable nonexistent--------------------------------------'
 ( ! "$systemctl" --root="$root" enable test1.service )
 
-: '------basic enablement--------------------------------------'
+: '-------basic enablement----------------------------------------'
 mkdir -p "$root/etc/systemd/system"
 cat >"$root/etc/systemd/system/test1.service" <<EOF
 [Install]
@@ -43,7 +43,7 @@ test -h "$root/etc/systemd/system/special.target.requires/test1.service"
 test ! -h "$root/etc/systemd/system/default.target.wants/test1.service"
 test ! -h "$root/etc/systemd/system/special.target.requires/test1.service"
 
-: '------enable when link already exists-----------------------'
+: '-------enable when link already exists-------------------------'
 # We don't read the symlink target, so it's OK for the symlink to point
 # to something else. We should just silently accept this.
 
@@ -64,7 +64,7 @@ test -h "$root/etc/systemd/system/special.target.requires/test1.service"
 test ! -h "$root/etc/systemd/system/default.target.wants/test1.service"
 test ! -h "$root/etc/systemd/system/special.target.requires/test1.service"
 
-: '------suffix guessing---------------------------------------'
+: '-------suffix guessing-----------------------------------------'
 "$systemctl" --root="$root" enable test1
 test -h "$root/etc/systemd/system/default.target.wants/test1.service"
 test -h "$root/etc/systemd/system/special.target.requires/test1.service"
@@ -77,7 +77,7 @@ test -h "$root/etc/systemd/system/special.target.requires/test1.service"
 test ! -e "$root/etc/systemd/system/default.target.wants/test1.service"
 test ! -e "$root/etc/systemd/system/special.target.requires/test1.service"
 
-: '-------aliases----------------------------------------------'
+: '-------aliases-------------------------------------------------'
 cat >>"$root/etc/systemd/system/test1.service" <<EOF
 Alias=test1-goodalias.service
 Alias=test1@badalias.service
@@ -97,7 +97,7 @@ test ! -e "$root/etc/systemd/system/test1-badalias.target"
 test ! -e "$root/etc/systemd/system/test1-badalias.socket"
 test -h "$root/etc/systemd/system/test1-goodalias2.service"
 
-: '-------aliases in reenable----------------------------------'
+: '-------aliases in reenable-------------------------------------'
 ( ! "$systemctl" --root="$root" reenable test1 )
 test -h "$root/etc/systemd/system/default.target.wants/test1.service"
 test ! -e "$root/etc/systemd/system/test1-goodalias.service"
@@ -112,7 +112,7 @@ test ! -e "$root/etc/systemd/system/default.target.wants/test1.service"
 test ! -e "$root/etc/systemd/system/special.target.requires/test1.service"
 test ! -e "$root/etc/systemd/system/test1-goodalias.service"
 
-: '-------aliases when link already exists---------------------'
+: '-------aliases when link already exists------------------------'
 cat >"$root/etc/systemd/system/test1a.service" <<EOF
 [Install]
 Alias=test1a-alias.service
@@ -126,7 +126,7 @@ test -h "$root/etc/systemd/system/test1a-alias.service"
 "$systemctl" --root="$root" disable test1a.service
 test ! -h "$root/etc/systemd/system/test1a-alias.service"
 
-: '-------also units-------------------------------------------'
+: '-------also units----------------------------------------------'
 cat >"$root/etc/systemd/system/test2.socket" <<EOF
 [Install]
 WantedBy=sockets.target
@@ -152,7 +152,7 @@ test ! -e "$root/etc/systemd/system/default.target.wants/test2.service"
 test ! -e "$root/etc/systemd/system/sockets.target.wants/test2.socket"
 
 
-: '-------link-------------------------------------------------'
+: '-------link----------------------------------------------------'
 # File doesn't exist yet
 test ! -e "$root/link1.path"
 ( ! "$systemctl" --root="$root" link '/link1.path' )
@@ -166,65 +166,65 @@ EOF
 "$systemctl" --root="$root" link '/link1.path'
 islink "$root/etc/systemd/system/link1.path" "/link1.path"
 
-: '-------link already linked same path------------------------'
+: '-------link already linked same path---------------------------'
 SYSTEMD_LOG_LEVEL=debug "$systemctl" --root="$root" link '/link1.path'  # this passes
 islink "$root/etc/systemd/system/link1.path" "/link1.path"
 
-: '-------link already linked different path-------------------'
+: '-------link already linked different path----------------------'
 mkdir "$root/subdir"
 cp "$root/link1.path" "$root/subdir/"
 ( ! "$systemctl" --root="$root" link '/subdir/link1.path' )
 islink "$root/etc/systemd/system/link1.path" "/link1.path"
 
-: '-------link bad suffix--------------------------------------'
+: '-------link bad suffix-----------------------------------------'
 cp "$root/link1.path" "$root/subdir/link1.suffix"
 ( ! "$systemctl" --root="$root" link '/subdir/link1.suffix' )
 test ! -e "$root/etc/systemd/system/link1.suffix"
 
-: '-------unlink by unit name----------------------------------'
+: '-------unlink by unit name-------------------------------------'
 "$systemctl" --root="$root" disable 'link1.path'
 test ! -e "$root/etc/systemd/system/link1.path"
 
-: '-------unlink by path---------------------------------------'
+: '-------unlink by path------------------------------------------'
 "$systemctl" --root="$root" link '/link1.path'
 test -h "$root/etc/systemd/system/link1.path"
 "$systemctl" --root="$root" disable '/link1.path'
 test ! -e "$root/etc/systemd/system/link1.path"
 
-: '-------unlink by wrong path---------------------------------'
+: '-------unlink by wrong path------------------------------------'
 "$systemctl" --root="$root" link '/link1.path'
 test -h "$root/etc/systemd/system/link1.path"
 "$systemctl" --root="$root" disable '/subdir/link1.path'  # we only care about the name
 test ! -e "$root/etc/systemd/system/link1.path"
 
 
-: '-------link and enable--------------------------------------'
+: '-------link and enable-----------------------------------------'
 "$systemctl" --root="$root" enable '/link1.path'
 islink "$root/etc/systemd/system/link1.path" "/link1.path"
 islink "$root/etc/systemd/system/paths.target.wants/link1.path" "/link1.path"
 
-: '-------enable already linked same path----------------------'
+: '-------enable already linked same path-------------------------'
 "$systemctl" --root="$root" enable '/link1.path'
 islink "$root/etc/systemd/system/link1.path" "/link1.path"
 islink "$root/etc/systemd/system/paths.target.wants/link1.path" "/link1.path"
 
-: '-------enable already linked different path-----------------'
+: '-------enable already linked different path--------------------'
 ( ! "$systemctl" --root="$root" enable '/subdir/link1.path' )
 islink "$root/etc/systemd/system/link1.path" "/link1.path"
 islink "$root/etc/systemd/system/paths.target.wants/link1.path" "/link1.path"
 
-: '-------enable bad suffix------------------------------------'
+: '-------enable bad suffix---------------------------------------'
 cp "$root/link1.path" "$root/subdir/link1.suffix"
 ( ! "$systemctl" --root="$root" enable '/subdir/link1.suffix' )
 test ! -e "$root/etc/systemd/system/link1.suffix"
 test ! -e "$root/etc/systemd/system/paths.target.wants/link1.suffix"
 
-: '-------disable by unit name---------------------------------'
+: '-------disable by unit name------------------------------------'
 "$systemctl" --root="$root" disable 'link1.path'
 test ! -e "$root/etc/systemd/system/link1.path"
 test ! -e "$root/etc/systemd/system/paths.target.wants/link1.path"
 
-: '-------disable by path--------------------------------------'
+: '-------disable by path-----------------------------------------'
 "$systemctl" --root="$root" enable '/link1.path'
 test -h "$root/etc/systemd/system/link1.path"
 test -h "$root/etc/systemd/system/paths.target.wants/link1.path"
@@ -233,7 +233,7 @@ test ! -e "$root/etc/systemd/system/link1.path"
 test ! -e "$root/etc/systemd/system/paths.target.wants/link1.path"
 
 
-: '-------link and enable-------------------------------------'
+: '-------link and enable-----------------------------------------'
 "$systemctl" --root="$root" link '/link1.path'
 islink "$root/etc/systemd/system/link1.path" "/link1.path"
 test ! -h "$root/etc/systemd/system/paths.target.wants/link1.path"
@@ -246,7 +246,7 @@ islink "$root/etc/systemd/system/paths.target.wants/link1.path" "/link1.path"
 islink "$root/etc/systemd/system/link1.path" "/link1.path"
 islink "$root/etc/systemd/system/paths.target.wants/link1.path" "/link1.path"
 
-: '-------link instance and enable-------------------------------------'
+: '-------link instance and enable--------------------------------'
 cat >"$root/link-instance@.service" <<EOF
 [Service]
 ExecStart=true
@@ -269,7 +269,7 @@ islink "$root/etc/systemd/system/services.target.wants/link-instance@first.servi
 test ! -h "$root/etc/systemd/system/link-instance@first.service"
 test ! -h "$root/etc/systemd/system/services.target.wants/link-instance@first.service"
 
-: '-------manual link------------------------------------------'
+: '-------manual link---------------------------------------------'
 cat >"$root/link3.suffix" <<EOF
 [Install]
 WantedBy=services.target
@@ -286,18 +286,18 @@ SYSTEMD_LOG_LEVEL=debug SYSTEMD_LOG_LOCATION=1 "$systemctl" --root="$root" disab
 test ! -h "$root/etc/systemd/system/link3.service"
 test ! -h "$root/etc/systemd/system/services.target.wants/link3.service"
 
-: '-------enable on masked-------------------------------------'
+: '-------enable on masked----------------------------------------'
 ln -s "/dev/null" "$root/etc/systemd/system/masked.service"
 ( ! "$systemctl" --root="$root" enable 'masked.service' )
 ( ! "$systemctl" --root="$root" enable '/etc/systemd/system/masked.service' )
 
-: '-------enable on masked alias-------------------------------'
+: '-------enable on masked alias----------------------------------'
 test -h "$root/etc/systemd/system/masked.service"
 ln -s "masked.service" "$root/etc/systemd/system/masked-alias.service"
 ( ! "$systemctl" --root="$root" enable 'masked-alias.service' )
 ( ! "$systemctl" --root="$root" enable '/etc/systemd/system/masked-alias.service' )
 
-: '-------issue 22000: link in subdirectory--------------------'
+: '-------issue 22000: link in subdirectory-----------------------'
 mkdir -p "$root/etc/systemd/system/myown.d"
 cat >"$root/etc/systemd/system/link5-also.service" <<EOF
 [Install]
@@ -318,7 +318,7 @@ test ! -h "$root/etc/systemd/system/services.target.wants/link5-also.service"
 test ! -h "$root/etc/systemd/system/services.target.wants/link5.service"
 islink "$root/etc/systemd/system/services.target.wants/link5-also.service" "/etc/systemd/system/link5-also.service"
 
-: '-------template enablement----------------------------------'
+: '-------template enablement-------------------------------------'
 cat >"$root/etc/systemd/system/templ1@.service" <<EOF
 [Install]
 WantedBy=services.target
@@ -352,7 +352,7 @@ test ! -h "$root/etc/systemd/system/services.target.wants/templ1@.service"
 test ! -h "$root/etc/systemd/system/services.target.wants/templ1@one.service"
 test ! -h "$root/etc/systemd/system/services.target.wants/templ1@two.service"
 
-: '-------template enablement w/ default instance--------------'
+: '-------template enablement w/ default instance-----------------'
 cat >"$root/etc/systemd/system/templ1@.service" <<EOF
 [Install]
 # check enablement with
@@ -401,7 +401,7 @@ test ! -h "$root/etc/systemd/system/other@templ1.target.requires/templ1@one.serv
 test ! -h "$root/etc/systemd/system/services.target.wants/templ1@two.service"
 test ! -h "$root/etc/systemd/system/other@templ1.target.requires/templ1@two.service"
 
-: '-------removal of relative enablement symlinks--------------'
+: '-------removal of relative enablement symlinks-----------------'
 test ! -h "$root/etc/systemd/system/services.target.wants/templ1@.service"
 ln -s '../templ1@one.service' "$root/etc/systemd/system/services.target.wants/templ1@one.service"
 ln -s 'templ1@two.service' "$root/etc/systemd/system/services.target.wants/templ1@two.service"
@@ -421,7 +421,7 @@ test ! -h "$root/etc/systemd/system/services.target.wants/templ1@five.service"
 test ! -h "$root/etc/systemd/system/services.target.wants/templ1@six.service"
 test ! -h "$root/etc/systemd/system/services.target.wants/templ1@seven.service"
 
-: '-------template enablement for another template-------------'
+: '-------template enablement for another template----------------'
 cat >"$root/etc/systemd/system/templ2@.service" <<EOF
 [Install]
 RequiredBy=another-template@.target
@@ -446,7 +446,7 @@ test ! -h "$root/etc/systemd/system/another-template@.target.requires/templ2@two
 test ! -h "$root/etc/systemd/system/another-template@.target.requires/templ2@.service"
 test ! -h "$root/etc/systemd/system/another-template@.target.requires/templ2@two.service"
 
-: '-------aliases w/ and w/o instance--------------------------'
+: '-------aliases w/ and w/o instance-----------------------------'
 test ! -e "$root/etc/systemd/system/link4.service"
 cat >"$root/etc/systemd/system/link4.service" <<EOF
 [Install]
@@ -471,7 +471,7 @@ test ! -h "$root/etc/systemd/system/link4@inst.service"
 test ! -h "$root/etc/systemd/system/link4alias.service"
 test ! -h "$root/etc/systemd/system/link4alias2.service"
 
-: '-------systemctl enable on path to unit file----------------'
+: '-------systemctl enable on path to unit file-------------------'
 cat >"$root/etc/systemd/system/link4.service" <<EOF
 [Install]
 Alias=link4alias.service
@@ -489,7 +489,7 @@ test ! -h "$root/etc/systemd/system/link4.service"
 test ! -h "$root/etc/systemd/system/link4alias.service"
 test ! -h "$root/etc/systemd/system/link4alias2.service"
 
-: '-------issue 661: enable on unit file--------------'
+: '-------issue 661: enable on unit file--------------------------'
 test ! -e "$root/etc/systemd/system/link5.service"
 cat >"$root/etc/systemd/system/link5.service" <<EOF
 [Install]
@@ -507,7 +507,7 @@ islink "$root/etc/systemd/system/link5alias2.service" "/etc/systemd/system/link5
 test ! -h "$root/etc/systemd/system/link5alias.service"
 test ! -h "$root/etc/systemd/system/link5alias2.service"
 
-: '-------issue 661: link and enable on unit file--------------'
+: '-------issue 661: link and enable on unit file-----------------'
 test ! -e "$root/etc/systemd/system/link5copy.service"
 cat >"$root/link5copy.service" <<EOF
 [Install]
@@ -542,7 +542,7 @@ test ! -h "$root/etc/systemd/system/link5copy.service"
 test ! -h "$root/etc/systemd/system/link5alias.service"
 test ! -h "$root/etc/systemd/system/link5alias2.service"
 
-: '----issue 19437: plain templates in .wants/ or .requires/---'
+: '-------issue 19437: plain templates in .wants/ or .requires/---'
 test ! -e "$root/etc/systemd/system/link5@.path"
 cat >"$root/etc/systemd/system/link5@.path" <<EOF
 [Install]
@@ -566,7 +566,7 @@ test ! -h "$root/etc/systemd/system/target5@.target.requires/link5@.path"
 test ! -h "$root/etc/systemd/system/target5@inst.target.wants/link5@.path"
 test ! -h "$root/etc/systemd/system/target5@inst.target.requires/link5@.path"
 
-: '-------removal of symlinks not listed in [Install]----------'
+: '-------removal of symlinks not listed in [Install]-------------'
 # c.f. 66a19d85a533b15ed32f4066ec880b5a8c06babd
 test ! -e "$root/etc/systemd/system/multilink.mount"
 cat >"$root/etc/systemd/system/multilink.mount" <<EOF
@@ -585,7 +585,7 @@ test ! -h "$root/etc/systemd/system/default.target.wants/"
 test ! -h "$root/etc/systemd/system/multilink-alias.mount"
 test ! -h "$root/etc/systemd/system/multilink-badalias.service"
 
-: '-------merge 20017: specifiers in the unit file-------------'
+: '-------merge 20017: specifiers in the unit file----------------'
 test ! -e "$root/etc/systemd/system/some-some-link6@.socket"
 # c.f. de61a04b188f81a85cdb5c64ddb4987dcd9d30d3
 
@@ -689,7 +689,7 @@ uname -r | grep -q '[^a-zA-Z0-9_.\\-]' || \
 # %z is not defined
 ( ! check_alias z 'z' )
 
-: '-------specifiers in WantedBy-------------------------------'
+: '-------specifiers in WantedBy----------------------------------'
 # We don't need to repeat all the tests. Let's do a basic check that specifier
 # expansion is performed.
 
@@ -715,7 +715,7 @@ test ! -h "$root/etc/systemd/system/another-target2@.target.requires/some-some-l
 
 # TODO: repeat the tests above for presets
 
-: '-------SYSTEMD_OS_RELEASE relative to root-------------------'
+: '-------SYSTEMD_OS_RELEASE relative to root---------------------'
 # check that os-release overwriting works as expected with root
 test -e "$root/etc/os-release"
 
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
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 d19d85074f51e1bae00825fb9d241b5f72df4b1f..d6b31622588fca4b8b63cbf3fea44689a068a11c 100755 (executable)
@@ -4,11 +4,12 @@ set -eux
 
 TMPDIR=
 TEST_RULE="/run/udev/rules.d/49-test.rules"
+TEST_CONF="/run/udev/udev.conf.d/test-17.conf"
 KILL_PID=
 
 setup() {
     mkdir -p "${TEST_RULE%/*}"
-    [[ -e /etc/udev/udev.conf ]] && cp -f /etc/udev/udev.conf /etc/udev/udev.conf.bak
+    mkdir -p /run/udev/udev.conf.d
 
     cat >"${TEST_RULE}" <<EOF
 ACTION!="add", GOTO="test_end"
@@ -21,7 +22,7 @@ PROGRAM!="/bin/sleep 60", ENV{PROGRAM_RESULT}="KILLED"
 
 LABEL="test_end"
 EOF
-    cat >/etc/udev/udev.conf <<EOF
+    cat >"$TEST_CONF" <<EOF
 event_timeout=10
 timeout_signal=SIGABRT
 EOF
@@ -38,8 +39,7 @@ teardown() {
     fi
 
     rm -rf "$TMPDIR"
-    rm -f "$TEST_RULE"
-    [[ -e /etc/udev/udev.conf.bak ]] && mv -f /etc/udev/udev.conf.bak /etc/udev/udev.conf
+    rm -f "$TEST_RULE" "$TEST_CONF"
     systemctl restart systemd-udevd.service
 }
 
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
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 8ea801ee96ee44069b0eb3e4219f3065b59fa947..e9af67f358a3a11fa0c2e84a548b5cd9128570ab 100755 (executable)
@@ -258,7 +258,7 @@ cleanup_session() (
 
     systemctl stop getty@tty2.service
 
-    for s in $(loginctl --no-legend list-sessions | awk '$3 == "logind-test-user" { print $1 }'); do
+    for s in $(loginctl --no-legend list-sessions | grep -v manager | awk '$3 == "logind-test-user" { print $1 }'); do
         echo "INFO: stopping session $s"
         loginctl terminate-session "$s"
     done
@@ -308,18 +308,18 @@ check_session() (
 
     local seat session leader_pid
 
-    if [[ $(loginctl --no-legend | grep -c "logind-test-user") != 1 ]]; then
+    if [[ $(loginctl --no-legend | grep -v manager | grep -c "logind-test-user") != 1 ]]; then
         echo "no session or multiple sessions for logind-test-user." >&2
         return 1
     fi
 
-    seat=$(loginctl --no-legend | grep 'logind-test-user *seat' | awk '{ print $4 }')
+    seat=$(loginctl --no-legend | grep -v manager | grep 'logind-test-user *seat' | awk '{ print $4 }')
     if [[ -z "$seat" ]]; then
         echo "no seat found for user logind-test-user" >&2
         return 1
     fi
 
-    session=$(loginctl --no-legend | awk '$3 == "logind-test-user" { print $1 }')
+    session=$(loginctl --no-legend | grep -v manager | awk '$3 == "logind-test-user" { print $1 }')
     if [[ -z "$session" ]]; then
         echo "no session found for user logind-test-user" >&2
         return 1
@@ -364,7 +364,7 @@ EOF
         check_session && break
     done
     check_session
-    assert_eq "$(loginctl --no-legend | awk '$3=="logind-test-user" { print $5 }')" "tty2"
+    assert_eq "$(loginctl --no-legend | grep -v manager | awk '$3=="logind-test-user" { print $7 }')" "tty2"
 }
 
 testcase_sanity_check() {
@@ -382,6 +382,8 @@ testcase_sanity_check() {
     # the seat/session autodetection work-ish
     systemd-run --user --pipe --wait -M "logind-test-user@.host" bash -eux <<\EOF
     loginctl list-sessions
+    loginctl list-sessions -j
+    loginctl list-sessions --json=short
     loginctl session-status
     loginctl show-session
     loginctl show-session -P DelayInhibited
@@ -455,7 +457,7 @@ EOF
     udevadm info "$dev"
 
     # trigger logind and activate session
-    loginctl activate "$(loginctl --no-legend | awk '$3 == "logind-test-user" { print $1 }')"
+    loginctl activate "$(loginctl --no-legend | grep -v manager | awk '$3 == "logind-test-user" { print $1 }')"
 
     # check ACL
     sleep 1
@@ -496,7 +498,7 @@ testcase_lock_idle_action() {
         return
     fi
 
-    if loginctl --no-legend | grep -q logind-test-user; then
+    if loginctl --no-legend | grep -v manager | grep -q logind-test-user; then
         echo >&2 "Session of the 'logind-test-user' is already present."
         exit 1
     fi
@@ -545,7 +547,7 @@ testcase_session_properties() {
     trap cleanup_session RETURN
     create_session
 
-    s=$(loginctl list-sessions --no-legend | awk '$3 == "logind-test-user" { print $1 }')
+    s=$(loginctl list-sessions --no-legend | grep -v manager | awk '$3 == "logind-test-user" { print $1 }')
     /usr/lib/systemd/tests/unit-tests/manual/test-session-properties "/org/freedesktop/login1/session/_3${s?}" /dev/tty2
 }
 
@@ -561,17 +563,17 @@ testcase_list_users_sessions_seats() {
     create_session
 
     # Activate the session
-    loginctl activate "$(loginctl --no-legend | awk '$3 == "logind-test-user" { print $1 }')"
+    loginctl activate "$(loginctl --no-legend | grep -v manager | awk '$3 == "logind-test-user" { print $1 }')"
 
-    session=$(loginctl list-sessions --no-legend | awk '$3 == "logind-test-user" { print $1 }')
+    session=$(loginctl list-sessions --no-legend | grep -v manager | awk '$3 == "logind-test-user" { print $1 }')
     : check that we got a valid session id
     busctl get-property org.freedesktop.login1 "/org/freedesktop/login1/session/_3${session?}" org.freedesktop.login1.Session Id
-    assert_eq "$(loginctl list-sessions --no-legend | awk '$3 == "logind-test-user" { print $2 }')" "$(id -ru logind-test-user)"
-    seat=$(loginctl list-sessions --no-legend | awk '$3 == "logind-test-user" { print $4 }')
-    assert_eq "$(loginctl list-sessions --no-legend | awk '$3 == "logind-test-user" { print $5 }')" tty2
-    assert_eq "$(loginctl list-sessions --no-legend | awk '$3 == "logind-test-user" { print $6 }')" active
-    assert_eq "$(loginctl list-sessions --no-legend | awk '$3 == "logind-test-user" { print $7 }')" no
-    assert_eq "$(loginctl list-sessions --no-legend | awk '$3 == "logind-test-user" { print $8 }')" '-'
+    assert_eq "$(loginctl list-sessions --no-legend | grep -v manager | awk '$3 == "logind-test-user" { print $2 }')" "$(id -ru logind-test-user)"
+    seat=$(loginctl list-sessions --no-legend | grep -v manager | awk '$3 == "logind-test-user" { print $4 }')
+    assert_eq "$(loginctl list-sessions --no-legend | grep -v manager | awk '$3 == "logind-test-user" { print $6 }')" user
+    assert_eq "$(loginctl list-sessions --no-legend | grep -v manager | awk '$3 == "logind-test-user" { print $7 }')" tty2
+    assert_eq "$(loginctl list-sessions --no-legend | grep -v manager | awk '$3 == "logind-test-user" { print $8 }')" no
+    assert_eq "$(loginctl list-sessions --no-legend | grep -v manager | awk '$3 == "logind-test-user" { print $9 }')" '-'
 
     loginctl list-seats --no-legend | grep -Fwq "${seat?}"
 
@@ -582,10 +584,10 @@ testcase_list_users_sessions_seats() {
     loginctl enable-linger logind-test-user
     assert_eq "$(loginctl list-users --no-legend | awk '$2 == "logind-test-user" { print $3 }')" yes
 
-    for s in $(loginctl list-sessions --no-legend | awk '$3 == "logind-test-user" { print $1 }'); do
+    for s in $(loginctl list-sessions --no-legend | grep tty | awk '$3 == "logind-test-user" { print $1 }'); do
         loginctl terminate-session "$s"
     done
-    if ! timeout 30 bash -c "while loginctl --no-legend | grep -q logind-test-user; do sleep 1; done"; then
+    if ! timeout 30 bash -c "while loginctl --no-legend | grep tty | grep -q logind-test-user; do sleep 1; done"; then
         echo "WARNING: session for logind-test-user still active, ignoring."
         return
     fi
@@ -613,7 +615,7 @@ testcase_stop_idle_session() {
     create_session
     trap teardown_stop_idle_session RETURN
 
-    id="$(loginctl --no-legend | awk '$3 == "logind-test-user" { print $1; }')"
+    id="$(loginctl --no-legend | grep tty | awk '$3 == "logind-test-user" { print $1; }')"
     ts="$(date '+%H:%M:%S')"
 
     mkdir -p /run/systemd/logind.conf.d
@@ -625,7 +627,7 @@ EOF
     sleep 5
 
     assert_eq "$(journalctl -b -u systemd-logind.service --since="$ts" --grep "Session \"$id\" of user \"logind-test-user\" is idle, stopping." | wc -l)" 1
-    assert_eq "$(loginctl --no-legend | grep -c "logind-test-user")" 0
+    assert_eq "$(loginctl --no-legend | grep -v manager | grep -c "logind-test-user")" 0
 }
 
 testcase_ambient_caps() {
@@ -680,6 +682,51 @@ EOF
     rm -f "$SCRIPT" "$PAMSERVICE"
 }
 
+background_at_return() {
+    rm -f /etc/pam.d/"$PAMSERVICE"
+    unset PAMSERVICE
+}
+
+testcase_background() {
+
+    local uid TRANSIENTUNIT1 TRANSIENTUNIT2
+
+    uid=$(id -u logind-test-user)
+
+    systemctl stop user@"$uid".service
+
+    PAMSERVICE="pamserv$RANDOM"
+    TRANSIENTUNIT1="bg$RANDOM.service"
+    TRANSIENTUNIT2="bgg$RANDOM.service"
+
+    trap background_at_return RETURN
+
+    cat > /etc/pam.d/"$PAMSERVICE" <<EOF
+auth sufficient    pam_unix.so
+auth required      pam_deny.so
+account sufficient pam_unix.so
+account required   pam_permit.so
+session optional   pam_systemd.so debug
+session required   pam_unix.so
+EOF
+
+    systemd-run -u "$TRANSIENTUNIT1" -p PAMName="$PAMSERVICE" -p "Environment=XDG_SESSION_CLASS=background-light" -p Type=exec -p User=logind-test-user sleep infinity
+
+    # This was a 'light' background service, hence the service manager should not be running
+    (! systemctl is-active user@"$uid".service )
+
+    systemctl stop "$TRANSIENTUNIT1"
+
+    systemd-run -u "$TRANSIENTUNIT2" -p PAMName="$PAMSERVICE" -p "Environment=XDG_SESSION_CLASS=background" -p Type=exec -p User=logind-test-user sleep infinity
+
+    # This was a regular background service, hence the service manager should be running
+    systemctl is-active user@"$uid".service
+
+    systemctl stop "$TRANSIENTUNIT2"
+
+    systemctl stop user@"$uid".service
+}
+
 setup_test_user
 test_write_dropin
 run_testcases
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 07e6fc9b6236a504d6fc5088513c634bc200b90c..4f31a33c343a52ba9c7e24da45b592798b9d0eac 100755 (executable)
@@ -6,6 +6,11 @@ set -o pipefail
 # shellcheck source=test/units/util.sh
 . "$(dirname "$0")"/util.sh
 
+if [[ "$(sysctl -ne kernel.apparmor_restrict_unprivileged_userns)" -eq 1 ]]; then
+    echo "Cannot create unprivileged user namespaces" >/skipped
+    exit 0
+fi
+
 systemd-analyze log-level debug
 
 runas testuser systemd-run --wait --user --unit=test-private-users \
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 b5d01bfc8e8b336e12ce84432144b7f45e4fae80..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
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
index c1b80d660e85e33df7b5d9ea2077b230f7162bf2..f91cd5f78d63eee7de3b3cf9043a844426305aed 100755 (executable)
@@ -22,6 +22,13 @@ systemd-id128 show root-x86-64 --app-specific=4f68bce3e8cd4db196e7fbcaf984b709
 systemd-id128 show --pretty root-x86-64 --app-specific=4f68bce3e8cd4db196e7fbcaf984b709
 [[ "$(systemd-id128 show root-x86-64 --app-specific=4f68bce3e8cd4db196e7fbcaf984b709 -P)" = "8ee5535e7cb14c249e1d28b8dfbb939c" ]]
 
+systemd-id128 show -j
+systemd-id128 show --no-pager
+systemd-id128 show --json=short
+systemd-id128 show --no-legend
+systemd-id128 show --no-pager --no-legend
+systemd-id128 show root -P -u
+
 [[ "$(systemd-id128 new | wc -c)" -eq 33 ]]
 systemd-id128 new -p
 systemd-id128 new -u
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
diff --git a/test/units/testsuite-74.ssh.sh b/test/units/testsuite-74.ssh.sh
new file mode 100755 (executable)
index 0000000..3372ec0
--- /dev/null
@@ -0,0 +1,60 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+if ! command -v ssh &> /dev/null || ! command -v sshd &> /dev/null ; then
+    echo "ssh/sshd not found, skipping test." >&2
+    exit 0
+fi
+
+systemctl -q is-active sshd-unix-local.socket
+
+if test -e /dev/vsock ; then
+    systemctl -q is-active sshd-vsock.socket
+fi
+
+if test -d /run/host/unix-export ; then
+    systemctl -q is-active sshd-unix-export.socket
+fi
+
+# FIXME: sshd seems to crash inside asan currently, skip the actual ssh test hence
+if [[ -v ASAN_OPTIONS ]] ; then
+    exit 0
+fi
+
+ROOTID=$(mktemp -u)
+# Needed on Ubuntu/Debian as we copy binaries manually
+mkdir -p /run/sshd
+
+removesshid() {
+    rm -f "$ROOTID" "$ROOTID".pub
+}
+
+ssh-keygen -N '' -C '' -t rsa -f "$ROOTID"
+
+mkdir -p 0700 /root/.ssh
+cat "$ROOTID".pub >> /root/.ssh/authorized_keys
+
+# set root pw to "foo", just to set it to something valid
+# shellcheck disable=SC2016
+usermod -p '$5$AAy6BYJ6rzz.QELv$6LpVEU3/RQmVz.svHu/33qoJWWWzZuJ3DM2fo9JgcUD' root
+usermod -U root
+
+mkdir -p /etc/ssh
+test -f /etc/ssh/ssh_host_rsa_key || ssh-keygen -t rsa -C '' -N '' -f /etc/ssh/ssh_host_rsa_key
+echo "PermitRootLogin yes" >> /etc/ssh/sshd_config
+echo "LogLevel DEBUG3" >> /etc/ssh/sshd_config
+
+test -f /etc/ssh/ssh_config || echo 'Include /etc/ssh/ssh_config.d/*.conf' > /etc/ssh/ssh_config
+
+# ssh wants this dir around, but distros cannot agree on a common name for it, let's just create all that are aware of distros use
+mkdir -p /usr/share/empty.sshd /var/empty /var/empty/sshd
+
+ssh -o StrictHostKeyChecking=no -v -i "$ROOTID" .host cat /etc/machine-id | cmp - /etc/machine-id
+ssh -o StrictHostKeyChecking=no -v -i "$ROOTID" unix/run/ssh-unix-local/socket cat /etc/machine-id | cmp - /etc/machine-id
+
+modprobe vsock_loopback ||:
+if test -e /dev/vsock -a -d /sys/module/vsock_loopback ; then
+    ssh -o StrictHostKeyChecking=no -v -i "$ROOTID" vsock/1 cat /etc/machine-id | cmp - /etc/machine-id
+fi
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 '{}'
index 501bbb542d104fa1d34cdbed998a26f91b2826c7..92f6b8e030dd7cc0a891b4a38717948b27e5cc2f 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
@@ -422,6 +447,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
@@ -549,6 +586,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
diff --git a/tmpfiles.d/20-systemd-ssh-generator.conf.in b/tmpfiles.d/20-systemd-ssh-generator.conf.in
new file mode 100644 (file)
index 0000000..033379e
--- /dev/null
@@ -0,0 +1,10 @@
+#  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.
+
+# See tmpfiles.d(5) for details
+
+L {{SSHCONFDIR}}/20-systemd-ssh-proxy.conf - - - - {{LIBEXECDIR}}/ssh_config.d/20-systemd-ssh-proxy.conf
index 390076b6d5004712c2b307275ed65a1d79df0a2c..d05ea94c160583f0b7ed76b1a0302a375f6d1560 100644 (file)
@@ -35,6 +35,7 @@ in_files = [['etc.conf',                      ''],
             ['systemd.conf',                  ''],
             ['var.conf',                      ''],
             ['20-systemd-userdb.conf',        'ENABLE_USERDB'],
+            ['20-systemd-ssh-generator.conf', ''],
            ]
 
 foreach pair : in_files
index 3dadab112360814908e27d2613e66c905b73021f..1eef2bd9be8bc5924da4237475f3563943da6b2c 100644 (file)
@@ -23,9 +23,7 @@ OnFailureJobMode=replace-irreversibly
 [Service]
 Type=oneshot
 
-# FIXME: once dracut is patched to install the symlink, change to:
-# ExecStart={{LIBEXECDIR}}/systemd-sysroot-fstab-check
-ExecStart=@{{SYSTEM_GENERATOR_DIR}}/systemd-fstab-generator systemd-sysroot-fstab-check
+ExecStart={{LIBEXECDIR}}/systemd-sysroot-fstab-check
 
 # We want to enqueue initrd-cleanup.service/start after we finished the part
 # above. It can't be part of the initial transaction, because non-oneshot units
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 25cd7a0ff952fe9fdc5561d5d109a200197b3508..c43195bc076d3f2686c2d8675dd9c7888e7552d8 100644 (file)
@@ -8,7 +8,7 @@
 #  (at your option) any later version.
 
 [Unit]
-Description=Hibernate
+Description=System Hibernate
 Documentation=man:systemd-hibernate.service(8)
 DefaultDependencies=no
 Requires=sleep.target
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 fe57f57fae6d006f34db1f67d524b47f5e9cc5e1..c85215bdacfd192a59c7f62f2905a3d630b79758 100644 (file)
@@ -8,7 +8,7 @@
 #  (at your option) any later version.
 
 [Unit]
-Description=Hybrid Suspend+Hibernate
+Description=System Hybrid Suspend+Hibernate
 Documentation=man:systemd-hybrid-sleep.service(8)
 DefaultDependencies=no
 Requires=sleep.target
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 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 150d8d2c239b119f13a53e8977d6ebb4b3e209d1..d7ab2c195e597b68a5d53f5e7e488a6737f3f762 100644 (file)
@@ -8,7 +8,7 @@
 #  (at your option) any later version.
 
 [Unit]
-Description=Suspend; Hibernate if not used for a period of time
+Description=System Suspend then Hibernate
 Documentation=man:systemd-suspend-then-hibernate.service(8)
 DefaultDependencies=no
 Requires=sleep.target
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