]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
Merge pull request #31648 from neighbourhoodie/review-content
authorZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>
Wed, 17 Apr 2024 15:34:43 +0000 (17:34 +0200)
committerZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>
Wed, 17 Apr 2024 15:41:07 +0000 (17:41 +0200)
I did the merge manually to resolve a trivial conflict.

719 files changed:
.github/ISSUE_TEMPLATE/bug_report.yml
.github/ISSUE_TEMPLATE/feature_request.yml
.github/advanced-issue-labeler.yml
.github/labeler.yml
.github/workflows/differential-shellcheck.yml
.github/workflows/issue_labeler.yml
.github/workflows/mkosi.yml
LICENSES/README.md
NEWS
README.md
TODO
docs/CODING_STYLE.md
docs/ENVIRONMENT.md
docs/HACKING.md
docs/PORTABILITY_AND_STABILITY.md
hwdb.d/60-evdev.hwdb
hwdb.d/60-sensor.hwdb
man/busctl.xml
man/coredumpctl.xml
man/custom-entities.ent.in
man/custom-html.xsl
man/event-quick-child.c
man/homectl.xml
man/hwdb-usb-device.c
man/journal-enumerate-fields.c
man/journal-iterate-foreach.c
man/journal-iterate-poll.c
man/journal-iterate-unique.c
man/journal-iterate-wait.c
man/journal-stream-fd.c
man/journald.conf.xml
man/kernel-command-line.xml
man/logcontrol-example.c
man/meson.build
man/networkctl.xml
man/networkd.conf.xml
man/notify-selfcontained-example.c [new file with mode: 0644]
man/org.freedesktop.portable1.xml
man/org.freedesktop.systemd1.xml
man/path-documents.c
man/portablectl.xml
man/print-unit-path-call-method.c
man/print-unit-path.c
man/repart.d.xml
man/resolvectl.xml
man/rules/meson.build
man/sd-bus-container-append.c
man/sd-journal.xml
man/sd_bus_error-example.c
man/sd_bus_service_reconnect.c
man/sd_notify.xml
man/standard-options.xml
man/systemctl.xml
man/systemd-analyze.xml
man/systemd-bsod.service.xml
man/systemd-creds.xml
man/systemd-cryptenroll.xml
man/systemd-debug-generator.xml
man/systemd-hibernate-resume.service.xml
man/systemd-measure.xml
man/systemd-mountfsd.service.xml [new file with mode: 0644]
man/systemd-network-generator.service.xml
man/systemd-networkd-wait-online.service.xml
man/systemd-notify.xml
man/systemd-nspawn.xml
man/systemd-nsresourced.service.xml [new file with mode: 0644]
man/systemd-pcrphase.service.xml
man/systemd-repart.xml
man/systemd-sleep.conf.xml
man/systemd-socket-activate.xml
man/systemd-sysext.xml
man/systemd-system.conf.xml
man/systemd-vmspawn.xml
man/systemd.exec.xml
man/systemd.netdev.xml
man/systemd.network.xml
man/systemd.service.xml
man/systemd.socket.xml
man/systemd.system-credentials.xml
man/systemd.xml
man/tmpfiles.d.xml
man/udevadm.xml
man/userdbctl.xml
man/varlinkctl.xml
man/vtable-example.c
meson.build
meson_options.txt
mkosi.conf
mkosi.images/base/mkosi.conf [deleted file]
mkosi.images/base/mkosi.conf.d/10-arch/initrd/mkosi.postinst [deleted file]
mkosi.images/base/mkosi.conf.d/10-arch/mkosi.prepare [deleted file]
mkosi.images/base/mkosi.conf.d/10-centos-fedora/initrd/mkosi.postinst [deleted file]
mkosi.images/base/mkosi.conf.d/10-centos/mkosi.conf [deleted file]
mkosi.images/base/mkosi.conf.d/10-debian-ubuntu/mkosi.conf [deleted file]
mkosi.images/base/mkosi.conf.d/10-debian-ubuntu/mkosi.prepare [deleted file]
mkosi.images/base/mkosi.conf.d/10-ubuntu/mkosi.conf [deleted file]
mkosi.images/base/mkosi.extra/usr/share/factory/mkosi/gdbinit.d/systemd.gdb [deleted file]
mkosi.images/base/mkosi.postinst [deleted file]
mkosi.images/base/mkosi.sync [deleted file]
mkosi.images/system/mkosi.clean [new file with mode: 0755]
mkosi.images/system/mkosi.conf
mkosi.images/system/mkosi.conf.d/10-arch/mkosi.build.chroot [moved from mkosi.images/base/mkosi.conf.d/10-arch/mkosi.build.chroot with 69% similarity]
mkosi.images/system/mkosi.conf.d/10-arch/mkosi.conf [moved from mkosi.images/base/mkosi.conf.d/10-arch/mkosi.conf with 57% similarity]
mkosi.images/system/mkosi.conf.d/10-arch/mkosi.prepare [new file with mode: 0755]
mkosi.images/system/mkosi.conf.d/10-centos-fedora/mkosi.build.chroot [moved from mkosi.images/base/mkosi.conf.d/10-centos-fedora/mkosi.build.chroot with 63% similarity]
mkosi.images/system/mkosi.conf.d/10-centos-fedora/mkosi.conf [moved from mkosi.images/base/mkosi.conf.d/10-centos-fedora/mkosi.conf with 51% similarity]
mkosi.images/system/mkosi.conf.d/10-centos-fedora/mkosi.prepare [moved from mkosi.images/base/mkosi.conf.d/10-centos-fedora/mkosi.prepare with 57% similarity]
mkosi.images/system/mkosi.conf.d/10-centos/mkosi.conf
mkosi.images/system/mkosi.conf.d/10-debian-ubuntu/mkosi.build.chroot [moved from mkosi.images/base/mkosi.conf.d/10-debian-ubuntu/mkosi.build.chroot with 81% similarity]
mkosi.images/system/mkosi.conf.d/10-debian-ubuntu/mkosi.conf [new file with mode: 0644]
mkosi.images/system/mkosi.conf.d/10-debian-ubuntu/mkosi.prepare [new file with mode: 0755]
mkosi.images/system/mkosi.conf.d/10-debian/mkosi.conf [moved from mkosi.images/base/mkosi.conf.d/10-debian/mkosi.conf with 100% similarity]
mkosi.images/system/mkosi.conf.d/10-debian/mkosi.conf.d/amd64.conf [moved from mkosi.images/base/mkosi.conf.d/10-debian/mkosi.conf.d/amd64.conf with 100% similarity]
mkosi.images/system/mkosi.conf.d/10-debian/mkosi.conf.d/arm64.conf [moved from mkosi.images/base/mkosi.conf.d/10-debian/mkosi.conf.d/arm64.conf with 100% similarity]
mkosi.images/system/mkosi.conf.d/10-fedora/mkosi.conf [moved from mkosi.images/base/mkosi.conf.d/10-fedora/mkosi.conf with 84% similarity]
mkosi.images/system/mkosi.conf.d/10-opensuse/initrd/mkosi.postinst [moved from mkosi.images/base/mkosi.conf.d/10-opensuse/initrd/mkosi.postinst with 82% similarity]
mkosi.images/system/mkosi.conf.d/10-opensuse/mkosi.build.chroot [moved from mkosi.images/base/mkosi.conf.d/10-opensuse/mkosi.build.chroot with 73% similarity]
mkosi.images/system/mkosi.conf.d/10-opensuse/mkosi.conf [moved from mkosi.images/base/mkosi.conf.d/10-opensuse/mkosi.conf with 62% similarity]
mkosi.images/system/mkosi.conf.d/10-opensuse/mkosi.prepare [moved from mkosi.images/base/mkosi.conf.d/10-opensuse/mkosi.prepare with 50% similarity]
mkosi.images/system/mkosi.conf.d/10-ubuntu/mkosi.conf [new file with mode: 0644]
mkosi.images/system/mkosi.extra/etc/issue [moved from mkosi.images/base/mkosi.extra/etc/issue with 100% similarity]
mkosi.images/system/mkosi.extra/usr/lib/systemd/journald.conf.d/50-persistent.conf [moved from mkosi.images/base/mkosi.extra/usr/lib/systemd/journald.conf.d/50-persistent.conf with 100% similarity]
mkosi.images/system/mkosi.extra/usr/lib/systemd/mkosi-check-and-shutdown.sh [moved from mkosi.images/base/mkosi.extra/usr/lib/systemd/mkosi-check-and-shutdown.sh with 100% similarity]
mkosi.images/system/mkosi.extra/usr/lib/systemd/system-preset/00-mkosi.preset [moved from mkosi.images/base/mkosi.extra/usr/lib/systemd/system-preset/00-mkosi.preset with 100% similarity]
mkosi.images/system/mkosi.extra/usr/lib/systemd/system-preset/99-mkosi.preset [moved from mkosi.images/base/mkosi.extra/usr/lib/systemd/system-preset/99-mkosi.preset with 100% similarity]
mkosi.images/system/mkosi.extra/usr/lib/systemd/system/mkosi-check-and-shutdown.service [moved from mkosi.images/base/mkosi.extra/usr/lib/systemd/system/mkosi-check-and-shutdown.service with 100% similarity]
mkosi.images/system/mkosi.extra/usr/lib/tmpfiles.d/99-mkosi.conf [moved from mkosi.images/base/mkosi.extra/usr/lib/tmpfiles.d/99-mkosi.conf with 100% similarity]
mkosi.images/system/mkosi.extra/usr/lib/tmpfiles.d/locale.conf [moved from mkosi.images/base/mkosi.extra/usr/lib/tmpfiles.d/locale.conf with 100% similarity]
mkosi.images/system/mkosi.finalize [moved from mkosi.images/base/mkosi.finalize with 78% similarity]
mkosi.images/system/mkosi.postinst.chroot [moved from mkosi.images/base/mkosi.postinst.chroot with 100% similarity]
mkosi.images/system/mkosi.sync [new file with mode: 0755]
network/80-namespace-ns.network [new file with mode: 0644]
network/meson.build
pkg/arch
pkg/centos
pkg/debian
pkg/fedora
pkg/ubuntu [new symlink]
po/ja.po
presets/90-systemd.preset
rules.d/99-systemd.rules.in
shell-completion/zsh/_bootctl
shell-completion/zsh/_busctl
shell-completion/zsh/_coredumpctl
shell-completion/zsh/_hostnamectl
shell-completion/zsh/_journalctl
shell-completion/zsh/_kernel-install
shell-completion/zsh/_localectl
shell-completion/zsh/_loginctl
shell-completion/zsh/_machinectl
shell-completion/zsh/_networkctl
shell-completion/zsh/_oomctl
shell-completion/zsh/_resolvectl
shell-completion/zsh/_systemctl.in
shell-completion/zsh/_systemd
shell-completion/zsh/_systemd-analyze
shell-completion/zsh/_systemd-delta
shell-completion/zsh/_systemd-inhibit
shell-completion/zsh/_systemd-nspawn
shell-completion/zsh/_systemd-path
shell-completion/zsh/_systemd-run
shell-completion/zsh/_systemd-tmpfiles
shell-completion/zsh/_timedatectl
shell-completion/zsh/_udevadm
src/analyze/analyze-plot.c
src/analyze/analyze-time-data.c
src/analyze/analyze-time-data.h
src/ask-password/ask-password.c
src/backlight/backlight.c
src/basic/cgroup-util.c
src/basic/cgroup-util.h
src/basic/dlfcn-util.c
src/basic/efivars.c
src/basic/gcrypt-util.c
src/basic/gcrypt-util.h
src/basic/linux/btrfs.h
src/basic/linux/if_link.h
src/basic/linux/in6.h
src/basic/linux/magic.h [new file with mode: 0644]
src/basic/linux/netfilter/nf_tables.h
src/basic/linux/nexthop.h
src/basic/linux/nl80211.h
src/basic/lock-util.h
src/basic/log.h
src/basic/meson.build
src/basic/missing_fs.h
src/basic/missing_magic.h
src/basic/namespace-util.c
src/basic/namespace-util.h
src/basic/os-util.c
src/basic/os-util.h
src/basic/parse-util.c
src/basic/pidref.c
src/basic/process-util.c
src/basic/signal-util.c
src/basic/socket-util.c
src/basic/string-util.c
src/basic/string-util.h
src/basic/time-util.h
src/basic/uid-range.c
src/basic/uid-range.h
src/basic/unit-def.c
src/basic/utf8.c
src/boot/bless-boot-generator.c
src/boot/efi/secure-boot.c
src/core/automount.c
src/core/bpf-firewall.c
src/core/cgroup.c
src/core/dbus-execute.c
src/core/dbus-manager.c
src/core/dbus-mount.c
src/core/device.c
src/core/dynamic-user.c
src/core/emergency-action.c
src/core/exec-invoke.c
src/core/execute-serialize.c
src/core/execute.c
src/core/execute.h
src/core/kmod-setup.c
src/core/load-fragment.c
src/core/main.c
src/core/manager-serialize.c
src/core/manager.c
src/core/manager.h
src/core/meson.build
src/core/mount.c
src/core/namespace.c
src/core/scope.c
src/core/service.c
src/core/show-status.c
src/core/slice.c
src/core/socket.c
src/core/swap.c
src/core/target.c
src/core/timer.c
src/core/transaction.c
src/core/unit.c
src/core/unit.h
src/coredump/coredumpctl.c
src/creds/creds.c
src/cryptsetup/cryptsetup-tokens/luks2-tpm2.h
src/cryptsetup/cryptsetup.c
src/debug-generator/debug-generator.c
src/dissect/dissect.c
src/hibernate-resume/hibernate-resume-config.c
src/hibernate-resume/hibernate-resume-config.h
src/hibernate-resume/hibernate-resume.c
src/home/homectl.c
src/home/homed-home.c
src/home/homed-home.h
src/home/homed-manager.c
src/home/homework-luks.c
src/home/homework-quota.c
src/home/meson.build
src/home/test-homed-regression-31896.c [new file with mode: 0644]
src/import/pull-job.c
src/journal-remote/journal-gatewayd.c
src/journal-remote/journal-remote-main.c
src/journal-remote/journal-remote.c
src/journal-remote/journal-remote.h
src/journal-remote/journal-upload-journal.c
src/journal-remote/journal-upload.c
src/journal-remote/journal-upload.h
src/journal/bsod.c
src/journal/journalctl-authenticate.c
src/journal/journalctl-filter.c
src/journal/journalctl-misc.c
src/journal/journalctl-show.c
src/journal/journalctl-util.c
src/journal/journalctl.c
src/journal/meson.build
src/kernel-install/90-loaderentry.install.in
src/libsystemd-network/dhcp-network.c
src/libsystemd-network/dhcp6-internal.h
src/libsystemd-network/dhcp6-network.c
src/libsystemd-network/dhcp6-protocol.h
src/libsystemd-network/fuzz-dhcp6-client.c
src/libsystemd-network/fuzz-ndisc-rs.c
src/libsystemd-network/icmp6-packet.c
src/libsystemd-network/icmp6-packet.h
src/libsystemd-network/icmp6-util-unix.c
src/libsystemd-network/icmp6-util.c
src/libsystemd-network/icmp6-util.h
src/libsystemd-network/meson.build
src/libsystemd-network/ndisc-neighbor-internal.h [new file with mode: 0644]
src/libsystemd-network/ndisc-option.c
src/libsystemd-network/ndisc-option.h
src/libsystemd-network/ndisc-redirect-internal.h [new file with mode: 0644]
src/libsystemd-network/ndisc-router-solicit-internal.h [new file with mode: 0644]
src/libsystemd-network/sd-dhcp-server.c
src/libsystemd-network/sd-dhcp6-client.c
src/libsystemd-network/sd-ipv4acd.c
src/libsystemd-network/sd-ipv4ll.c
src/libsystemd-network/sd-ndisc-neighbor.c [new file with mode: 0644]
src/libsystemd-network/sd-ndisc-redirect.c [new file with mode: 0644]
src/libsystemd-network/sd-ndisc-router-solicit.c [new file with mode: 0644]
src/libsystemd-network/sd-ndisc-router.c
src/libsystemd-network/sd-ndisc.c
src/libsystemd-network/sd-radv.c
src/libsystemd-network/test-acd.c
src/libsystemd-network/test-dhcp6-client.c
src/libsystemd-network/test-ipv4ll-manual.c
src/libsystemd-network/test-ndisc-rs.c
src/libsystemd-network/test-ndisc-send.c [new file with mode: 0644]
src/libsystemd/sd-bus/bus-error.c
src/libsystemd/sd-device/device-monitor.c
src/libsystemd/sd-id128/id128-util.c
src/libsystemd/sd-id128/id128-util.h
src/libsystemd/sd-id128/sd-id128.c
src/libsystemd/sd-journal/fsprg.c
src/libsystemd/sd-journal/fsprg.h
src/libsystemd/sd-journal/journal-authenticate.c
src/libsystemd/sd-journal/journal-file.c
src/libsystemd/sd-journal/journal-verify.c
src/libsystemd/sd-journal/sd-journal.c
src/libsystemd/sd-journal/test-journal-enum.c
src/libsystemd/sd-journal/test-journal-match.c
src/libsystemd/sd-journal/test-journal-stream.c
src/libsystemd/sd-netlink/test-netlink.c
src/libudev/libudev-device.c
src/login/logind-dbus.c
src/login/logind.c
src/modules-load/meson.build
src/modules-load/modules-load.c
src/mountfsd/io.systemd.mount-file-system.policy [new file with mode: 0644]
src/mountfsd/meson.build [new file with mode: 0644]
src/mountfsd/mountfsd-manager.c [new file with mode: 0644]
src/mountfsd/mountfsd-manager.h [new file with mode: 0644]
src/mountfsd/mountfsd.c [new file with mode: 0644]
src/mountfsd/mountwork.c [new file with mode: 0644]
src/network/generator/main.c
src/network/generator/network-generator.c
src/network/meson.build
src/network/netdev/bridge.c
src/network/netdev/fou-tunnel.c
src/network/netdev/geneve.c
src/network/netdev/ipvlan.c
src/network/netdev/macvlan.c
src/network/netdev/netdev.c
src/network/netdev/tuntap.c
src/network/netdev/veth.c
src/network/netdev/vlan.c
src/network/netdev/vrf.c
src/network/netdev/vxlan.c
src/network/netdev/wireguard.c
src/network/networkctl-config-file.c
src/network/networkctl.c
src/network/networkd-address-generation.c
src/network/networkd-address-generation.h
src/network/networkd-address-label.c
src/network/networkd-address.c
src/network/networkd-address.h
src/network/networkd-bridge-mdb.c
src/network/networkd-can.c
src/network/networkd-conf.c
src/network/networkd-dhcp-common.c
src/network/networkd-dhcp-common.h
src/network/networkd-dhcp-prefix-delegation.c
src/network/networkd-dhcp-prefix-delegation.h
src/network/networkd-dhcp-server.c
src/network/networkd-dhcp6.c
src/network/networkd-gperf.gperf
src/network/networkd-link.c
src/network/networkd-link.h
src/network/networkd-manager-varlink.c
src/network/networkd-manager.c
src/network/networkd-manager.h
src/network/networkd-ndisc.c
src/network/networkd-ndisc.h
src/network/networkd-network-gperf.gperf
src/network/networkd-network.c
src/network/networkd-network.h
src/network/networkd-nexthop.c
src/network/networkd-radv.c
src/network/networkd-radv.h
src/network/networkd-routing-policy-rule.c
src/network/networkd-setlink.c
src/network/networkd-state-file.c
src/network/networkd-sysctl.c
src/network/networkd-sysctl.h
src/network/networkd.conf
src/network/tc/qdisc.c
src/network/tc/tclass.c
src/network/test-network-tables.c
src/nspawn/nspawn-cgroup.c
src/nspawn/nspawn-cgroup.h
src/nspawn/nspawn-mount.c
src/nspawn/nspawn-mount.h
src/nspawn/nspawn-network.c
src/nspawn/nspawn.c
src/nsresourced/bpf/userns_restrict/meson.build [new file with mode: 0644]
src/nsresourced/bpf/userns_restrict/userns-restrict-skel.h [new file with mode: 0644]
src/nsresourced/bpf/userns_restrict/userns-restrict.bpf.c [new file with mode: 0644]
src/nsresourced/meson.build [new file with mode: 0644]
src/nsresourced/nsresourced-manager.c [new file with mode: 0644]
src/nsresourced/nsresourced-manager.h [new file with mode: 0644]
src/nsresourced/nsresourced.c [new file with mode: 0644]
src/nsresourced/nsresourcework.c [new file with mode: 0644]
src/nsresourced/test-userns-restrict.c [new file with mode: 0644]
src/nsresourced/userns-registry.c [new file with mode: 0644]
src/nsresourced/userns-registry.h [new file with mode: 0644]
src/nsresourced/userns-restrict.c [new file with mode: 0644]
src/nsresourced/userns-restrict.h [new file with mode: 0644]
src/partition/meson.build
src/partition/repart.c
src/portable/portable.c
src/resolve/fuzz-resource-record.c
src/resolve/meson.build
src/resolve/resolved-dns-dnssec.c
src/resolve/resolved-dns-rr.c
src/resolve/resolved-dns-transaction.c
src/resolve/resolved-dnssd.c
src/resolve/resolved-varlink.c
src/run/run.c
src/shared/base-filesystem.c
src/shared/bpf-compat.h
src/shared/bpf-dlopen.c
src/shared/bpf-dlopen.h
src/shared/btrfs-util.c
src/shared/btrfs-util.h
src/shared/bus-polkit.c
src/shared/cgroup-setup.c
src/shared/cgroup-setup.h
src/shared/cgroup-show.c
src/shared/creds-util.c
src/shared/creds-util.h
src/shared/discover-image.c
src/shared/discover-image.h
src/shared/dissect-image.c
src/shared/dissect-image.h
src/shared/firewall-util-iptables.c
src/shared/hibernate-util.c
src/shared/hibernate-util.h
src/shared/idn-util.c
src/shared/image-policy.c
src/shared/image-policy.h
src/shared/initreq.h
src/shared/killall.c
src/shared/libfido2-util.c
src/shared/libfido2-util.h
src/shared/logs-show.c
src/shared/meson.build
src/shared/module-util.c
src/shared/module-util.h
src/shared/nsresource.c [new file with mode: 0644]
src/shared/nsresource.h [new file with mode: 0644]
src/shared/open-file.c
src/shared/open-file.h
src/shared/pkcs11-util.c
src/shared/ptyfwd.c
src/shared/resize-fs.c
src/shared/serialize.c
src/shared/sleep-config.c
src/shared/sleep-config.h
src/shared/socket-netlink.c
src/shared/tests.h
src/shared/tpm2-util.c
src/shared/udev-util.c
src/shared/udev-util.h
src/shared/userdb.c
src/shared/varlink-io.systemd.MountFileSystem.c [new file with mode: 0644]
src/shared/varlink-io.systemd.MountFileSystem.h [new file with mode: 0644]
src/shared/varlink-io.systemd.NamespaceResource.c [new file with mode: 0644]
src/shared/varlink-io.systemd.NamespaceResource.h [new file with mode: 0644]
src/shared/varlink.c
src/shared/varlink.h
src/shared/vpick.c
src/shared/vpick.h
src/shared/watchdog.c
src/sleep/sleep.c
src/sleep/sleep.conf
src/ssh-generator/ssh-generator.c
src/storagetm/storagetm.c
src/sysext/sysext.c
src/systemctl/meson.build
src/systemctl/systemctl-logind.c
src/systemd/meson.build
src/systemd/sd-bus-vtable.h
src/systemd/sd-ndisc-neighbor.h [new file with mode: 0644]
src/systemd/sd-ndisc-redirect.h [new file with mode: 0644]
src/systemd/sd-ndisc-router-solicit.h [new file with mode: 0644]
src/systemd/sd-ndisc-router.h
src/systemd/sd-ndisc.h
src/systemd/sd-radv.h
src/sysupdate/sysupdate-transfer.c
src/sysupdate/sysupdate.c
src/sysusers/meson.build
src/test/meson.build
src/test/test-acl-util.c
src/test/test-af-list.c
src/test/test-architecture.c
src/test/test-arphrd-util.c
src/test/test-ask-password-api.c
src/test/test-async.c
src/test/test-barrier.c
src/test/test-bitmap.c
src/test/test-boot-timestamps.c
src/test/test-bootspec.c
src/test/test-bpf-devices.c
src/test/test-bpf-firewall.c
src/test/test-bpf-foreign-programs.c
src/test/test-bpf-restrict-fs.c
src/test/test-calendarspec.c
src/test/test-cap-list.c
src/test/test-capability.c
src/test/test-cgroup-mask.c
src/test/test-cgroup-setup.c
src/test/test-cgroup-unit-default.c
src/test/test-cgroup-util.c
src/test/test-cgroup.c
src/test/test-chase.c
src/test/test-conf-parser.c
src/test/test-copy.c
src/test/test-core-unit.c
src/test/test-cpu-set-util.c
src/test/test-creds.c
src/test/test-cryptolib.c
src/test/test-data-fd-util.c
src/test/test-date.c
src/test/test-devnum-util.c
src/test/test-dirent-util.c
src/test/test-dlopen-so.c
src/test/test-dns-domain.c
src/test/test-ellipsize.c
src/test/test-engine.c
src/test/test-env-file.c
src/test/test-env-util.c
src/test/test-errno-list.c
src/test/test-errno-util.c
src/test/test-escape.c
src/test/test-ether-addr-util.c
src/test/test-exec-util.c
src/test/test-execute.c
src/test/test-exit-status.c
src/test/test-extract-word.c
src/test/test-fd-util.c
src/test/test-fileio.c
src/test/test-format-table.c
src/test/test-format-util.c
src/test/test-fs-util.c
src/test/test-fstab-util.c
src/test/test-glob-util.c
src/test/test-gpt.c
src/test/test-hashmap-plain.c
src/test/test-hashmap.c
src/test/test-hexdecoct.c
src/test/test-hmac.c
src/test/test-hostname-setup.c
src/test/test-hostname-util.c
src/test/test-id128.c
src/test/test-image-policy.c
src/test/test-import-util.c
src/test/test-in-addr-prefix-util.c
src/test/test-in-addr-util.c
src/test/test-install-root.c
src/test/test-ip-protocol-list.c
src/test/test-json.c
src/test/test-list.c
src/test/test-load-fragment.c
src/test/test-local-addresses.c
src/test/test-loop-block.c
src/test/test-macro.c
src/test/test-mempress.c
src/test/test-memstream-util.c
src/test/test-mount-util.c
src/test/test-mountpoint-util.c
src/test/test-net-naming-scheme.c
src/test/test-netlink-manual.c
src/test/test-nulstr-util.c
src/test/test-open-file.c
src/test/test-openssl.c
src/test/test-ordered-set.c
src/test/test-os-util.c
src/test/test-parse-argument.c
src/test/test-path-lookup.c
src/test/test-path-util.c
src/test/test-prioq.c
src/test/test-proc-cmdline.c
src/test/test-process-util.c
src/test/test-recovery-key.c
src/test/test-replace-var.c
src/test/test-rlimit-util.c
src/test/test-seccomp.c
src/test/test-secure-bits.c
src/test/test-serialize.c
src/test/test-set.c
src/test/test-sha256.c
src/test/test-sleep-config.c
src/test/test-socket-bind.c
src/test/test-socket-netlink.c
src/test/test-socket-util.c
src/test/test-specifier.c
src/test/test-stat-util.c
src/test/test-strbuf.c
src/test/test-string-util.c
src/test/test-strip-tab-ansi.c
src/test/test-strv.c
src/test/test-strxcpyx.c
src/test/test-sysctl-util.c
src/test/test-terminal-util.c
src/test/test-time-util.c
src/test/test-tmpfile-util.c
src/test/test-tpm2.c
src/test/test-udev-util.c
src/test/test-uid-range.c
src/test/test-unit-file.c
src/test/test-unit-name.c
src/test/test-user-util.c
src/test/test-utf8.c
src/test/test-varlink-idl.c
src/test/test-varlink.c
src/test/test-vpick.c
src/test/test-watch-pid.c
src/test/test-xattr-util.c
src/test/test-xml.c
src/timedate/timedated.c
src/timesync/meson.build
src/tmpfiles/meson.build
src/udev/meson.build
src/udev/net/link-config.c
src/udev/net/link-config.h
src/udev/scsi_id/scsi.h
src/udev/test-udev-rule-runner.c
src/udev/test-udev-spawn.c
src/udev/udev-builtin-blkid.c
src/udev/udev-builtin-btrfs.c
src/udev/udev-builtin-hwdb.c
src/udev/udev-builtin-input_id.c
src/udev/udev-builtin-keyboard.c
src/udev/udev-builtin-kmod.c
src/udev/udev-builtin-net_driver.c
src/udev/udev-builtin-net_id.c
src/udev/udev-builtin-net_setup_link.c
src/udev/udev-builtin-path_id.c
src/udev/udev-builtin-uaccess.c
src/udev/udev-builtin-usb_id.c
src/udev/udev-builtin.c
src/udev/udev-builtin.h
src/udev/udev-event.c
src/udev/udev-event.h
src/udev/udev-rules.c
src/udev/udev-spawn.c
src/udev/udev-worker.c
src/udev/udevadm-control.c
src/udev/udevadm-test-builtin.c
src/udev/udevadm-test.c
src/udev/udevadm-wait.c
src/udev/v4l_id/v4l_id.c
src/userdb/userdbctl.c
src/vmspawn/vmspawn-scope.c
src/vmspawn/vmspawn-util.c
src/vmspawn/vmspawn.c
test/README.testsuite
test/TEST-24-CRYPTSETUP/test.sh
test/TEST-50-DISSECT/test.sh
test/TEST-54-CREDS/test.sh
test/TEST-64-UDEV-STORAGE/test.sh
test/TEST-69-SHUTDOWN/test.sh
test/TEST-77-OPENFILE/Makefile [deleted symlink]
test/TEST-77-OPENFILE/test.sh [deleted file]
test/test-execute/exec-personality-s390x.service [new file with mode: 0644]
test/test-functions
test/test-network-generator-conversion.sh
test/test-network/conf/25-dhcp-server-veth-peer.network
test/test-network/conf/25-ipv6-prefix-veth-token-prefixstable.network
test/test-network/conf/25-veth-peer-no-address.network [new file with mode: 0644]
test/test-network/conf/persist-leases-no.conf [new file with mode: 0644]
test/test-network/systemd-networkd-tests.py
test/test-shutdown.py
test/testsuite-23.units/testsuite-23-openfile-server.socket [moved from test/units/testsuite-77-server.socket with 56% similarity]
test/testsuite-23.units/testsuite-23-openfile-server@.service [moved from test/units/testsuite-77-server@.service with 66% similarity]
test/units/end.sh
test/units/testsuite-04.journal-gatewayd.sh
test/units/testsuite-08.sh
test/units/testsuite-17.10.sh
test/units/testsuite-17.credentials.sh [new file with mode: 0755]
test/units/testsuite-17.link-property.sh
test/units/testsuite-23-openfile-child.sh [new file with mode: 0755]
test/units/testsuite-23.openfile.sh [new file with mode: 0755]
test/units/testsuite-29.sh
test/units/testsuite-43.sh
test/units/testsuite-46.sh
test/units/testsuite-50.DDI.sh [new file with mode: 0755]
test/units/testsuite-50.dissect.sh [new file with mode: 0755]
test/units/testsuite-50.mountnfsd.sh [new file with mode: 0755]
test/units/testsuite-50.sh
test/units/testsuite-50.sysext.sh [new file with mode: 0755]
test/units/testsuite-54.sh
test/units/testsuite-55.sh
test/units/testsuite-58.sh
test/units/testsuite-59.sh
test/units/testsuite-62.sh
test/units/testsuite-64.sh
test/units/testsuite-72.sh
test/units/testsuite-74.coredump.sh
test/units/testsuite-74.network-generator.sh
test/units/testsuite-74.networkctl.sh
test/units/testsuite-75.sh
test/units/testsuite-77-client.sh [deleted file]
test/units/testsuite-77-run.sh [deleted file]
test/units/testsuite-77.service [deleted file]
test/units/testsuite-77.sh [deleted file]
test/units/testsuite-82.sh
tmpfiles.d/20-systemd-ssh-generator.conf.in
tools/check-version-history.py
tools/git-contrib.sh
tools/git-post-rewrite-hook.sh [moved from mkosi.images/base/mkosi.conf.d/10-debian-ubuntu/initrd/mkosi.postinst with 50% similarity]
tools/git-setup.sh
units/meson.build
units/systemd-boot-check-no-failures.service.in
units/systemd-hibernate-clear.service.in [new file with mode: 0644]
units/systemd-mountfsd.service.in [new file with mode: 0644]
units/systemd-mountfsd.socket [new file with mode: 0644]
units/systemd-networkd-persistent-storage.service
units/systemd-nsresourced.service.in [new file with mode: 0644]
units/systemd-nsresourced.socket [new file with mode: 0644]
units/systemd-repart.service
units/systemd-udev-load-credentials.service [new file with mode: 0644]
units/systemd-udevd.service.in

index 549421a1cfdbbc335550be515aceecad9193db35..f3ba2cab8d0dcc4cba6e8e8c2755aa251e23e099 100644 (file)
@@ -140,6 +140,7 @@ body:
         - 'systemd-udevd'
         - 'systemd-userdb'
         - 'systemd-veritysetup'
+        - 'systemd-vmspawn'
         - 'systemd-xdg-autostart-generator'
         - 'timedatectl'
         - 'udevadm'
index 76b95a127a3173e1fc25667608bee06dbb7f82df..645cef916732d53cf57606c3f2dda43651dc35d6 100644 (file)
@@ -81,6 +81,7 @@ body:
         - 'systemd-udevd'
         - 'systemd-userdb'
         - 'systemd-veritysetup'
+        - 'systemd-vmspawn'
         - 'systemd-xdg-autostart-generator'
         - 'timedatectl'
         - 'udevadm'
index e6ae5dc68fa68520e84489738e6b8bda5b7a542f..bee39e8285222dbb5c55e447dc0d5aca5d6a9171 100644 (file)
@@ -145,5 +145,8 @@ policy:
           - name: veritysetup
             keys: ['systemd-veritysetup']
 
+          - name: vmspawn
+            keys: ['systemd-vmspawn']
+
           - name: xdg-autostart
             keys: ['systemd-xdg-autostart-generator']
index 86a31776f4932cf03b4f667acc16b71178d17b7a..e8952dc82a6dbd153e1f3aa7c854665442d1c8d5 100644 (file)
@@ -76,12 +76,18 @@ meson:
 mkosi:
   - changed-files:
     - any-glob-to-any-file: ['.mkosi/*', 'mkosi.build']
+mountfsd:
+  - changed-files:
+    - any-glob-to-any-file: ['src/mountfsd/*']
 network:
   - changed-files:
     - any-glob-to-any-file: ['src/libsystemd-network/**/*', 'src/network/**/*']
 nspawn:
   - changed-files:
     - any-glob-to-any-file: '**/*nspawn*'
+nsresource:
+  - changed-files:
+    - any-glob-to-any-file: '**/*nsresource*'
 portable:
   - changed-files:
     - any-glob-to-any-file: 'src/portable/**/*'
index 3db0e79528ed6494bb0c2d769d3b393970686393..948372ee965da93d5f10e43c8924bf1a1f097a65 100644 (file)
@@ -28,7 +28,7 @@ jobs:
           fetch-depth: 0
 
       - name: Differential ShellCheck
-        uses: redhat-plumbers-in-action/differential-shellcheck@b9df2a9417f69c056e0aeaf870abd9a2065a403e
+        uses: redhat-plumbers-in-action/differential-shellcheck@c15070885a82a2c93db8a765d332c38c50dde8b3
         with:
           # exclude all `.in` files because they may contain unsupported syntax, and they have to be preprocessed first
           # TEMPORARY: exclude bash completion files, they would generate too many defects in Code scanning dashboard (600+)
index e71153a6b080453958af0145177de7510367a9ec..0ecfb271642be739174f6b7b6341c6559046a394 100644 (file)
@@ -23,7 +23,7 @@ jobs:
       - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633
 
       - name: Parse issue form
-        uses: stefanbuck/github-issue-parser@c1a559d78bfb8dd05216dab9ffd2b91082ff5324
+        uses: stefanbuck/github-issue-parser@1e5bdee70d4b3e066a33aa0669ab782943825f94
         id: issue-parser
         with:
           template-path: .github/ISSUE_TEMPLATE/${{ matrix.template }}
index f9df0a22bad0069f8773a4df31251e5c9235019b..2f5d5b28f5c0d07aa6b7d23746b4daaf7d2fb02e 100644 (file)
@@ -64,10 +64,8 @@ jobs:
             release: "39"
           - distro: fedora
             release: rawhide
-          # TODO: Re-enable once https://lists.opensuse.org/archives/list/factory@lists.opensuse.org/thread/AU4NWTBXNA7MVAUXWR74XYCHCSZN4Z4K/
-          # is resolved or https://build.opensuse.org/request/show/1152118 is merged.
-          # - distro: opensuse
-          #   release: tumbleweed
+          - distro: opensuse
+            release: tumbleweed
           - distro: centos
             release: "9"
 
@@ -76,7 +74,7 @@ jobs:
 
     steps:
     - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633
-    - uses: systemd/mkosi@1445b389750af22756c0fde6facc1f2f343340b4
+    - uses: systemd/mkosi@a845d4108ac87ca443bd1ad78ab53520bffd2eda
 
     # Freeing up disk space with rm -rf can take multiple minutes. Since we don't need the extra free space
     # immediately, we remove the files in the background. However, we first move them to a different location
@@ -102,7 +100,6 @@ jobs:
         ToolsTree=default
         ToolsTreeDistribution=fedora
         QemuVsock=yes
-        # Sometimes we run on a host with /dev/kvm, but it is broken, so explicitly disable it
         QemuKvm=yes
         # TODO: Drop once https://bugs.launchpad.net/ubuntu/+source/linux/+bug/2038777 is fixed in Github Actions
         QemuFirmware=uefi
@@ -114,6 +111,7 @@ jobs:
         tee mkosi.conf.d/99-ci.conf <<EOF
         [Host]
         KernelCommandLineExtra=systemd.unit=mkosi-check-and-shutdown.service
+                               systemd.log_level=debug
                                systemd.journald.max_level_console=debug
                                # udev's debug log output is very verbose, so up it to info in CI.
                                udev.log_level=info
index e4c9fd6c34a5fb954b1931758efef00546550b1d..ea21bafcaccc78dcd70b82df1f4d8f705a0c4e7d 100644 (file)
@@ -28,8 +28,9 @@ The following exceptions apply:
    verbatim from the Linux kernel source tree and are licensed under **GPL-2.0 WITH
    Linux-syscall-note** and are used within the scope of the Linux-syscall-note
    exception provisions
- * the src/shared/initreq.h header is licensed under original license,
-   **LGPL-2.0-or-later**.
+ * the following sources are licensed under the **LGPL-2.0-or-later** license:
+   - src/basic/utf8.c
+   - src/shared/initreq.h
  * the src/shared/linux/bpf_insn.h header is copied from the Linux kernel
    source tree and is licensed under either **BSD-2-Clause** or **GPL-2.0-only**,
    and thus is included in the systemd build under the BSD-2-Clause license.
diff --git a/NEWS b/NEWS
index 7014e7582af84316544143edf06381ae2ce76d4a..c28f485ceb7d953747634c24a501e45f7eba4559 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -4,8 +4,8 @@ CHANGES WITH 256 in spe:
 
         Announcements of Future Feature Removals and Incompatible Changes:
 
-        * Support for flushing of the nscd user/group database caches will be
-          dropped in a future release.
+        * Support for automatic flushing of the nscd user/group database caches
+          will be dropped in a future release.
 
         * Support for cgroup v1 ('legacy' and 'hybrid' hierarchies) is now
           considered obsolete and systemd by default will refuse to boot under
@@ -22,11 +22,11 @@ CHANGES WITH 256 in spe:
           that are not configured in the .network file are removed.
 
         * systemd-gpt-auto-generator will stop generating units for ESP or
-          XBOOTLDR partitions if it finds mount entries in the /boot/ or /efi/
-          hierarchies in fstab. This is to prevent the generator from
-          interfering with systems where ESP is explicitly configured to be
-          mounted at some path, for example /boot/efi/ (this type of setup is
-          obsolete but still commonly found).
+          XBOOTLDR partitions if it finds mount entries for or below the /boot/
+          or /efi/ hierarchies in /etc/fstab. This is to prevent the generator
+          from interfering with systems where the ESP is explicitly configured
+          to be mounted at some path, for example /boot/efi/ (this type of
+          setup is obsolete but still commonly found).
 
         * The behavior of systemd-sleep and systemd-homed has been updated to
           freeze user sessions when entering the various sleep modes or when
@@ -37,83 +37,64 @@ CHANGES WITH 256 in spe:
           and related services, and SYSTEMD_HOME_LOCK_FREEZE_SESSION=false for
           systemd-homed.service.
 
-        * systemd-tmpfiles and systemd-sysusers, when given a relative path
-          (with at least one directory separator '/'), will open the file
-          directly, instead of searching for the given partial path in the
-          standard locations. The old mode wasn't useful because tmpfiles.d and
-          sysusers.d configuration has a flat structure with no subdirectories
-          under the standard locations and this change makes it easier to work
-          with local files with those tools.
+        * systemd-tmpfiles and systemd-sysusers, when given a relative
+          configuration file path (with at least one directory separator '/'),
+          will open the file directly, instead of searching for the given
+          partial path in the standard locations. The old mode wasn't useful
+          because tmpfiles.d/ and sysusers.d/ configuration has a flat
+          structure with no subdirectories under the standard locations and
+          this change makes it easier to work with local files with those
+          tools.
 
         * systemd-tmpfiles now properly applies nested configuration to 'R' and
-          'D' stanzas. For example, with 'R /foo; x /foo/bar', /foo/bar will
-          now be excluded from removal.
+          'D' stanzas. For example, with the combination of 'R /foo' and 'x
+          /foo/bar', /foo/bar will now be excluded from removal.
 
         General Changes and New Features:
 
-        * Various programs will load the main configuration from under
-          /usr/lib/, /usr/local/lib/, and /run/, not just from under /etc/. For
-          example, systemd-logind will look for /etc/systemd/logind.conf,
-          /run/systemd/logind.conf, /usr/local/lib/systemd/logind.conf, and
-          /usr/lib/systemd/logind.conf, and use the first file that is found.
-          This means that the location logic for the main config file and for
-          drop-ins is now the same.
+        * Various programs will now attempt to load the main configuration file
+          from locations below /usr/lib/, /usr/local/lib/, and /run/, not just
+          below /etc/. For example, systemd-logind will look for
+          /etc/systemd/logind.conf, /run/systemd/logind.conf,
+          /usr/local/lib/systemd/logind.conf, and /usr/lib/systemd/logind.conf,
+          and use the first file that is found.  This means that the search
+          logic for the main config file and for drop-ins is now the same.
 
-          ukify will look for the config files in /usr/lib/kernel/ and the
-          other locations, and now also supports drop-ins.
+          Similarly, kernel-install will look for the config files in
+          /usr/lib/kernel/ and the other search locations, and now also
+          supports drop-ins.
 
           systemd-udevd now supports drop-ins for udev.conf.
 
         * A new 'systemd-vpick' binary has been added. It implements the new
-          vpick protocol, where a .v directory may contain multiple files with
-          a version, following the UAPI version format specification, embedded
-          in the file name. The files are ordered by version and the newest one
-          is selected.
-
-          systemd-nspawn, systemd-dissect, and the RootDirectory=, RootImage=,
-          ExtensionImages=, and ExtensionDirectories= settings for units now
-          support the vpick protocol and allow the latest version to be
-          selected automatically if a "*.v/" directory is specified as the
-          source.
-
-        * Credentials can now be made accessible to and used by unprivileged
-          users. 'systemd-creds --user --uid=<user>' will encrypt or decrypt a
-          credential for a specific user.
-
-        * With systemd-homed, it is now possible to log in and activate an
-          encrypted home area over SSH.
-
-          homectl is now installed as a multi-call binary. When invoked as
-          systemd-home-fallback-shell it can be used as a temporary shell which
-          allows the home area to interactively unlocked. When the home area
-          becomes available, the temporary shell executes the normal one.
-
-          systemd-homed gained new methods
-          org.freedesktop.home1.Manager.RefHomeUnrestricted,
-          org.freedesktop.home1.Home.RefUnrestricted,
-          org.freedesktop.home1.Manager.ActivateHomeIfReferenced, and
-          org.freedesktop.home1.Home.ActivateIfReferenced to allow logging in
-          without activating the home area and then activating the home area
-          later.
-
-        * JSON User Records have been extended with a separate storage area
-          called "User Record Blob Directories". This is intended to store the
-          user's background image, avatar picture, and other similar items
-          which are too large to fit into the User Record itself.
+          vpick protocol, where a "*.v/" directory may contain multiple files
+          which have versions (following the UAPI version format specification)
+          embedded in the file name. The files are ordered by version and
+          the newest one is selected.
 
-          systemd-homed, userdbctl, and homectl gained support for blob
-          directories.
+          systemd-nspawn --image=/--directory=, systemd-dissect, and the
+          RootDirectory=, RootImage=, ExtensionImages=, and
+          ExtensionDirectories= settings for units now support the vpick
+          protocol and allow the latest version to be selected automatically if
+          a "*.v/" directory is specified as the source.
+
+        * Encrypted service credentials may now be made accessible to
+          unprivileged users. systemd-creds gained new options --user/--uid=
+          for encrypting/decrypting a credential for a specific user.
 
         * New command-line tool 'importctl' to download, import, and export
           disk images via systemd-importd is added with the following verbs:
           pull-tar, pull-raw, import-tar, import-raw, import-fs, export-tar,
-          export-raw, list-transfers, cancel-transfer.
+          export-raw, list-transfers, cancel-transfer. This functionality was
+          previously available in "machinectl", where it was exclusively for
+          machine image. The new "importctl" generalizes this for sysext,
+          confext, portable service images, too.
 
-        Service Manager:
+        Service Management:
 
-        * New manager setting ProtectSystem= has been added. It is analogous to
-          the unit setting, but applies to the whole system. It is enabled by
-          default in the initrd.
+        * New system manager setting ProtectSystem= has been added. It is
+          analogous to the unit setting, but applies to the whole system. It is
+          enabled by default in the initrd.
 
         * New unit setting WantsMountsFor= has been added. It is analogous to
           RequiresMountsFor=, but with a Wants= dependency instead of
@@ -125,16 +106,17 @@ CHANGES WITH 256 in spe:
           memory.zswap.writeback cgroup knob added in kernel 6.8.
 
         * The manager gained a org.freedesktop.systemd1.StartAuxiliaryScope()
-          method to devolve some processes from a service into a new scope.
-          This new scope will remain even if the original service unit is
-          restarted. Cgroup properties of the new scope are copied from the
-          service, so various limits are retained.
+          D-Bus method to devolve some processes from a service into a new
+          scope.  This new scope will remain even if the original service unit
+          is restarted. Control group properties of the new scope are copied
+          from the originating unit, so various limits are retained.
 
         * Units now expose properties EffectiveMemoryMax=,
           EffectiveMemoryHigh=, and EffectiveTasksMax=, which report the
           most stringent limit systemd is aware of for the given unit.
 
-        * A new specifier %D expands to $XDG_DATA_HOME.
+        * A new unit file specifier %D expands to $XDG_DATA_HOME (for user
+          services) or /usr/share/ (for system services).
 
         * AllowedCPUs= now supports specifier expansion.
 
@@ -146,31 +128,80 @@ CHANGES WITH 256 in spe:
 
         * PAMName= now implies SetLoginEnvironment=yes.
 
-        * homectl gained a new verb 'firstboot', and a new
-          systemd-homed-firstboot.service unit uses this verb to create users
-          in a first boot environment, either from credentials or by querying
-          interactively.
-
         * systemd.firstboot=no can be used on the kernel command-line to
           disable interactive queries, but allow other first boot configuration
           to happen based on credentials.
 
-        * A new kernel command-line option systemd.default_debug_tty= can be
-          used to specify the TTY for the debug shell, independently of
-          enabling or disabling it.
+        * The system's hostname can be configured via the systemd.hostname
+          system credential.
+
+        * The systemd binary will no longer chainload sysvinit's "telinit"
+          binary when called under the init/telinit name on a system that
+          isn't booted with systemd. This previously has been supported to make
+          sure a distribution that has both init systems installed can be
+          reasonably switched from one to the other via a simple
+          reboot. Distributions apparently have lost interest in this, and the
+          functionality has not been supported on the primary distribution this
+          was still intended for for a longer time, and hence has been removed
+          now.
 
-        * Systemd hostname can be configured via the systemd.hostname
-          credential.
+        * A new concept called "capsules" has been introduced. "Capsules"
+          encapsulate additional per-user service managers, whose users are
+          transient and are only defined as long as the service manager
+          is running (implemented via DynamicUser=1). These service managers run
+          off home directories defined in /var/lib/capsules/<name>, where
+          <name> is a the capsule's name. These home directories can contain
+          regular per-user services and other units. A capsule is started via a
+          simple "systemctl start capsule@<name>.service". See the
+          capsule@.service(5) man page for further details. Various systemd
+          tools (including, and most importantly, systemctl and systemd-run)
+          have been updated to interact with capsules via the new
+          "--capsule="/"-C" switch.
+
+        * .socket units gained a new setting PassFileDescriptorsToExec=, taking
+          a boolean value. If set to true the file descriptors the socket unit
+          encapsulates are passed to the ExecStartPost=, ExecStopPre=,
+          ExecStopPost= using the usual $LISTEN_FDS interface. This may be used
+          for doing additional initializations on the sockets once they are
+          allocated (for example, install an additional eBPF program on them).
+
+        * The .socket setting MaxConnectionsPerSource= (which so far put a
+          limit on concurrent connections per IP in Accept=yes socket units),
+          now also has an effect on AF_UNIX sockets: it will put a limit on the
+          number of simultaneous connections from the same source UID (as
+          determined via SO_PEERCRED). This is useful for implementing IPC
+          services in a simple Accept=yes mode.
+
+        * The service manager will now maintain a counter of soft reboot cycles
+          the system went through so far. It may be queried via the D-Bus APIs.
+
+        * systemd's execution logic now supports the new pidfd_spawn() API
+          introduced by glibc 2.39, which allows us to invoke a subprocess in a
+          target cgroup and get a pidfd back in a single operation.
+
+        * systemd/PID 1 will now send an additional sd_notify() message to its
+          supervising VMM or container manager reporting the selected hostname
+          ("X_SYSTEMD_HOSTNAME=") and machine ID ("X_SYSTEMD_MACHINE_ID=") at
+          boot. Moreover, the service manager will send additional sd_notify()
+          messages ("X_SYSTEMD_UNIT_ACTIVE=") whenever a target unit is
+          reached. This can be used by VMMs/container managers to schedule
+          access to the system precisely. For example, the moment a system
+          reports "ssh-access.target" being reached a VMM/container manager
+          knows it can now connect to the system via SSH. Finally, a new
+          sd_notify() message ("X_SYSTEMD_SIGNALS_LEVEL=2") is sent the moment
+          PID 1 successfully completed installation of its various UNIX process
+          signal handlers (i.e. the moment where SIGRTMIN+4 sent to PID 1 will
+          start to have the effect of shutting down the system cleanly).
 
-        The Journal:
+        Journal:
 
         * systemd-journald can now forward journal entries to a socket
           (AF_INET, AF_INET6, AF_UNIX, or AF_VSOCK). The socket can be
           specified in journald.conf via a new option ForwardAddress= or via
-          the 'journald.forward_address' credential.
-
-        * systemd-journal-remote now also accepts AF_VSOCK and AF_UNIX sockets
-          (so it can be used to receive entries forwarded by systemd-journald).
+          the 'journald.forward_address' credential. Log records are sent in
+          the Journal Export Format. A related setting MaxLevelSocket= has been
+          added to control the maximum log levels for the messages sent to this
+          socket.
 
         * systemd-vmspawn gained a new --forward-journal= option to forward the
           virtual machine's journal entries to the host. This is done over a
@@ -183,75 +214,90 @@ CHANGES WITH 256 in spe:
 
         * journalctl gained a new --list-namespaces option.
 
+        * systemd-journal-remote now also accepts AF_VSOCK and AF_UNIX sockets
+          (so it can be used to receive entries forwarded by systemd-journald).
+
         * systemd-journal-gatewayd allows restricting the time range of
-          retrieved entries with realtime=[<since>]:[<until>].
+          retrieved entries with a new "realtime=[<since>]:[<until>]" URL
+          parameter.
+
+        * systemd-cat gained a new option --namespace= to specify the target
+          journal namespace to which the output shall be connected.
+
+        * systemd-bsod gained a new option --tty= to specify the output TTY
 
         Device Management:
 
-        * Udev now creates symlinks that combine by-path and by-{label,uuid}
+        * /dev/ now contains symlinks that combine by-path and by-{label,uuid}
           information:
-            /dev/disk/by-path/<path>/by-<label|uuid|…>/<label|uuid|…>.
+
+              /dev/disk/by-path/<path>/by-<label|uuid|…>/<label|uuid|…>
+
           This allows distinguishing partitions with identical contents on
           multiple storage devices. This is useful, for example, when copying
           raw disk contents between devices.
 
-        * Udev now creates persistent /dev/media/by-path symlinks for media
-          controllers. For example, the uvcvideo driver may create /dev/media0
-          which will be linked as
+        * systemd-udevd now creates persistent /dev/media/by-path/ symlinks for
+          media controllers. For example, the uvcvideo driver may create
+          /dev/media0 which will be linked as
           /dev/media/by-path/pci-0000:04:00.3-usb-0:1:1.0-media-controller.
 
+        * A new unit systemd-udev-load-credentials.service has been added
+          to pick up udev.conf drop-ins and udev rules from credentials.
+
         * An allowlist/denylist may be specified to filter which sysfs
           attributes are used when crafting network interface names. Those
-          lists are stored as HWDB entries
+          lists are stored as hwdb entries
             ID_NET_NAME_ALLOW_<sysfsattr>=0|1
           and
             ID_NET_NAME_ALLOW=0|1.
+
           The goal is to avoid unexpected changes to interface names when the
           kernel is updated and new sysfs attributes become visible.
 
         * A new unit tpm2.target has been added to provide a synchronization
-          point for units which expect the TPM hardware to be available.
+          point for units which expect the TPM hardware to be available. A new
+          generator "systemd-tpm2-generator" has been added that will insert
+          this target whenever it detects that the firmware has initialized a
+          TPM, but Linux hasn't loaded a driver for it yet.
 
         * systemd-backlight now properly supports numbered devices which the
           kernel creates to avoid collisions in the leds subsystem.
 
-        * systemd-hwdb update operation can be disabled with environment
+        * systemd-hwdb update operation can be disabled with a new environment
           variable SYSTEMD_HWDB_UPDATE_BYPASS=1.
 
-        * systemd-logind gained a new org.freedesktop.login1.Manager.Sleep()
-          method that automatically redirects to SuspendThenHibernate(),
-          Suspend(), HybridSleep(), or Hibernate(), depending on what is
-          supported and configured, a new configuration setting SleepOperation=,
-          and an accompanying helper method
-          org.freedesktop.login1.Manager.CanSleep() and property
-          org.freedesktop.login1.Manager.SleepOperation.
+        systemd-hostnamed:
 
-          'systemctl sleep' calls the new method to automatically put the
-          machine to sleep in the most appropriate way.
+        * systemd-hostnamed now exposes the machine ID and boot ID via
+          D-Bus. It also exposes the hosts AF_VSOCK CID, if available.
 
-        * systemd-hostnamed now exposes the machine ID and boot ID via D-Bus.
+        * systemd-hostnamed now provides a basic Varlink interface.
 
-        * systemd-hostnamed now provides a Varlink interface.
-
-        * systemd-hostnamed exports the data in os-release(5) and
+        * systemd-hostnamed exports the full data in os-release(5) and
           machine-info(5) via D-Bus and Varlink.
 
+        * hostnamectl now shows the system's product UUID and hardware serial
+          number if known.
+
         Network Management:
 
-        * systemd-networkd now provides a Varlink interface.
+        * systemd-networkd now provides a basic Varlink interface.
 
-        * systemd-networkd's proxy support gained a new option to configure
-          a private VLAN variant of the proxy ARP supported by the kernel
-          under the name IPv4ProxyARPPrivateVLAN=.
+        * systemd-networkd's ARP proxy support gained a new option to configure
+          a private VLAN variant of the proxy ARP supported by the kernel under
+          the name IPv4ProxyARPPrivateVLAN=.
 
         * systemd-networkd now exports the NamespaceId and NamespaceNSID
-          properties via D-Bus and Varlink.
+          properties via D-Bus and Varlink. (which expose the inode and NSID of
+          the network namespace the networkd instance manages)
 
         * systemd-networkd now supports IPv6RetransmissionTimeSec= and
           UseRetransmissionTime= settings in .network files to configure
           retransmission time for IPv6 neighbor solicitation messages.
 
-        * networkctl gained new verbs 'mask' and 'unmask'.
+        * networkctl gained new verbs 'mask' and 'unmask' for masking networkd
+          configuration files such as .network files.
 
         * 'networkctl edit --runtime' allows editing volatile configuration
           under /run/systemd/network/.
@@ -259,42 +305,148 @@ CHANGES WITH 256 in spe:
         * The implementation behind TTLPropagate= network setting has been
           removed and the setting is now ignored.
 
-        * systemd-network-generator will now pick up .netdev/.link/.network
-          configuration from credentials.
+        * systemd-network-generator will now pick up .netdev/.link/.network/
+          networkd.conf configuration from system credentials.
 
-        * systemd-networkd will now pick up wireguard configuration from
+        * systemd-networkd will now pick up wireguard secrets from
           credentials.
 
-        * systemd-ssh-proxy is a new SSH client plugin that allows connecting
-          to AF_SOCK or AF_UNIX sockets.
+        * systemd-networkd's Varlink API now supports enumerating LLDP peers.
+
+        * .link files now support new Property=, ImportProperty=,
+          UnsetProperty= fields for setting udev properties on a link.
+
+        * The various .link files that systemd ships for interfaces that are
+          supposed to be managed by systemd-networkd only now carry a
+          ID_NET_MANAGED_BY=io.systemd.Network udev property ensuring that
+          other network management solutions honouring this udev property do
+          not come into conflict with networkd, trying to manage these
+          interfaces.
+
+        * .link files now support a new ReceivePacketSteeringCPUMask= setting
+          for configuring which CPUs to steer incoming packets to.
+
+        systemd-nspawn:
 
         * systemd-nspawn now provides a /run/systemd/nspawn/unix-export/
           directory where the container payload can expose AF_UNIX sockets to
           allow them them to be accessed from outside.
 
-        * systemd-nspawn will tint the background for container output.
-          This can be controller with the new --backgroup= option.
+        * systemd-nspawn will tint the terminal background for containers in a
+          blueish color. This can be controller with the new --background=
+          switch.
 
-        * systemd-nspawn gained support for the 'owneridmap' option for bind
+        * systemd-nspawn gained support for the 'owneridmap' option for --bind=
           mounts to map the target directory owner from inside the container to
           the owner of the directory bound from the host filesystem.
 
-        * An sshd config drop-in to allow ssh keys acquired via userdbctl to be
-          used for authorization.
+        * systemd-nspawn now supports moving Wi-Fi network devices into a
+          container, just like other network interfaces.
 
-        * New generator systemd-ssh-generator can be used to bind a
-          socket-activated SSH instance to a local AF_SOCK or AF_UNIX socket.
-          This generator will automatically bind /run/host/unix-export/ssh.
+        systemd-resolved:
 
-        * systemd-resolved now implements RFC 8914 EDE error codes.
+        * systemd-resolved now reads RFC 8914 EDE error codes provided by
+          upstream DNS services.
 
         * systemd-resolved and resolvectl now support RFC 9460 SVCB and HTTPS
-          records.
+          records, as well as RFC 2915 NAPTR records.
 
         * resolvectl gained a new option --relax-single-label= to allow
-          querying single-label hostnames via DNS.
-
-        Systemd-boot and systemd-stub and Related Tools:
+          querying single-label hostnames via unicast DNS on a per-query basis.
+
+        * systemd-resolved's Varlink IPC interface now supports resolving
+          DNS-SD services as well as an API for resolving raw DNS RRs.
+
+        * systemd-resolved's .dnssd DNS_SD service description files now
+          support DNS-SD "subtypes" via the new SubType= setting.
+
+        * systemd-resolved's configuration may now be reloaded without
+          restarting the service. (i.e. "systemctl reload systemd-resolved" is
+          now supported)
+
+        SSH Integration:
+
+        * An sshd config drop-in to allow ssh keys acquired via userdbctl (for
+          example expose by homed accounts) to be used for authorization of
+          incoming SSH connections.
+
+        * A small new unit generator "systemd-ssh-generator" has been added. It
+          checks if the sshd binary is installed. If so, it binds it via
+          per-connection socket activation to various sockets depending on the
+          execution context:
+
+            • If the system is run in a VM providing AF_VSOCK support, it
+              automatically binds sshd to AF_VSOCK port 22.
+
+            • If the system is invoked as a full-OS container and the container
+              manager pre-mounts a directory /run/host/unix-export/, it will
+              bind sshd to an AF_UNIX socket /run/host/unix-export/ssh. The
+              idea is the container manager bind mounts the directory to an
+              appropriate place on the host as well, so that the AF_UNIX socket
+              may be used to easily connect from the host to the container.
+
+            • sshd is also bound to an AF_UNIX socket
+              /run/ssh-unix-local/socket, which may be to use ssh/sftp in a
+              "sudo"-like fashion to access resources of other local users.
+
+            • Via the kernel command line option "systemd.ssh_listen=" and the
+              system credential "ssh.listen" sshd may be bound to additional,
+              explicitly configured options, including AF_INET/AF_INET6 ports.
+
+          In particular the first two mechanisms should make dealing with local
+          VMs and full OS containers a lot easier, as SSH connections will
+          *just* *work* from the host – even if no networking is available
+          whatsoever.
+
+          systemd-ssh-generator optionally generates a per-connection
+          socket activation service file wrapping sshd. This is only done if
+          the distribution does not provide one on its own under the name
+          "sshd@.service". The generated unit only works correctly if the SSH
+          privilege separation ("privsep") directory exists. Unfortunately
+          distributions vary wildly where they place this directory. An
+          incomprehensive list:
+
+            • /usr/share/empty.sshd/  (new fedora)
+            • /var/empty/
+            • /var/empty/sshd/
+            • /run/sshd/              (debian/ubuntu?)
+
+          If the SSH privsep directory is placed below /var/ or /run/ care
+          needs to be taken that the directory is created automatically at boot
+          if needed, since these directories possibly or always come up
+          empty. This can be done via a tmpfiles.d/ drop-in. You may use the
+          "sshdprivsepdir" meson option provided by systemd to configure the
+          directory, in case you want systemd to create the directory as needed
+          automatically, if your distribution does not cover this natively.
+
+          Recommendations to distributions, in order to make things just work:
+
+            • Please provide a per-connection SSH service file under the name
+              "sshd@.service".
+
+            • Please move the SSH privsep dir into /usr/ (so that it is truly
+              immutable on image-based operating systems, is strictly under
+              package manager control, and never requires recreation if the
+              system boots up with an empty /run/ or /var/).
+
+            • As an extension of this: please consider following Fedora's lead
+              here, and use /usr/share/empty.sshd/ to minimize needless
+              differences between distributions.
+
+            • If your distribution insists on placing the directory in /var/ or
+              /run/ then please at least provide a tmpfiles.d/ drop-in to
+              recreate it automatically at boot, so that the sshd binary just
+              works, regardless in which context it is called.
+
+        * A small tool "systemd-ssh-proxy" has been added, which is supposed to
+          act as counterpart to "systemd-ssh-generator". It's a small plug-in
+          for the SSH client (via ProxyCommand/ProxyUseFdpass) to allow it to
+          connect to AF_VSOCK or AF_UNIX sockets. Example: "ssh vsock/4711"
+          connects to a local VM with cid 4711, or "ssh
+          unix/run/ssh-unix-local/socket" to connect to the local host via the
+          AF_UNIX socket /run/ssh-unix-local/socket.
+
+        systemd-boot and systemd-stub and Related Tools:
 
         * TPM 1.2 PCR measurement support has been removed from systemd-stub.
           TPM 1.2 is obsolete and – due to the (by today's standards) weak
@@ -303,7 +455,11 @@ CHANGES WITH 256 in spe:
           of systemd's codebase never supported TPM 1.2, the support has now
           been removed from systemd-stub as well.
 
-        * Confexts are loaded by systemd-stub from the ESP as well.
+        * systemd-stub will now measure its payload via the new EFI
+          Confidential Computing APIs (CC), in addition to the pre-existing
+          measurements to TPM.
+
+        * confexts are loaded by systemd-stub from the ESP as well.
 
         * The pcrlock policy is saved in an unencrypted credential file
           "pcrlock.<entry-token>.cred" under XBOOTLDR/ESP in the
@@ -316,32 +472,53 @@ CHANGES WITH 256 in spe:
         * systemd-pcrlock gained an --entry-token= option to configure the
           entry-token.
 
-        * systemd-pcrlock now provides a Varlink interface and can be
-          run as a daemon via a template unit.
+        * systemd-pcrlock now provides a basic Varlink interface and can be run
+          as a daemon via a template unit.
 
-        * bootctl now provides a Varlink interface and can be run as a daemon
-          via a template unit.
+        * bootctl now provides a basic Varlink interface and can be run as a
+          daemon via a template unit.
+
+        * systemd-measure gained new options --certificate=, --private-key=,
+          and --private-key-source= to allow using OpenSSL's "engines" or
+          "providers" as the signing mechanism to use when creating signed
+          TPM2 PCR measurement values.
 
         * ukify gained support for signing of PCR signatures via OpenSSL's
           engines and providers.
 
         * ukify now supports zboot kernels.
 
-        Command-line tools:
+        * systemd-boot now supports passing additional kernel command line
+          switches to invoked kernels via an SMBIOS Type #11 string
+          "io.systemd.boot.kernel-cmdline-extra". This is similar to the
+          pre-existing support for this in systemd-stub, but also applies to
+          Type #1 Boot Loader Specification Entries.
+
+        * systemd-boot's automatic SecureBoot enrollment support gained support
+          for enrolling "dbx" too (Previously, only db/KEK/PK enrollment was
+          supported). It also now supports UEFI "Custom" mode.
+
+        systemd-run/run0:
 
         * systemd-run is now a multi-call binary. When invoked as 'run0', it
           provides as interface similar to 'sudo', with all arguments starting
-          at the first non-option parameter being treated the command to
-          invoke as root. Unlike 'sudo' and similar tools, it does not make use
-          of setuid binaries or other privilege escalation methods, but instead
+          at the first non-option parameter being treated the command to invoke
+          as root. Unlike 'sudo' and similar tools, it does not make use of
+          setuid binaries or other privilege escalation methods, but instead
           runs the specified command as a transient unit, which is started by
           the system service manager, so privileges are dropped, rather than
-          gained, thus implementing a much more robust and safe security model.
+          gained, thus implementing a much more robust and safe security
+          model. As usual, authorization is managed via Polkit.
+
+        * systemd-run/run0 will now tint the terminal background on supported
+          terminals: in a reddish tone when invoking a root service, in a
+          yellowish tone otherwise. This may be controlled and turned off via
+          the new --background= switch.
 
         * systemd-run gained a new option '--ignore-failure' to suppress
           command failures.
 
-        * systemd-creds gained new options --user/--uid=.
+        Command-line tools:
 
         * 'systemctl edit --stdin' allows creation of unit files and drop-ins
           with contents supplied via standard input. This is useful when creating
@@ -370,6 +547,18 @@ CHANGES WITH 256 in spe:
 
         * resolvectl now supports -j/--json= for --type=.
 
+        * systemd-tmpfiles gained a new option --dry-run to print what would be
+          done without actually taking action.
+
+        * varlinkctl gained a new --collect switch to collect all responses of
+          a method call that supports multiple replies and turns it into a
+          single JSON array.
+
+        * systemd-dissect gained a new --make-archive option to generate an
+          archive file (tar.gz and similar) from a disk image.
+
+        systemd-vmspawn:
+
         * systemd-vmspawn gained a new --firmware= option to configure or list
           firmware definitions for Qemu, a new --tpm= option to enable or
           disable the use of a software TPM, a new --linux= option to specify a
@@ -385,94 +574,270 @@ CHANGES WITH 256 in spe:
         * A new systemd-vmspawn@.service can be used to launch systemd-vmspawn
           as a service.
 
-        * varlinkctl gained support for the "ssh:" transport. This requires
-          OpenSSH 9.4 or newer.
+        * systemd-vmspawn gained the new --console= and --background= switches
+          that control how to interact with the VM. As before, by default an
+          interactive terminal interface is provided, but now with a background
+          tinted with a greenish hue.
 
-        * varlinkctl gained a new --collect switch to collect all responses of
-          a method call emitted in JSON_SEQ mode and turn them into normal
-          JSON.
+        * systemd-vmspawn can now register its VMs with systemd-machined,
+          controlled via the --register= switch.
 
-        * systemd-sysext gained support for mutable system extensions, where a
-          writeable upperdir is stored under /var/lib/extensions.mutable/, and
-          a new --mutable option to configure this behaviour.
+        * machinectl's start command (and related) can now invoke images either
+          as containers via `systemd-nspawn` (switch is --runner=nspawn, the
+          default) or as VMs via `systemd-vmspawn` (switch is --runner=vmspawn,
+          or short -V).
 
-        * systemd-dissect gained a new --make-archive-option to generate an
-          archive file from a disk image.
+        * systemd-vmspawn now supports two switches --pass-ssh-key= and
+          --ssh-key-type= to optionally set up transient SSH keys to pass to the
+          invoked VMs in order to be able to SSH into them once booted.
+
+        systemd-repart:
 
         * systemd-repart gained new options --generate-fstab= and
-          --generate-crypttab= to write the fstab and crypttab files.
+          --generate-crypttab= to write out fstab and crypttab files matching the
+          generated partitions.
 
         * systemd-repart gained a new option --private-key-source= to allow
           using OpenSSL's "engines" or "providers" as the signing mechanism to
           use when creating verity signature partitions.
 
-        * systemd-measure gained new options --certificate=, --private-key=,
-          and --private-key-source= to allow using OpenSSL's "engines" or
-          "providers" as the signing mechanism to use when creating signed
-          TPM2 PCR measurement values.
-
-        * systemd-tmpfiles gained a new option --dry-run to print what would be
-          done without actually taking action.
-
-        * systemd-bsod gained a new option --tty= to specify the output TTY
-
-        * timedatectl and machinectl gained option '-P', an alias for
-          '--value --property=…'.
-
-        * Various tools that pretty-print config files will now highlight
-          configuration directives.
+        * systemd-repart gained a new DefaultSubvolume= setting in repart.d/
+          drop-ins that allow configuring the default btrfs subvolume for newly
+          formatted btrfs file systems.
 
         Libraries:
 
-        * libsystemd gained new call sd_bus_creds_new_from_pidfd to get a
+        * libsystemd gained new call sd_bus_creds_new_from_pidfd() to get a
           credentials object for a pidfd and sd_bus_creds_get_pidfd_dup() to
           retrieve the pidfd from a credentials object.
 
+        * sd-bus' credentials logic will now also acquire peer's UNIX group
+          lists and peer's pidfd if supported and requested.
+
         * RPM macro %_kernel_install_dir has been added with the path
           to the directory for kernel-install plugins.
 
-        Other:
+        * The liblz4, libzstd, liblzma, libkmod, libgcrypt dependencies have
+          been changed from regular shared library dependencies into dlopen()
+          based ones.
+
+        * The sd-journal API gained a new call
+          sd_journal_stream_fd_with_namespace() which is just like
+          sd_journal_stream_fd() but creates a log stream targeted at a
+          specific log namespace.
+
+        systemd-cryptsetup/systemd-cryptenroll:
+
+        * systemd-cryptenroll can now enroll directly with a PKCS11 public key
+          (instead of a certificate).
+
+        * systemd-cryptsetup/systemd-cryptenroll now may lock a disk against a
+          PKCS#11 provided EC key (before it only supported RSA).
+
+        * systemd-cryptsetup gained support for crypttab option
+          link-volume-key= to link the volume key into the kernel keyring when
+          the volume is opened.
+
+        * systemd-cryptenroll will no longer enable Dictionary Attack
+          Protection (i.e. turn on NO_DA) for TPM enrollments that do not
+          involve a PIN. DA should not be necessary in that case (since key
+          entropy is high enough to make this unnecessary), but risks
+          accidental lock-out in case of unexpected PCR changes.
+
+        * systemd-cryptenroll now supports enrolling a new slot while unlocking
+          the old slot via TPM2 (previously unlocking only worked via password
+          or FIDO2).
+
+        Documentation:
+
+        * The remaining documentation that was on
+          https://freedesktop.org/wiki/Software/systemd/ has been moved to
+          https://systemd.io/.
+
+        * A new text describing the VM integration interfaces of systemd has
+          been added:
+
+          https://systemd.io/VM_INTERFACE
+
+        * The sd_notify() man page has gained an example with C code that shows
+          how to implement the interface in C without involving libsystemd.
+
+        systemd-homed, systemd-logind, systemd-userdbd:
+
+        * systemd-homed now supports unlocking of home directories when logging
+          in via SSH. Previously home directories needed to be unlocked before
+          an SSH login is attempted.
+
+        * JSON User Records have been extended with a separate public storage
+          area called "User Record Blob Directories". This is intended to store
+          the user's background image, avatar picture, and other similar items
+          which are too large to fit into the User Record itself.
+
+          systemd-homed, userdbctl, and homectl gained support for blob
+          directories. homectl gained --avatar= and --login-background= to
+          control two specific items of the blob directories.
+
+        * A new "additionalLanguages" field has been added to JSON user records
+          (as supported by systemd-homed and systemd-userdbd), which is closely
+          related to the pre-existing "preferredLanguage", and allows
+          specifying multiple additional languages for the user account. It is
+          used to initialize the $LANGUAGES environment variable when used.
+
+        * A new pair of "preferredSessionType" and "preferredSessionLauncher"
+          fields have been added to JSON user records, that may be used to
+          control which kind of desktop session to preferable activate on
+          logins of the user.
+
+        * homectl gained a new verb 'firstboot', and a new
+          systemd-homed-firstboot.service unit uses this verb to create users
+          in a first boot environment, either from system credentials or by
+          querying interactively.
 
         * systemd-logind now supports a new "background-light" session class
           which does not pull in the user@.service unit. This is intended in
-          particular for cron jobs.
+          particular for lighter weight per-user cron jobs which do require any
+          per-user service manager to be around.
 
-          systemd-logind now also supports a new "user-incomplete" session
-          class for a user session that does not have a running user manager,
-          but may be upgraded to a full "user" session later on. This has
-          been hooked into the PAM stack to appropriately classify sessions
-          while they are being started.
+        * The per-user service manager will now be tracked as a distinct "manager"
+          session type among logind sessions of each user.
 
-          systemd-logind gained a new org.freedesktop.login1.Session.SetClass()
-          method to change the session class.
-
-          systemd-logind will not allow background, background-light, manager,
-          and manager-early session types to take control of devices or change
-          the session type.
+        * homectl now supports an --offline mode, by which certain account
+          properties can be changed without unlocking the home directory.
 
         * systemd-logind gained a new
           org.freedesktop.login1.Manager.ListSessionsEx() method that provides
           additional metadata compared to ListSessions(). loginctl makes use of
           this to list additional fields in list-sessions.
 
-        * systemd-cryptenroll can now enroll directly with a PKCS11 public key
-          (instead of a certificate).
+        * systemd-logind gained a new org.freedesktop.login1.Manager.Sleep()
+          method that automatically redirects to SuspendThenHibernate(),
+          Suspend(), HybridSleep(), or Hibernate(), depending on what is
+          supported and configured, a new configuration setting SleepOperation=,
+          and an accompanying helper method
+          org.freedesktop.login1.Manager.CanSleep() and property
+          org.freedesktop.login1.Manager.SleepOperation.
 
-        * Core dumps are now retained for two weeks by default.
+          'systemctl sleep' calls the new method to automatically put the
+          machine to sleep in the most appropriate way.
 
-        * systemd-cryptsetup gained support for crypttab option
-          link-volume-key= to enter the volume key into the kernel keyring when
-          the volume is opened.
+        Credential Management:
+
+        * systemd-creds now provides a Varlink IPC API for encrypting and
+          decrypting credentials.
+
+        * systemd-creds' "tpm2-absent" key selection has been renamed to
+          "null", since that's what it actually does: "encrypt" and "sign"
+          with a fixed null key. --with-key=null should only be used in very
+          specific cases, as it provides zero integrity or confidentiality
+          protections. (i.e. it's only safe to use as fallback in environments
+          lacking both a TPM and access to the root fs to use the host
+          encryption key, or when integrity is provided some other way.)
+
+        * systemd-creds gained a new switch --allow-null. If specified, the
+          "decrypt" verb will decode encrypted credentials that use the "null"
+          key (by default this is refused, since using the "null" key defeats
+          the authenticated encryption normally done).
+
+        Suspend & Hibernate:
+
+        * The sleep.conf configuration file gained a new MemorySleepMode=
+          setting for configuring the sleep mode in more detail.
+
+        * A tiny new service systemd-hibernate-clear.service has been added
+          which clears hibernation information from the HibernateLocation EFI
+          variable, in case the resume device is gone. Normally, this variable
+          is supposed to be cleaned up by the code that initiates the resume
+          from hibernation image. But when the device is missing and that code
+          doesn't run, this service will now do the necessary work, ensuring
+          that no outdated hibernation image information remains on subsequent
+          boots.
+
+        Unprivileged User Namespaces & Mounts:
+
+        * A small new service systemd-nsresourced.service has been added. It
+          provides a Varlink IPC API that assigns a free, transiently allocated
+          64K UID/GID range to an uninitialized user namespace a client
+          provides. It may be used to implement unprivileged container managers
+          and other programs that need dynamic user ID ranges. It also provides
+          interfaces to then delegate mount file descriptors, control groups
+          and network interfaces to user namespaces set up this way.
+
+        * A small new service systemd-mountfsd.service has been added. It
+          provides a Varlink IPC API for mounting DDI images, and returning a set
+          of mount file descriptors for it. If a user namespace fd is provided
+          as input, then the mounts are registered with the user namespace. To
+          ensure trust in the image it must provide Verity information (or
+          alternatively interactive polkit authentication is required).
+
+        * The systemd-dissect tool now can access DDIs fully unprivileged by
+          using systemd-nsresourced/systemd-mountfsd.
+
+        * If the service manager runs unprivileged (i.e. systemd --user) it now
+          supports RootImage= for accessing DDI images, also implemented via
+          the systemd-nsresourced/systemd-mountfsd.
+
+        * systemd-nspawn may now operate without privileges, if a suitable DDI
+          is provided via --image=, again implemented via
+          systemd-nsresourced/systemd-mountfsd.
+
+        Other:
+
+        * timedatectl and machinectl gained option '-P', an alias for
+          '--value --property=…'.
+
+        * Various tools that pretty-print config files will now highlight
+          configuration directives.
+
+        * varlinkctl gained support for the "ssh:" transport. This requires
+          OpenSSH 9.4 or newer.
+
+        * systemd-sysext gained support for enabling system extensions in
+          mutable fashion, where a writeable upperdir is stored under
+          /var/lib/extensions.mutable/, and a new --mutable= option to
+          configure this behaviour. An "ephemeral" mode is not also supported
+          where the mutable layer is configured to be a tmpfs that is
+          automatically released when the system extensions are reattached.
+
+        * Coredumps are now retained for two weeks by default (instead of three
+          days, as before).
 
         * portablectl --copy= parameter gained a new 'mixed' argument, that will
           result in resources owned by the OS (e.g.: portable profiles) to be linked
           but resources owned by the portable image (e.g.: the unit files and the
           images themselves) to be copied.
 
-        * The remaining documentation that was on
-          https://freedesktop.org/wiki/Software/systemd/ has been moved to
-          https://systemd.io.
+        * systemd will now register MIME types for various of its file types
+          (e.g. journal files, DDIs, encrypted credentials …) via the XDG
+          shared-mime-info infrastructure. (Files of these types will thus be
+          recognized as their own thing in desktop file managers such as GNOME
+          Files.)
 
+        * systemd-dissect will now show the detected sector size of a given DDI
+          in its default output.
+
+        * systemd-portabled now generates recognizable structured log messages
+          whenever a portable service is attached or detached.
+
+        * Verity signature checking in userspace (i.e. checking against
+          /etc/verity.d/ keys) when activating DDIs can now be turned on/off
+          via a kernel command line option systemd.allow_userspace_verity= and
+          an environment variable SYSTEMD_ALLOW_USERSPACE_VERITY=.
+
+        * ext4/xfs file system quota handling has been reworked, so that
+          quotacheck and quotaon are now invoked as per-file-system templated
+          services (as opposed to single system-wide singletons), similar in
+          style to the fsck, growfs, pcrfs logic. This means file systems with
+          quota enabled can now be reasonably enabled at runtime of the system,
+          not just at boot.
+
+        * "systemd-analyze dot" will now also show BindsTo= dependencies.
+
+        * systemd-debug-generator gained the ability add in arbitrary units
+          based on them being passed in via system credentials.
+
+        * A new kernel command-line option systemd.default_debug_tty= can be
+          used to specify the TTY for the debug shell, independently of
+          enabling or disabling it.
 
 CHANGES WITH 255:
 
index c4522a2bac635b07cc790dda5c5eea58588aa717..12f1381f08d5c3a0aeb08fdbb5e838215a13012e 100644 (file)
--- a/README.md
+++ b/README.md
@@ -37,3 +37,5 @@ When preparing patches for systemd, please follow our [Coding Style Guidelines](
 If you are looking for support, please contact our [mailing list](https://lists.freedesktop.org/mailman/listinfo/systemd-devel), join our [IRC channel #systemd on libera.chat](https://web.libera.chat/#systemd) or [Matrix channel](https://matrix.to/#/#systemd-project:matrix.org)
 
 Stable branches with backported patches are available in the [stable repo](https://github.com/systemd/systemd-stable).
+
+We have a security bug bounty program sponsored by the [Sovereign Tech Fund](https://www.sovereigntechfund.de/) hosted on [YesWeHack](https://yeswehack.com/programs/systemd-bug-bounty-program)
diff --git a/TODO b/TODO
index b5a2c1f30a4302612dc5e812741046bdf3d4f039..1c77f9ffc5c1db32df0d783fb8a2a8e312a1330c 100644 (file)
--- a/TODO
+++ b/TODO
@@ -130,6 +130,8 @@ Deprecations and removals:
 
 Features:
 
+* systemd-nspawn should get the same SSH key support that vmspawn now has.
+
 * insert the new pidfs inode number as a third field into PidRef, so that
   PidRef are reasonably serializable without having to pass around fds.
 
@@ -268,6 +270,18 @@ Features:
 * use udev rule networkd ownership property to take ownership of network
   interfaces nspawn creates
 
+* mountfsd/nsresourced
+  - userdb: maybe allow callers to map one uid to their own uid
+  - bpflsm: allow writes if resulting UID on disk would be userns' owner UID
+  - make encrypted DDIs work (password…)
+  - add API for creating a new file system from scratch (together with some
+    dm-integrity/HMAC key). Should probably work using systemd-repart (access
+    via varlink).
+  - add api to make an existing file "trusted" via dm-integry/HMAC key
+  - port: portabled
+  - port: tmpfiles, sysusers and similar
+  - lets see if we can make runtime bind mounts into unpriv nspawn work
+
 * add a kernel cmdline switch (and cred?) for marking a system to be
   "headless", in which case we never open /dev/console for reading, only for
   writing. This would then mean: systemd-firstboot would process creds but not
@@ -1436,11 +1450,8 @@ Features:
 
 * make us use dynamically fewer deps for containers in general purpose distros:
   o turn into dlopen() deps:
-    - kmod-libs (only when called from PID 1)
     - libblkid (only in RootImage= handling in PID 1, but not elsewhere)
     - libpam (only when called from PID 1)
-    - bzip2 (always — gzip should probably stay static dep the way it is,
-      since it's so basic and our defaults)
 
 * seccomp: maybe use seccomp_merge() to merge our filters per-arch if we can.
   Apparently kernel performance is much better with fewer larger seccomp
index b4e88c99df257634fa77a38eb6afad4649cf6222..8f687e66623ef4e6addc52683a6e2d324272282c 100644 (file)
@@ -780,3 +780,13 @@ SPDX-License-Identifier: LGPL-2.1-or-later
   good idea where it might end up running inside of libsystemd.so or
   similar. Hence, use TLS (i.e. `thread_local`) where appropriate, and maybe
   the occasional `pthread_once()`.
+
+## Tests
+
+- Use the assertion macros from `tests.h` (`ASSERT_GE()`, `ASSERT_OK()`, ...) to
+  make sure a descriptive error is logged when an assertion fails. If no assertion
+  macro exists for your specific use case, please add a new assertion macro in a
+  separate commit.
+
+- When modifying existing tests, please convert the test to use the new assertion
+  macros from `tests.h` if it is not already using those.
index 449e284a91827294c5ad3ac7f9aa530d15dffc5e..3585ab012359787123529fd476d85eb235cbd90c 100644 (file)
@@ -676,3 +676,21 @@ Tools using the Varlink protocol (such as `varlinkctl`) or sd-bus (such as
   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.
+
+`systemd-mountfsd`:
+
+* `$SYSTEMD_MOUNTFSD_TRUSTED_DIRECTORIES` – takes a boolean argument. If true
+  disk images from the usual disk image directories (`/var/lib/machines/`,
+  `/var/lib/confexts/`, …) will be considered "trusted", i.e. are validated
+  with a more relaxed image policy (typically not requiring Verity signature
+  checking) than those from other directories (where Verity signature checks
+  are mandatory). If false all images are treated the same, regardless if
+  placed in the usual disk image directories or elsewhere. If not set defaults
+  to a compile time setting.
+
+* `$SYSTEMD_MOUNTFSD_IMAGE_POLICY_TRUSTED`,
+  `$SYSTEMD_MOUNTFSD_IMAGE_POLICY_UNTRUSTED` – the default image policy to
+  apply to trusted and untrusted disk images. An image is considered trusted if
+  placed in a trusted disk image directory (see above), or if suitable polkit
+  authentication was acquired. See `systemd.image-policy(7)` for the valid
+  syntax for image policy strings.
index c1134f38b4ca69881a1e29fef1902f1dc9b02b87..82b64349847133e3bf6e9628ddc4a6c56645c881 100644 (file)
@@ -22,7 +22,13 @@ If adding a new source file, consider adding a matching test executable.
 For features at a higher level, tests in `src/test/` are very strongly recommended.
 If that is not possible, integration tests in `test/` are encouraged.
 
-Please also have a look at our list of [code quality tools](CODE_QUALITY) we have setup for systemd, to ensure our codebase stays in good shape.
+```shell
+$ git config submodule.recurse true
+$ git config fetch.recurseSubmodules on-demand
+$ git config push.recurseSubmodules no
+$ cp .git/hooks/pre-commit.sample .git/hooks/pre-commit
+$ cp tools/git-post-rewrite-hook.sh .git/hooks/post-rewrite
+```
 
 Please always test your work before submitting a PR.
 For many of the components of systemd testing is straightforward as you can simply compile systemd and run the relevant tool from the build directory.
@@ -66,6 +72,22 @@ To boot in UEFI mode instead of using QEMU's direct kernel boot, add the followi
 QemuFirmware=uefi
 ```
 
+To avoid having to build a new image all the time when iterating on a patch,
+add the following to `mkosi.local.conf`:
+
+```conf
+[Host]
+RuntimeBuildSources=yes
+```
+
+After enabling this setting, the source and build directories will be mounted to
+`/work/src` and `/work/build` respectively when booting the image as a container
+or virtual machine. To build the latest changes and re-install, run
+`meson install -C /work/build --only-changed` in the container or virtual machine
+and optionally restart the daemon(s) you're working on using
+`systemctl restart <units>` or `systemctl daemon-reexec` if you're working on pid1
+or `systemctl soft-reboot` to restart everything.
+
 Putting this all together, here's a series of commands for preparing a patch for systemd:
 
 ```sh
@@ -188,7 +210,8 @@ done
 ./infra/helper.py coverage --no-corpus-download systemd
 ```
 
-If you find a bug that impacts the security of systemd, please follow the guidance in [CONTRIBUTING.md](CONTRIBUTING) on how to report a security vulnerability.
+If you find a bug that impacts the security of systemd,
+please follow the guidance in [CONTRIBUTING.md](CONTRIBUTING) on how to report a security vulnerability.
 
 For more details on building fuzzers and integrating with OSS-Fuzz, visit:
 
@@ -221,21 +244,9 @@ please refer to the official vscode documentation [here](https://code.visualstud
 
 To simplify debugging systemd when testing changes using mkosi, we're going to show how to attach [VSCode](https://code.visualstudio.com/)'s debugger to an instance of systemd running in a mkosi image using QEMU.
 
-To allow VSCode's debugger to attach to systemd running in a mkosi image, we have to make sure it can access the virtual machine spawned by mkosi where systemd is running.
-mkosi makes this possible via a handy SSH option that makes the generated image accessible via SSH when booted.
-Thus you must build the image with `mkosi --ssh`.
-The easiest way to set the option is to create a file `mkosi.local.conf` in the root of the repository and add the following contents:
-
-```conf
-[Host]
-Ssh=yes
-RuntimeTrees=.
-```
-
-Also make sure that the SSH agent is running on your system and that you've added your SSH key to it with `ssh-add`.
-Also make sure that `virtiofsd` is installed.
-
-After rebuilding the image and booting it with `mkosi qemu`,
+To allow VSCode's debugger to attach to systemd running in a mkosi image,
+we have to make sure it can access the virtual machine spawned by mkosi where systemd is running.
+After booting the image with `mkosi qemu`,
 you should now be able to connect to it by running `mkosi ssh` from the same directory in another terminal window.
 
 Now we need to configure VSCode.
@@ -266,16 +277,12 @@ Create the file, and possibly the directory, and add the following contents:
             "name": "systemd",
             "pipeTransport": {
                 "pipeProgram": "mkosi",
-                "pipeArgs": [
-                    "-C",
-                    "/path/to/systemd/repo/directory/on/host/system/",
-                    "ssh"
-                ],
+                "pipeArgs": ["-C", "${workspaceFolder}", "ssh"],
                 "debuggerPath": "/usr/bin/gdb"
             },
             "MIMode": "gdb",
             "sourceFileMap": {
-                "/root/src/systemd": {
+                "/work/src": {
                     "editorPath": "${workspaceFolder}",
                     "useForBreakpoints": false
                 },
index 890d376ce372c2fbc8570fbcb457b0ba70d8e87b..441c77ccfb15247b9ac80ee146509d160b633462 100644 (file)
@@ -20,7 +20,11 @@ The stable interfaces are:
   Note however that the output generated by these commands is generally not included in the promise, unless it is documented in the man page.
   Example: the output of `systemctl status` is not stable, but that of `systemctl show` is, because the former is intended to be human readable and the latter computer readable, and this is documented in the man page.
 
-* **The protocol spoken on the socket referred to by `$NOTIFY_SOCKET`**, as documented in [sd_notify(3)](https://www.freedesktop.org/software/systemd/man/sd_notify.html).
+* **The protocol spoken on the socket referred to by `$NOTIFY_SOCKET`**, as documented in
+  [sd_notify(3)](https://www.freedesktop.org/software/systemd/man/sd_notify.html). Note that, although using
+  libsystemd is a good choice, this protocol can also be reimplemented without external dependencies, as
+  demonstrated in the example listed in
+  [sd_notify(3)](https://www.freedesktop.org/software/systemd/man/devel/sd_notify.html#Notes)
 
 * Some of the **"special" unit names** and their semantics.
   To be precise the ones that are necessary for normal services, and not those required only for early boot and late shutdown, with very few exceptions.
index c603f9ca2cc4bca2bb06df22f55e9d1f8eff6861..a4431e239e931ccfc5ea6ebf72162b243dd5a77d 100644 (file)
@@ -498,9 +498,11 @@ evdev:input:b0003v256Cp0068*
  EVDEV_ABS_01=::200
 
 # HUION Kamvas 19 Pro (GT-1902)
-evdev:input:b0003v256Cp006Be0110*
- EVDEV_ABS_00=::68
- EVDEV_ABS_01=::123
+evdev:input:b0003v256Cp006B*
+ EVDEV_ABS_00=::40
+ EVDEV_ABS_01=::42
+ EVDEV_ABS_35=::40
+ EVDEV_ABS_36=::42
 
 #########################################
 # Lenovo
index 75f7b2647936987d98ec1e4475f73afcd21a2428..e54d6543d78b3d21eee5160ccf4afb178d884232 100644 (file)
@@ -313,7 +313,7 @@ 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:*
+sensor:modalias:acpi:MXC6655*:dmi:*:svnCHUWIInnovationAndTechnology*:pnUBookX:*
  ACCEL_MOUNT_MATRIX=0, -1, 0; -1, 0, 0; 0, 0, -1
 
 #########################################
index 8cb9a4bfa590344b527ba6ae401de3d90f355acd..27327ee1928ca76f51bcf66fa946b50c44400be9 100644 (file)
       </varlistentry>
 
       <varlistentry>
-        <term><option>--json=</option><replaceable>MODE</replaceable></term>
+        <term><option>--json=<replaceable>MODE</replaceable></option></term>
 
         <listitem>
           <para>When used with the <command>call</command> or <command>get-property</command> command, shows output
       </varlistentry>
 
       <varlistentry>
-        <term><option>--expect-reply=</option><replaceable>BOOL</replaceable></term>
+        <term><option>--expect-reply=<replaceable>BOOL</replaceable></option></term>
 
         <listitem>
           <para>When used with the <command>call</command> command,
       </varlistentry>
 
       <varlistentry>
-        <term><option>--auto-start=</option><replaceable>BOOL</replaceable></term>
+        <term><option>--auto-start=<replaceable>BOOL</replaceable></option></term>
 
         <listitem>
           <para>When used with the <command>call</command> or <command>emit</command> command, specifies
       </varlistentry>
 
       <varlistentry>
-        <term><option>--allow-interactive-authorization=</option><replaceable>BOOL</replaceable></term>
+        <term><option>--allow-interactive-authorization=<replaceable>BOOL</replaceable></option></term>
 
         <listitem>
           <para>When used with the <command>call</command> command,
       </varlistentry>
 
       <varlistentry>
-        <term><option>--timeout=</option><replaceable>SECS</replaceable></term>
+        <term><option>--timeout=<replaceable>SECS</replaceable></option></term>
 
         <listitem>
           <para>When used with the <command>call</command> command,
       </varlistentry>
 
       <varlistentry>
-        <term><option>--augment-creds=</option><replaceable>BOOL</replaceable></term>
+        <term><option>--augment-creds=<replaceable>BOOL</replaceable></option></term>
 
         <listitem>
           <para>Controls whether credential data reported by
       </varlistentry>
 
       <varlistentry>
-        <term><option>--watch-bind=</option><replaceable>BOOL</replaceable></term>
+        <term><option>--watch-bind=<replaceable>BOOL</replaceable></option></term>
 
         <listitem>
           <para>Controls whether to wait for the specified <constant>AF_UNIX</constant> bus socket to appear in the
       </varlistentry>
 
       <varlistentry>
-        <term><option>--destination=</option><replaceable>SERVICE</replaceable></term>
+        <term><option>--destination=<replaceable>SERVICE</replaceable></option></term>
 
         <listitem>
           <para>Takes a service name. When used with the <command>emit</command> command, a signal is
index 9e356a0da6b8f7685d17442021ab61e98ecf9413..6179d6b760d95e69d6ed4e95641dcdd07d00227e 100644 (file)
 
       <varlistentry>
         <term><option>-F</option> <replaceable>FIELD</replaceable></term>
-        <term><option>--field=</option><replaceable>FIELD</replaceable></term>
+        <term><option>--field=<replaceable>FIELD</replaceable></option></term>
 
         <listitem><para>Print all possible data values the specified
         field takes in matching core dump entries of the
 
       <varlistentry>
         <term><option>-o</option> <replaceable>FILE</replaceable></term>
-        <term><option>--output=</option><replaceable>FILE</replaceable></term>
+        <term><option>--output=<replaceable>FILE</replaceable></option></term>
 
         <listitem><para>Write the core to <option>FILE</option>.
         </para>
       </varlistentry>
 
       <varlistentry>
-        <term><option>--debugger=</option><replaceable>DEBUGGER</replaceable></term>
+        <term><option>--debugger=<replaceable>DEBUGGER</replaceable></option></term>
 
         <listitem><para>Use the given debugger for the <command>debug</command>
         command. If not given and <varname>$SYSTEMD_DEBUGGER</varname> is unset, then
 
       <varlistentry>
         <term><option>-A</option> <replaceable>ARGS</replaceable></term>
-        <term><option>--debugger-arguments=</option><replaceable>ARGS</replaceable></term>
+        <term><option>--debugger-arguments=<replaceable>ARGS</replaceable></option></term>
 
         <listitem><para>Pass the given <replaceable>ARGS</replaceable> as extra command line arguments
         to the debugger. Quote as appropriate when <replaceable>ARGS</replaceable> contain whitespace.
 
       <varlistentry>
         <term><option>-D</option> <replaceable>DIR</replaceable></term>
-        <term><option>--directory=</option><replaceable>DIR</replaceable></term>
+        <term><option>--directory=<replaceable>DIR</replaceable></option></term>
 
         <listitem><para>Use the journal files in the specified <option>DIR</option>.
         </para>
index a854d11f8586a3ac1e8bd0987b7399ed1063ef61..c82e2b8d8f6ece720cb3534b0fbc6420bce21689 100644 (file)
@@ -18,5 +18,4 @@
 <!ENTITY DEFAULT_TIMEOUT "{{DEFAULT_TIMEOUT_SEC}} s">
 <!ENTITY DEFAULT_USER_TIMEOUT "{{DEFAULT_USER_TIMEOUT_SEC}} s">
 <!ENTITY DEFAULT_KEYMAP "{{SYSTEMD_DEFAULT_KEYMAP}}">
-<!ENTITY fedora_latest_version "38">
-<!ENTITY fedora_cloud_release "1.6">
+<!ENTITY fedora_latest_version "40">
index 8b21e15fdae6f764b69b33168f2ce880f1aff643..e5551fc798a2e0c73113df26a85e8de054122f18 100644 (file)
   </a>
 </xsl:template>
 
-<xsl:template match="citerefentry[@project='wireguard']">
-  <a>
-    <xsl:attribute name="href">
-      <xsl:text>https://git.zx2c4.com/WireGuard/about/src/tools/</xsl:text>
-      <xsl:value-of select="refentrytitle"/>
-      <xsl:text>.</xsl:text>
-      <xsl:value-of select="manvolnum"/>
-    </xsl:attribute>
-    <xsl:call-template name="inline.charseq"/>
-  </a>
-</xsl:template>
-
 <xsl:template match="citerefentry[@project='mankier']">
   <a>
     <xsl:attribute name="href">
 <xsl:template match="citerefentry[@project='archlinux']">
   <a>
     <xsl:attribute name="href">
-      <xsl:text>https://www.archlinux.org/</xsl:text>
-      <xsl:value-of select="refentrytitle"/>
-      <xsl:text>/</xsl:text>
+      <xsl:text>https://man.archlinux.org/man/</xsl:text>
       <xsl:value-of select="refentrytitle"/>
       <xsl:text>.</xsl:text>
       <xsl:value-of select="manvolnum"/>
-      <xsl:text>.html</xsl:text>
+      <xsl:text>.en.html</xsl:text>
     </xsl:attribute>
     <xsl:call-template name="inline.charseq"/>
   </a>
index 8195efbaa5e3334448e756cb95542bfa20d288e7..828f0cd6f4b5737f2e1ffd22860cb22d8e6c7a11 100644 (file)
@@ -1,9 +1,10 @@
 /* SPDX-License-Identifier: MIT-0 */
 
+#define _GNU_SOURCE 1
 #include <assert.h>
 #include <stdio.h>
 #include <unistd.h>
-#include <sd-event.h>
+#include <systemd/sd-event.h>
 
 int main(int argc, char **argv) {
   pid_t pid = fork();
index f1bade205343244717b0a750679a29fa814bef1c..effdb27f72a565f055b7e776e54c1f176f769169 100644 (file)
     <variablelist>
 
       <varlistentry>
-        <term><option>--identity=</option><replaceable>FILE</replaceable></term>
+        <term><option>--identity=<replaceable>FILE</replaceable></option></term>
 
         <listitem><para>Read the user's JSON record from the specified file. If passed as
         <literal>-</literal> read the user record from standard input. The supplied JSON object must follow
       </varlistentry>
 
       <varlistentry>
-        <term><option>--json=</option><replaceable>FORMAT</replaceable></term>
+        <term><option>--json=<replaceable>FORMAT</replaceable></option></term>
         <term><option>-j</option></term>
 
         <listitem><para>Controls whether to output the user record in JSON format, if the
       </varlistentry>
 
       <varlistentry>
-        <term><option>--export-format=</option><replaceable>FORMAT</replaceable></term>
+        <term><option>--export-format=<replaceable>FORMAT</replaceable></option></term>
         <term><option>-E</option></term>
         <term><option>-EE</option></term>
 
     <variablelist>
 
       <varlistentry>
-        <term><option>--real-name=</option><replaceable>NAME</replaceable></term>
+        <term><option>--real-name=<replaceable>NAME</replaceable></option></term>
         <term><option>-c</option> <replaceable>NAME</replaceable></term>
 
         <listitem><para>The real name for the user. This corresponds with the GECOS field on classic UNIX NSS
       </varlistentry>
 
       <varlistentry>
-        <term><option>--realm=</option><replaceable>REALM</replaceable></term>
+        <term><option>--realm=<replaceable>REALM</replaceable></option></term>
 
         <listitem><para>The realm for the user. The realm associates a user with a specific organization or
         installation, and allows distinguishing users of the same name defined in different contexts. The
       </varlistentry>
 
       <varlistentry>
-        <term><option>--email-address=</option><replaceable>EMAIL</replaceable></term>
+        <term><option>--email-address=<replaceable>EMAIL</replaceable></option></term>
 
         <listitem><para>Takes an electronic mail address to associate with the user. On log-in the
         <varname>$EMAIL</varname> environment variable is initialized from this value.</para>
       </varlistentry>
 
       <varlistentry>
-        <term><option>--location=</option><replaceable>TEXT</replaceable></term>
+        <term><option>--location=<replaceable>TEXT</replaceable></option></term>
 
         <listitem><para>Takes location specification for this user. This is free-form text, which might or
         might not be usable by geo-location applications. Example: <option>--location="Berlin,
       </varlistentry>
 
       <varlistentry>
-        <term><option>--icon-name=</option><replaceable>ICON</replaceable></term>
+        <term><option>--icon-name=<replaceable>ICON</replaceable></option></term>
 
         <listitem><para>Takes an icon name to associate with the user, following the scheme defined by the <ulink
         url="https://standards.freedesktop.org/icon-naming-spec/icon-naming-spec-latest.html">Icon Naming
       </varlistentry>
 
       <varlistentry>
-        <term><option>--home-dir=</option><replaceable>PATH</replaceable></term>
-        <term><option>-d</option><replaceable>PATH</replaceable></term>
+        <term><option>--home-dir=<replaceable>PATH</replaceable></option></term>
+        <term><option>-d<replaceable>PATH</replaceable></option></term>
 
         <listitem><para>Takes a path to use as home directory for the user. Note that this is the directory
         the user's home directory is mounted to while the user is logged in. This is not where the user's
       </varlistentry>
 
       <varlistentry>
-        <term><option>--uid=</option><replaceable>UID</replaceable></term>
+        <term><option>--uid=<replaceable>UID</replaceable></option></term>
 
         <listitem><para>Takes a preferred numeric UNIX UID to assign this user. If a user is to be created
         with the specified UID and it is already taken by a different user on the local system then creation
       </varlistentry>
 
       <varlistentry>
-        <term><option>--member-of=</option><replaceable>GROUP</replaceable></term>
+        <term><option>--member-of=<replaceable>GROUP</replaceable></option></term>
         <term><option>-G</option> <replaceable>GROUP</replaceable></term>
 
         <listitem><para>Takes a comma-separated list of auxiliary UNIX groups this user shall belong
       </varlistentry>
 
       <varlistentry>
-        <term><option>--capability-bounding-set=</option><replaceable>CAPABILITIES</replaceable></term>
-        <term><option>--capability-ambient-set=</option><replaceable>CAPABILITIES</replaceable></term>
+        <term><option>--capability-bounding-set=<replaceable>CAPABILITIES</replaceable></option></term>
+        <term><option>--capability-ambient-set=<replaceable>CAPABILITIES</replaceable></option></term>
 
         <listitem><para>These options take a space separated list of process capabilities
         (e.g. <constant>CAP_WAKE_ALARM</constant>, <constant>CAP_BLOCK_SUSPEND</constant>, …) that shall be
       </varlistentry>
 
       <varlistentry>
-        <term><option>--skel=</option><replaceable>PATH</replaceable></term>
+        <term><option>--skel=<replaceable>PATH</replaceable></option></term>
 
         <listitem><para>Takes a file system path to a directory. Specifies the skeleton directory to
         initialize the home directory with. All files and directories in the specified path are copied into
       </varlistentry>
 
       <varlistentry>
-        <term><option>--shell=</option><replaceable>SHELL</replaceable></term>
+        <term><option>--shell=<replaceable>SHELL</replaceable></option></term>
 
         <listitem><para>Takes a file system path. Specifies the shell binary to execute on terminal
         logins. If not specified defaults to <filename>/bin/bash</filename>.</para>
       </varlistentry>
 
       <varlistentry>
-        <term><option>--setenv=</option><replaceable>VARIABLE</replaceable>[=<replaceable>VALUE</replaceable>]</term>
+        <term><option>--setenv=<replaceable>VARIABLE</replaceable>[=<replaceable>VALUE</replaceable>]</option></term>
 
         <listitem><para>Takes an environment variable assignment to set for all user processes. May be used
         multiple times to set multiple environment variables. When <literal>=</literal> and
       </varlistentry>
 
       <varlistentry>
-        <term><option>--timezone=</option><replaceable>TIMEZONE</replaceable></term>
+        <term><option>--timezone=<replaceable>TIMEZONE</replaceable></option></term>
 
         <listitem><para>Takes a time zone location name that sets the timezone for the specified user. When
         the user logs in the <varname>$TZ</varname> environment variable is initialized from this
       </varlistentry>
 
       <varlistentry>
-        <term><option>--language=</option><replaceable>LANG</replaceable></term>
+        <term><option>--language=<replaceable>LANG</replaceable></option></term>
 
         <listitem><para>Takes a comma- or colon-separated list of languages preferred by the user, ordered
         by descending priority. The <varname>$LANG</varname> and <varname>$LANGUAGE</varname> environment
       </varlistentry>
 
       <varlistentry>
-        <term><option>--ssh-authorized-keys=</option><replaceable>KEYS</replaceable></term>
+        <term><option>--ssh-authorized-keys=<replaceable>KEYS</replaceable></option></term>
         <listitem><para>Either takes a SSH authorized key line to associate with the user record or a
         <literal>@</literal> character followed by a path to a file to read one or more such lines from. SSH
         keys configured this way are made available to SSH to permit access to this home directory and user
       </varlistentry>
 
       <varlistentry>
-        <term><option>--pkcs11-token-uri=</option><replaceable>URI</replaceable></term>
+        <term><option>--pkcs11-token-uri=<replaceable>URI</replaceable></option></term>
         <listitem><para>Takes an RFC 7512 PKCS#11 URI referencing a security token (e.g. YubiKey or PIV
         smartcard) that shall be able to unlock the user account. The security token URI should reference a
         security token with exactly one pair of X.509 certificate and private key. A random secret key is
       </varlistentry>
 
       <varlistentry>
-        <term><option>--fido2-credential-algorithm=</option><replaceable>STRING</replaceable></term>
+        <term><option>--fido2-credential-algorithm=<replaceable>STRING</replaceable></option></term>
         <listitem><para>Specify COSE algorithm used in credential generation. The default value is
         <literal>es256</literal>. Supported values are <literal>es256</literal>, <literal>rs256</literal>
         and <literal>eddsa</literal>.</para>
       </varlistentry>
 
       <varlistentry>
-        <term><option>--fido2-device=</option><replaceable>PATH</replaceable></term>
+        <term><option>--fido2-device=<replaceable>PATH</replaceable></option></term>
 
         <listitem><para>Takes a path to a Linux <literal>hidraw</literal> device
         (e.g. <filename>/dev/hidraw1</filename>), referring to a FIDO2 security token implementing the
       </varlistentry>
 
       <varlistentry>
-        <term><option>--fido2-with-client-pin=</option><replaceable>BOOL</replaceable></term>
+        <term><option>--fido2-with-client-pin=<replaceable>BOOL</replaceable></option></term>
 
         <listitem><para>When enrolling a FIDO2 security token, controls whether to require the user to enter
         a PIN when unlocking the account (the FIDO2 <literal>clientPin</literal> feature). Defaults to
       </varlistentry>
 
       <varlistentry>
-        <term><option>--fido2-with-user-presence=</option><replaceable>BOOL</replaceable></term>
+        <term><option>--fido2-with-user-presence=<replaceable>BOOL</replaceable></option></term>
 
         <listitem><para>When enrolling a FIDO2 security token, controls whether to require the user to
         verify presence (tap the token, the FIDO2 <literal>up</literal> feature) when unlocking the account.
       </varlistentry>
 
       <varlistentry>
-        <term><option>--fido2-with-user-verification=</option><replaceable>BOOL</replaceable></term>
+        <term><option>--fido2-with-user-verification=<replaceable>BOOL</replaceable></option></term>
 
         <listitem><para>When enrolling a FIDO2 security token, controls whether to require user verification
         when unlocking the account (the FIDO2 <literal>uv</literal> feature). Defaults to
       </varlistentry>
 
       <varlistentry>
-        <term><option>--recovery-key=</option><replaceable>BOOL</replaceable></term>
+        <term><option>--recovery-key=<replaceable>BOOL</replaceable></option></term>
 
         <listitem><para>Accepts a boolean argument. If enabled a recovery key is configured for the
         account. A recovery key is a computer generated access key that may be used to regain access to an
       </varlistentry>
 
     <varlistentry>
-        <term><option>--blob=</option><replaceable>PATH</replaceable></term>
+        <term><option>--blob=<replaceable>PATH</replaceable></option></term>
         <term><option>-b</option> <replaceable>PATH</replaceable></term>
-        <term><option>--blob=</option><replaceable>FILENAME</replaceable>=<replaceable>PATH</replaceable></term>
+        <term><option>--blob=<replaceable>FILENAME</replaceable>=<replaceable>PATH</replaceable></option></term>
         <term><option>-b</option> <replaceable>FILENAME</replaceable>=<replaceable>PATH</replaceable></term>
 
         <listitem><para>Accepts either a directory path, or a file name followed by a file path. If just a
       </varlistentry>
 
     <varlistentry>
-        <term><option>--avatar=</option><replaceable>PATH</replaceable></term>
-        <term><option>--login-background=</option><replaceable>PATH</replaceable></term>
+        <term><option>--avatar=<replaceable>PATH</replaceable></option></term>
+        <term><option>--login-background=<replaceable>PATH</replaceable></option></term>
 
         <listitem><para>Accept a file path. If set, the specified file is used to overwrite the
         corresponding file in the user's blob directory. If blank, the corresponding file is deleted
         from the blob directory. Essentially, these options are shortcuts to
-        <option>--blob=</option><replaceable>FILENAME</replaceable>=<replaceable>PATH</replaceable>
+        <option>--blob=<replaceable>FILENAME</replaceable>=<replaceable>PATH</replaceable></option>
         for the known filenames defined in
         <ulink url="https://systemd.io/USER_RECORD_BLOB_DIRS">User Record Blob Directories</ulink>.</para>
 
       </varlistentry>
 
       <varlistentry>
-        <term><option>--locked=</option><replaceable>BOOLEAN</replaceable></term>
+        <term><option>--locked=<replaceable>BOOLEAN</replaceable></option></term>
 
         <listitem><para>Takes a boolean argument. Specifies whether this user account shall be locked. If
         true logins into this account are prohibited, if false (the default) they are permitted (of course,
       </varlistentry>
 
       <varlistentry>
-        <term><option>--not-before=</option><replaceable>TIMESTAMP</replaceable></term>
-        <term><option>--not-after=</option><replaceable>TIMESTAMP</replaceable></term>
+        <term><option>--not-before=<replaceable>TIMESTAMP</replaceable></option></term>
+        <term><option>--not-after=<replaceable>TIMESTAMP</replaceable></option></term>
 
         <listitem><para>These options take a timestamp string, in the format documented in
         <citerefentry><refentrytitle>systemd.time</refentrytitle><manvolnum>7</manvolnum></citerefentry> and
       </varlistentry>
 
       <varlistentry>
-        <term><option>--rate-limit-interval=</option><replaceable>SECS</replaceable></term>
-        <term><option>--rate-limit-burst=</option><replaceable>NUMBER</replaceable></term>
+        <term><option>--rate-limit-interval=<replaceable>SECS</replaceable></option></term>
+        <term><option>--rate-limit-burst=<replaceable>NUMBER</replaceable></option></term>
 
         <listitem><para>Configures a rate limit on authentication attempts for this user. If the user
         attempts to authenticate more often than the specified number, on a specific system, within the
       </varlistentry>
 
       <varlistentry>
-        <term><option>--password-hint=</option><replaceable>TEXT</replaceable></term>
+        <term><option>--password-hint=<replaceable>TEXT</replaceable></option></term>
 
         <listitem><para>Takes a password hint to store alongside the user record. This string is stored
         accessible only to privileged users and the user itself and may not be queried by other users.
       </varlistentry>
 
       <varlistentry>
-        <term><option>--enforce-password-policy=</option><replaceable>BOOL</replaceable></term>
+        <term><option>--enforce-password-policy=<replaceable>BOOL</replaceable></option></term>
         <term><option>-P</option></term>
 
         <listitem><para>Takes a boolean argument. Configures whether to enforce the system's password policy
       </varlistentry>
 
       <varlistentry>
-        <term><option>--password-change-now=</option><replaceable>BOOL</replaceable></term>
+        <term><option>--password-change-now=<replaceable>BOOL</replaceable></option></term>
 
         <listitem><para>Takes a boolean argument. If true the user is asked to change their password on next
         login.</para>
       </varlistentry>
 
       <varlistentry>
-        <term><option>--password-change-min=</option><replaceable>TIME</replaceable></term>
-        <term><option>--password-change-max=</option><replaceable>TIME</replaceable></term>
-        <term><option>--password-change-warn=</option><replaceable>TIME</replaceable></term>
-        <term><option>--password-change-inactive=</option><replaceable>TIME</replaceable></term>
+        <term><option>--password-change-min=<replaceable>TIME</replaceable></option></term>
+        <term><option>--password-change-max=<replaceable>TIME</replaceable></option></term>
+        <term><option>--password-change-warn=<replaceable>TIME</replaceable></option></term>
+        <term><option>--password-change-inactive=<replaceable>TIME</replaceable></option></term>
 
         <listitem><para>Each of these options takes a time span specification as argument (in the syntax
         documented in
       </varlistentry>
 
       <varlistentry>
-        <term><option>--disk-size=</option><replaceable>BYTES</replaceable></term>
+        <term><option>--disk-size=<replaceable>BYTES</replaceable></option></term>
         <listitem><para>Either takes a size in bytes as argument (possibly using the usual K, M, G, …
         suffixes for 1024 base values), a percentage value, or the special strings <literal>min</literal> or
         <literal>max</literal>, and configures the disk space to assign to the user. If a percentage value is
       </varlistentry>
 
       <varlistentry>
-        <term><option>--access-mode=</option><replaceable>MODE</replaceable></term>
+        <term><option>--access-mode=<replaceable>MODE</replaceable></option></term>
 
         <listitem><para>Takes a UNIX file access mode written in octal. Configures the access mode of the
         home directory itself. Note that this is only used when the directory is first created, and the user
       </varlistentry>
 
       <varlistentry>
-        <term><option>--umask=</option><replaceable>MASK</replaceable></term>
+        <term><option>--umask=<replaceable>MASK</replaceable></option></term>
 
         <listitem><para>Takes the access mode mask (in octal syntax) to apply to newly created files and
         directories of the user ("umask"). If set this controls the initial umask set for all login sessions of
       </varlistentry>
 
       <varlistentry>
-        <term><option>--nice=</option><replaceable>NICE</replaceable></term>
+        <term><option>--nice=<replaceable>NICE</replaceable></option></term>
 
         <listitem><para>Takes the numeric scheduling priority ("nice level") to apply to the processes of the user at login
         time. Takes a numeric value in the range -20 (highest priority) to 19 (lowest priority).</para>
       </varlistentry>
 
       <varlistentry>
-        <term><option>--rlimit=</option><replaceable>LIMIT</replaceable>=<replaceable>VALUE</replaceable><optional>:<replaceable>VALUE</replaceable></optional></term>
+        <term><option>--rlimit=<replaceable>LIMIT</replaceable>=<replaceable>VALUE</replaceable><optional>:<replaceable>VALUE</replaceable></optional></option></term>
 
         <listitem><para>Allows configuration of resource limits for processes of this user, see <citerefentry
         project='man-pages'><refentrytitle>getrlimit</refentrytitle><manvolnum>2</manvolnum></citerefentry>
       </varlistentry>
 
       <varlistentry>
-        <term><option>--tasks-max=</option><replaceable>TASKS</replaceable></term>
+        <term><option>--tasks-max=<replaceable>TASKS</replaceable></option></term>
 
         <listitem><para>Takes a non-zero unsigned integer as argument. Configures the maximum number of tasks
         (i.e. threads, where each process is at least one thread) the user may have at any given time. This
       </varlistentry>
 
       <varlistentry>
-        <term><option>--memory-high=</option><replaceable>BYTES</replaceable></term>
-        <term><option>--memory-max=</option><replaceable>BYTES</replaceable></term>
+        <term><option>--memory-high=<replaceable>BYTES</replaceable></option></term>
+        <term><option>--memory-max=<replaceable>BYTES</replaceable></option></term>
 
         <listitem><para>Set a limit on the memory a user may take up on a system at any given time in bytes
         (the usual K, M, G, … suffixes are supported, to the base of 1024). This includes all memory used by
       </varlistentry>
 
       <varlistentry>
-        <term><option>--cpu-weight=</option><replaceable>WEIGHT</replaceable></term>
-        <term><option>--io-weight=</option><replaceable>WEIGHT</replaceable></term>
+        <term><option>--cpu-weight=<replaceable>WEIGHT</replaceable></option></term>
+        <term><option>--io-weight=<replaceable>WEIGHT</replaceable></option></term>
 
         <listitem><para>Set CPU and IO scheduling weights of the processes of the user, including those of
         processes forked off by the user that changed user credentials. Takes a numeric value in the range
       </varlistentry>
 
       <varlistentry>
-        <term><option>--storage=</option><replaceable>STORAGE</replaceable></term>
+        <term><option>--storage=<replaceable>STORAGE</replaceable></option></term>
 
         <listitem><para>Selects the storage mechanism to use for this home directory. Takes one of
         <literal>luks</literal>, <literal>fscrypt</literal>, <literal>directory</literal>,
       </varlistentry>
 
       <varlistentry>
-        <term><option>--image-path=</option><replaceable>PATH</replaceable></term>
+        <term><option>--image-path=<replaceable>PATH</replaceable></option></term>
 
         <listitem><para>Takes a file system path. Configures where to place the user's home directory. When
         LUKS2 storage is used refers to the path to the loopback file, otherwise to the path to the home
       </varlistentry>
 
       <varlistentry>
-        <term><option>--drop-caches=</option><replaceable>BOOL</replaceable></term>
+        <term><option>--drop-caches=<replaceable>BOOL</replaceable></option></term>
 
         <listitem><para>Automatically flush OS file system caches on logout. This is useful in combination
         with the fscrypt storage backend to ensure the OS does not keep decrypted versions of the files and
       </varlistentry>
 
       <varlistentry>
-        <term><option>--fs-type=</option><replaceable>TYPE</replaceable></term>
+        <term><option>--fs-type=<replaceable>TYPE</replaceable></option></term>
 
         <listitem><para>When LUKS2 storage is used configures the file system type to use inside the home
         directory LUKS2 container. One of <literal>btrfs</literal>, <literal>ext4</literal>,
       </varlistentry>
 
       <varlistentry>
-        <term><option>--luks-discard=</option><replaceable>BOOL</replaceable></term>
+        <term><option>--luks-discard=<replaceable>BOOL</replaceable></option></term>
 
         <listitem><para>When LUKS2 storage is used configures whether to enable the
         <literal>discard</literal> feature of the file system. If enabled the file system on top of the LUKS2
       </varlistentry>
 
       <varlistentry>
-        <term><option>--luks-offline-discard=</option><replaceable>BOOL</replaceable></term>
+        <term><option>--luks-offline-discard=<replaceable>BOOL</replaceable></option></term>
 
         <listitem><para>Similar to <option>--luks-discard=</option>, controls the trimming of the file
         system. However, while <option>--luks-discard=</option> controls what happens when the home directory
       </varlistentry>
 
       <varlistentry>
-        <term><option>--luks-extra-mount-options=</option><replaceable>OPTIONS</replaceable></term>
+        <term><option>--luks-extra-mount-options=<replaceable>OPTIONS</replaceable></option></term>
 
         <listitem><para>Takes a string containing additional mount options to use when mounting the LUKS
         volume. If specified, this string will be appended to the default, built-in mount
       </varlistentry>
 
       <varlistentry>
-        <term><option>--luks-cipher=</option><replaceable>CIPHER</replaceable></term>
-        <term><option>--luks-cipher-mode=</option><replaceable>MODE</replaceable></term>
-        <term><option>--luks-volume-key-size=</option><replaceable>BYTES</replaceable></term>
-        <term><option>--luks-pbkdf-type=</option><replaceable>TYPE</replaceable></term>
-        <term><option>--luks-pbkdf-hash-algorithm=</option><replaceable>ALGORITHM</replaceable></term>
-        <term><option>--luks-pbkdf-force-iterations=</option><replaceable>ITERATIONS</replaceable></term>
-        <term><option>--luks-pbkdf-time-cost=</option><replaceable>SECONDS</replaceable></term>
-        <term><option>--luks-pbkdf-memory-cost=</option><replaceable>BYTES</replaceable></term>
-        <term><option>--luks-pbkdf-parallel-threads=</option><replaceable>THREADS</replaceable></term>
-        <term><option>--luks-sector-size=</option><replaceable>BYTES</replaceable></term>
+        <term><option>--luks-cipher=<replaceable>CIPHER</replaceable></option></term>
+        <term><option>--luks-cipher-mode=<replaceable>MODE</replaceable></option></term>
+        <term><option>--luks-volume-key-size=<replaceable>BYTES</replaceable></option></term>
+        <term><option>--luks-pbkdf-type=<replaceable>TYPE</replaceable></option></term>
+        <term><option>--luks-pbkdf-hash-algorithm=<replaceable>ALGORITHM</replaceable></option></term>
+        <term><option>--luks-pbkdf-force-iterations=<replaceable>ITERATIONS</replaceable></option></term>
+        <term><option>--luks-pbkdf-time-cost=<replaceable>SECONDS</replaceable></option></term>
+        <term><option>--luks-pbkdf-memory-cost=<replaceable>BYTES</replaceable></option></term>
+        <term><option>--luks-pbkdf-parallel-threads=<replaceable>THREADS</replaceable></option></term>
+        <term><option>--luks-sector-size=<replaceable>BYTES</replaceable></option></term>
 
         <listitem><para>Configures various cryptographic parameters for the LUKS2 storage mechanism. See
         <citerefentry
       </varlistentry>
 
       <varlistentry>
-        <term><option>--nosuid=</option><replaceable>BOOL</replaceable></term>
-        <term><option>--nodev=</option><replaceable>BOOL</replaceable></term>
-        <term><option>--noexec=</option><replaceable>BOOL</replaceable></term>
+        <term><option>--nosuid=<replaceable>BOOL</replaceable></option></term>
+        <term><option>--nodev=<replaceable>BOOL</replaceable></option></term>
+        <term><option>--noexec=<replaceable>BOOL</replaceable></option></term>
 
         <listitem><para>Configures the <literal>nosuid</literal>, <literal>nodev</literal> and
         <literal>noexec</literal> mount options for the home directories. By default <literal>nodev</literal>
       </varlistentry>
 
       <varlistentry>
-        <term><option>--cifs-domain=</option><replaceable>DOMAIN</replaceable></term>
-        <term><option>--cifs-user-name=</option><replaceable>USER</replaceable></term>
-        <term><option>--cifs-service=</option><replaceable>SERVICE</replaceable></term>
-        <term><option>--cifs-extra-mount-options=</option><replaceable>OPTIONS</replaceable></term>
+        <term><option>--cifs-domain=<replaceable>DOMAIN</replaceable></option></term>
+        <term><option>--cifs-user-name=<replaceable>USER</replaceable></option></term>
+        <term><option>--cifs-service=<replaceable>SERVICE</replaceable></option></term>
+        <term><option>--cifs-extra-mount-options=<replaceable>OPTIONS</replaceable></option></term>
 
         <listitem><para>Configures the Windows File Sharing (CIFS) domain and user to associate with the home
         directory/user account, as well as the file share ("service") to mount as directory. The latter is
       </varlistentry>
 
       <varlistentry>
-        <term><option>--stop-delay=</option><replaceable>SECS</replaceable></term>
+        <term><option>--stop-delay=<replaceable>SECS</replaceable></option></term>
 
         <listitem><para>Configures the time the per-user service manager shall continue to run after the all
         sessions of the user ended. The default is configured in
       </varlistentry>
 
       <varlistentry>
-        <term><option>--kill-processes=</option><replaceable>BOOL</replaceable></term>
+        <term><option>--kill-processes=<replaceable>BOOL</replaceable></option></term>
 
         <listitem><para>Configures whether to kill all processes of the user on logout. The default is
         configured in
       </varlistentry>
 
       <varlistentry>
-        <term><option>--auto-login=</option><replaceable>BOOL</replaceable></term>
+        <term><option>--auto-login=<replaceable>BOOL</replaceable></option></term>
 
         <listitem><para>Takes a boolean argument. Configures whether the graphical UI of the system should
         automatically log this user in if possible. Defaults to off. If less or more than one user is marked
       </varlistentry>
 
       <varlistentry>
-        <term><option>--session-launcher=</option><replaceable>LAUNCHER</replaceable></term>
+        <term><option>--session-launcher=<replaceable>LAUNCHER</replaceable></option></term>
 
         <listitem><para>Takes a string argument. Configures the user's preferred session launcher
         .desktop entry file (i.e. <literal>gnome</literal>, <literal>plasma</literal>, or other names that
       </varlistentry>
 
       <varlistentry>
-        <term><option>--session-type=</option><replaceable>TYPE</replaceable></term>
+        <term><option>--session-type=<replaceable>TYPE</replaceable></option></term>
 
         <listitem><para>Takes a string argument. Configures the user's preferred session type
         (i.e. <literal>x11</literal>, <literal>wayland</literal>, and other values accepted by
 
       <varlistentry>
         <term><command>create</command> <replaceable>USER</replaceable></term>
-        <term><command>create</command> <option>--identity=</option><replaceable>PATH</replaceable> <optional><replaceable>USER</replaceable></optional></term>
+        <term><command>create</command> <option>--identity=<replaceable>PATH</replaceable></option> <optional><replaceable>USER</replaceable></optional></term>
 
         <listitem><para>Create a new home directory/user account of the specified name. Use the various
         user record property options (as documented above) to control various aspects of the home directory
 
       <varlistentry>
         <term><command>update</command> <replaceable>USER</replaceable></term>
-        <term><command>update</command> <option>--identity=</option><replaceable>PATH</replaceable> <optional><replaceable>USER</replaceable></optional></term>
+        <term><command>update</command> <option>--identity=<replaceable>PATH</replaceable></option> <optional><replaceable>USER</replaceable></optional></term>
 
         <listitem><para>Update a home directory/user account. Use the various user record property options
         (as documented above) to make changes to the account, or alternatively provide a full, updated JSON
index 19a5db802299a552fc6cf22a94bb04408220c295..3ce3ccd87f5b727c9cbd614641e74d3927a3bf0b 100644 (file)
@@ -1,17 +1,18 @@
 /* SPDX-License-Identifier: MIT-0 */
 
+#define _GNU_SOURCE 1
 #include <stdio.h>
 #include <stdint.h>
-#include <sd-hwdb.h>
+#include <systemd/sd-hwdb.h>
 
 int print_usb_properties(uint16_t vid, uint16_t pid) {
-  char match[STRLEN("usb:vp") + DECIMAL_STR_MAX(uint16_t) * 2];
+  char match[128];
   sd_hwdb *hwdb;
   const char *key, *value;
   int r;
 
   /* Match this USB vendor and product ID combination */
-  xsprintf(match, "usb:v%04Xp%04X", vid, pid);
+  snprintf(match, sizeof match, "usb:v%04Xp%04X", vid, pid);
 
   r = sd_hwdb_new(&hwdb);
   if (r < 0)
index bb0931970a66ecd367375746976251f094cd4d51..3d35b00178651449d10219a3887a0dd29e7cf54c 100644 (file)
@@ -11,8 +11,7 @@ int main(int argc, char *argv[]) {
 
   r = sd_journal_open(&j, SD_JOURNAL_LOCAL_ONLY);
   if (r < 0) {
-    errno = -r;
-    fprintf(stderr, "Failed to open journal: %m\n");
+    fprintf(stderr, "Failed to open journal: %s\n", strerror(-r));
     return 1;
   }
   SD_JOURNAL_FOREACH_FIELD(j, field)
index 381b50f9ceb0e1149a1acd72dbe3acd1a8aabb7f..9c0fa0eaf13258a65299afb06726bd5ddd509d52 100644 (file)
@@ -7,10 +7,10 @@
 int main(int argc, char *argv[]) {
   int r;
   sd_journal *j;
+
   r = sd_journal_open(&j, SD_JOURNAL_LOCAL_ONLY);
   if (r < 0) {
-    errno = -r;
-    fprintf(stderr, "Failed to open journal: %m\n");
+    fprintf(stderr, "Failed to open journal: %s\n", strerror(-r));
     return 1;
   }
   SD_JOURNAL_FOREACH(j) {
@@ -19,8 +19,7 @@ int main(int argc, char *argv[]) {
 
     r = sd_journal_get_data(j, "MESSAGE", (const void **)&d, &l);
     if (r < 0) {
-      errno = -r;
-      fprintf(stderr, "Failed to read message field: %m\n");
+      fprintf(stderr, "Failed to read message field: %s\n", strerror(-r));
       continue;
     }
 
index d377324b780e54aafb5e766217f8f712d54ae00f..6b78296267ad3f1613c0bbb8bfc66c8a64107fa9 100644 (file)
@@ -1,5 +1,6 @@
 /* SPDX-License-Identifier: MIT-0 */
 
+#define _GNU_SOURCE 1
 #include <poll.h>
 #include <time.h>
 #include <systemd/sd-journal.h>
index 5fe98b36b75d20571acd9e32164dd4a88f959d26..f44303d75cdfb99d2e61a0535af51cfd0b1e873d 100644 (file)
@@ -12,14 +12,12 @@ int main(int argc, char *argv[]) {
 
   r = sd_journal_open(&j, SD_JOURNAL_LOCAL_ONLY);
   if (r < 0) {
-    errno = -r;
-    fprintf(stderr, "Failed to open journal: %m\n");
+    fprintf(stderr, "Failed to open journal: %s\n", strerror(-r));
     return 1;
   }
   r = sd_journal_query_unique(j, "_SYSTEMD_UNIT");
   if (r < 0) {
-    errno = -r;
-    fprintf(stderr, "Failed to query journal: %m\n");
+    fprintf(stderr, "Failed to query journal: %s\n", strerror(-r));
     return 1;
   }
   SD_JOURNAL_FOREACH_UNIQUE(j, d, l)
index ac4b60b8e9bb9a4016e594186e519656ab7491ea..69d3cccb34af97c19b7365c97cee506d44b3e7ff 100644 (file)
@@ -7,39 +7,38 @@
 int main(int argc, char *argv[]) {
   int r;
   sd_journal *j;
+
   r = sd_journal_open(&j, SD_JOURNAL_LOCAL_ONLY);
   if (r < 0) {
-    errno = -r;
-    fprintf(stderr, "Failed to open journal: %m\n");
+    fprintf(stderr, "Failed to open journal: %s\n", strerror(-r));
     return 1;
   }
+
   for (;;)  {
     const void *d;
     size_t l;
     r = sd_journal_next(j);
     if (r < 0) {
-      errno = -r;
-      fprintf(stderr, "Failed to iterate to next entry: %m\n");
+      fprintf(stderr, "Failed to iterate to next entry: %s\n", strerror(-r));
       break;
     }
     if (r == 0) {
       /* Reached the end, let's wait for changes, and try again */
       r = sd_journal_wait(j, (uint64_t) -1);
       if (r < 0) {
-        errno = -r;
-        fprintf(stderr, "Failed to wait for changes: %m\n");
+        fprintf(stderr, "Failed to wait for changes: %s\n", strerror(-r));
         break;
       }
       continue;
     }
     r = sd_journal_get_data(j, "MESSAGE", &d, &l);
     if (r < 0) {
-      errno = -r;
-      fprintf(stderr, "Failed to read message field: %m\n");
+      fprintf(stderr, "Failed to read message field: %s\n", strerror(-r));
       continue;
     }
     printf("%.*s\n", (int) l, (const char*) d);
   }
+
   sd_journal_close(j);
   return 0;
 }
index 8aad5ff8c66e0c3a733acdcf37f40797e22ba64b..595091af810b02113f2d76ff281495cf2bc87150 100644 (file)
@@ -1,5 +1,6 @@
 /* SPDX-License-Identifier: MIT-0 */
 
+#define _GNU_SOURCE 1
 #include <errno.h>
 #include <syslog.h>
 #include <stdio.h>
 int main(int argc, char *argv[]) {
   int fd;
   FILE *log;
+
   fd = sd_journal_stream_fd("test", LOG_INFO, 1);
   if (fd < 0) {
-    errno = -fd;
-    fprintf(stderr, "Failed to create stream fd: %m\n");
+    fprintf(stderr, "Failed to create stream fd: %s\n", strerror(-fd));
     return 1;
   }
+
   log = fdopen(fd, "w");
   if (!log) {
-    fprintf(stderr, "Failed to create file object: %m\n");
+    fprintf(stderr, "Failed to create file object: %s\n", strerror(errno));
     close(fd);
     return 1;
   }
index 4363702b10ca34b643ff8ad0aa67f25f975a6ed7..4aa8a0ef42f4bf2c04682626ac5dfb0b89eece88 100644 (file)
@@ -25,6 +25,8 @@
   <refsynopsisdiv>
     <para><simplelist>
       <member><filename>/etc/systemd/journald.conf</filename></member>
+      <member><filename>/run/systemd/journald.conf</filename></member>
+      <member><filename>/usr/lib/systemd/journald.conf</filename></member>
       <member><filename>/etc/systemd/journald.conf.d/*.conf</filename></member>
       <member><filename>/run/systemd/journald.conf.d/*.conf</filename></member>
       <member><filename>/usr/lib/systemd/journald.conf.d/*.conf</filename></member>
index 72003d86f2fd16d0e87caf064aa923d83aff88b8..28250a2720b0d1933565f759cd6ad8ffe78f2798 100644 (file)
         <term><varname>resumeflags=</varname></term>
 
         <listitem>
-          <para>Enables resume from hibernation using the specified
-          device and mount options. All
-          <citerefentry project='man-pages'><refentrytitle>fstab</refentrytitle><manvolnum>5</manvolnum></citerefentry>-like
-          paths are supported. For details, see
+          <para>Enable resume from hibernation using the specified device and timeout options. All
+          <citerefentry project='man-pages'><refentrytitle>fstab</refentrytitle><manvolnum>5</manvolnum></citerefentry>-style
+          device identifiers are supported. For details, see
           <citerefentry><refentrytitle>systemd-hibernate-resume-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>.</para>
 
           <xi:include href="version-info.xml" xpointer="v217"/>
         </listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><varname>resume_offset=</varname></term>
+
+        <listitem><para>Configure the page offset of the swap space from the resume device. For details, see
+        <citerefentry><refentrytitle>systemd-hibernate-resume-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>.
+        </para>
+
+        <xi:include href="version-info.xml" xpointer="v254"/></listitem>
+      </varlistentry>
+
       <varlistentry>
         <term><varname>systemd.firstboot=</varname></term>
 
index c199ec7a0f2b1c33d57924c63f785d4052aae238..23e73846cdb2fd2f0a67e0d74b217e91314fc70d 100644 (file)
 
 #define _cleanup_(f) __attribute__((cleanup(f)))
 
-#define check(log_level, x) ({                  \
-  int _r = (x);                                 \
-  errno = _r < 0 ? -_r : 0;                     \
-  sd_journal_print((log_level), #x ": %m");     \
-  if (_r < 0)                                   \
-    return EXIT_FAILURE;                        \
-  })
+static int log_error(int log_level, int error, const char *str) {
+  sd_journal_print(log_level, "%s failed: %s", str, strerror(-error));
+  return error;
+}
 
 typedef enum LogTarget {
   LOG_TARGET_JOURNAL,
@@ -127,7 +124,8 @@ static int property_set(
     return r;
 
   if (strcmp(property, "LogLevel") == 0) {
-    for (int i = 0; i < LOG_DEBUG + 1; i++)
+    int i;
+    for (i = 0; i < LOG_DEBUG + 1; i++)
       if (strcmp(value, log_level_table[i]) == 0) {
         o->log_level = i;
         setlogmask(LOG_UPTO(i));
@@ -141,7 +139,8 @@ static int property_set(
   }
 
   if (strcmp(property, "LogTarget") == 0) {
-    for (LogTarget i = 0; i < _LOG_TARGET_MAX; i++)
+    LogTarget i;
+    for (i = 0; i < _LOG_TARGET_MAX; i++)
       if (strcmp(value, log_target_table[i]) == 0) {
         o->log_target = i;
         return 0;
@@ -193,6 +192,7 @@ int main(int argc, char **argv) {
     .log_target = LOG_TARGET_JOURNAL,
     .syslog_identifier = "example",
   };
+  int r;
 
   /* https://man7.org/linux/man-pages/man3/setlogmask.3.html
    * Programs using syslog() instead of sd_journal can use this API to cut logs
@@ -203,37 +203,49 @@ int main(int argc, char **argv) {
   /* Acquire a connection to the bus, letting the library work out the details.
    * https://www.freedesktop.org/software/systemd/man/sd_bus_default.html
    */
-  check(o.log_level, sd_bus_default(&bus));
+  r = sd_bus_default(&bus);
+  if (r < 0)
+    return log_error(o.log_level, r, "sd_bus_default()");
 
   /* Publish an interface on the bus, specifying our well-known object access
    * path and public interface name.
    * https://www.freedesktop.org/software/systemd/man/sd_bus_add_object.html
    * https://dbus.freedesktop.org/doc/dbus-tutorial.html
    */
-  check(o.log_level, sd_bus_add_object_vtable(bus, NULL,
-                                              "/org/freedesktop/LogControl1",
-                                              "org.freedesktop.LogControl1",
-                                              vtable,
-                                              &o));
+  r = sd_bus_add_object_vtable(bus, NULL,
+                               "/org/freedesktop/LogControl1",
+                               "org.freedesktop.LogControl1",
+                               vtable,
+                               &o);
+  if (r < 0)
+    return log_error(o.log_level, r, "sd_bus_add_object_vtable()");
 
   /* By default the service is assigned an ephemeral name. Also add a fixed
    * one, so that clients know whom to call.
    * https://www.freedesktop.org/software/systemd/man/sd_bus_request_name.html
    */
-  check(o.log_level, sd_bus_request_name(bus, "org.freedesktop.Example", 0));
+  r = sd_bus_request_name(bus, "org.freedesktop.Example", 0);
+  if (r < 0)
+    return log_error(o.log_level, r, "sd_bus_request_name()");
 
   for (;;) {
     /* https://www.freedesktop.org/software/systemd/man/sd_bus_wait.html
      */
-    check(o.log_level, sd_bus_wait(bus, UINT64_MAX));
+    r = sd_bus_wait(bus, UINT64_MAX);
+    if (r < 0)
+      return log_error(o.log_level, r, "sd_bus_wait()");
     /* https://www.freedesktop.org/software/systemd/man/sd_bus_process.html
      */
-    check(o.log_level, sd_bus_process(bus, NULL));
+    r = sd_bus_process(bus, NULL);
+    if (r < 0)
+      return log_error(o.log_level, r, "sd_bus_process()");
   }
 
   /* https://www.freedesktop.org/software/systemd/man/sd_bus_release_name.html
    */
-  check(o.log_level, sd_bus_release_name(bus, "org.freedesktop.Example"));
+  r = sd_bus_release_name(bus, "org.freedesktop.Example");
+  if (r < 0)
+    return log_error(o.log_level, r, "sd_bus_release_name()");
 
   return 0;
 }
index 488ff7022093e30b076e25427030fc254c033431..69ea5c403ecb48328158ff56e08072cc7bb32a46 100644 (file)
@@ -248,3 +248,89 @@ update_man_rules = custom_target(
                    '@0@/man/*.xml'.format(project_source_root),
                    '@0@/rules/meson.build'.format(meson.current_source_dir())],
         depends : man_page_depends)
+
+############################################################
+
+simple_examples = files(
+        'event-quick-child.c',
+        'hwdb-usb-device.c',
+        'id128-app-specific.c',
+        'inotify-watch-tmp.c',
+        'journal-enumerate-fields.c',
+        'journal-iterate-foreach.c',
+        'journal-iterate-poll.c',
+        'journal-iterate-unique.c',
+        'journal-iterate-wait.c',
+        'journal-stream-fd.c',
+        'logcontrol-example.c',
+        'notify-selfcontained-example.c',
+        'path-documents.c',
+        'print-unit-path-call-method.c',
+        'print-unit-path.c',
+        'sd-bus-container-append.c',
+        'sd-bus-container-read.c',
+        'sd_bus_error-example.c',
+        'sd_bus_service_reconnect.c',
+        'send-unit-files-changed.c',
+        'vtable-example.c',
+)
+
+examples = []
+foreach example : simple_examples
+        examples += [ { 'file' : example } ]
+endforeach
+
+if conf.get('HAVE_GLIB') == 1
+        examples += [
+                {
+                        'file' : files('glib-event-glue.c'),
+                        'opts' : [
+                                '-I', libglib.get_variable('includedir') / 'glib-2.0',
+                                '-I', libglib.get_variable('libdir') / 'glib-2.0/include',
+                        ],
+                },
+        ]
+endif
+
+default_args = [
+        cc.cmd_array(),
+        '-c',
+        '-x', 'c',
+        '-pedantic',
+        '-Wall',
+        '-Werror',
+        '-Wextra',
+        '-Wno-unused-parameter',
+        '-o', '/dev/null',
+        '-I', meson.current_source_dir() / '../src',
+]
+
+std_args_in = [
+        [ '-std=c90', '-Wno-pedantic', '-Wno-variadic-macros', ],
+        [ '-std=c99', ],
+        [ '-std=c11', ],
+        [ '-std=c17', ],
+        [ '-std=c23', ],
+        [ '-std=gnu90', '-Wno-pedantic', '-Wno-variadic-macros', ],
+        [ '-std=gnu99', ],
+        [ '-std=gnu11', ],
+        [ '-std=gnu17', ],
+        [ '-std=gnu23', ],
+]
+
+std_args = []
+foreach std : std_args_in
+        if cc.has_argument(std[0])
+                std_args += [std]
+        endif
+endforeach
+
+foreach item : examples
+        foreach std : std_args
+                file = item.get('file')
+                test('cc-' + fs.stem(file) + '-' + std[0].split('=')[1],
+                     env,
+                     suite : 'example',
+                     args : default_args + std + item.get('opts', []) + [file])
+        endforeach
+endforeach
index 8370e72cf9ba663a40a92fd09d4b6d768551b1d3..5e2126ff218c7ea773f938f7e59f35901559c71f 100644 (file)
@@ -454,12 +454,16 @@ s - Service VLAN, m - Two-port MAC Relay (TPMR)
       <varlistentry>
         <term>
           <command>cat</command>
-          <replaceable>FILE</replaceable>|<replaceable>@DEVICE</replaceable>…
+          <optional><replaceable>FILE</replaceable>|<replaceable>@DEVICE</replaceable>…</optional>
         </term>
-        <listitem><para>Show network configuration files. This command honors
-        the <literal>@</literal> prefix in the same way as <command>edit</command>.</para>
+        <listitem>
+          <para>Show network configuration files. This command honors the <literal>@</literal> prefix in the
+          same way as <command>edit</command>. When no argument is specified,
+          <citerefentry><refentrytitle>networkd.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+          and its drop-in files will be shown.</para>
 
-        <xi:include href="version-info.xml" xpointer="v254"/></listitem>
+          <xi:include href="version-info.xml" xpointer="v254"/>
+        </listitem>
       </varlistentry>
 
       <varlistentry>
@@ -569,7 +573,7 @@ s - Service VLAN, m - Two-port MAC Relay (TPMR)
       </varlistentry>
 
       <varlistentry>
-        <term><option>--drop-in=</option><replaceable>NAME</replaceable></term>
+        <term><option>--drop-in=<replaceable>NAME</replaceable></option></term>
 
         <listitem>
           <para>When used with <command>edit</command>, edit the drop-in file <replaceable>NAME</replaceable>
index f7b3b4711ceef0d4d408db7131722cb8162963d7..8820fcc507da1d02d0d708056a0d09b5ec700ab2 100644 (file)
@@ -32,6 +32,7 @@
       <member><filename>/run/systemd/networkd.conf</filename></member>
       <member><filename>/usr/lib/systemd/networkd.conf</filename></member>
       <member><filename>/etc/systemd/networkd.conf.d/*.conf</filename></member>
+      <member><filename>/run/systemd/networkd.conf.d/*.conf</filename></member>
       <member><filename>/usr/lib/systemd/networkd.conf.d/*.conf</filename></member>
     </simplelist></para>
   </refsynopsisdiv>
@@ -256,6 +257,17 @@ DUIDRawData=00:00:ab:11:f9:2a:c2:77:29:f9:5c:00</programlisting>
           <xi:include href="version-info.xml" xpointer="v230"/>
         </listitem>
       </varlistentry>
+
+      <varlistentry>
+        <term><varname>UseDomains=</varname></term>
+          <listitem><para>Specifies the default value for per-network <varname>UseDomains=</varname>.
+          Takes a boolean. See for details in
+          <citerefentry><refentrytitle>systemd.network</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
+          Defaults to <literal>no</literal>.</para>
+          
+          <xi:include href="version-info.xml" xpointer="v256"/>
+        </listitem>
+      </varlistentry>
     </variablelist>
   </refsect1>
 
@@ -280,6 +292,34 @@ DUIDRawData=00:00:ab:11:f9:2a:c2:77:29:f9:5c:00</programlisting>
 
         <xi:include href="version-info.xml" xpointer="v249"/></listitem>
       </varlistentry>
+
+      <varlistentry>
+        <term><varname>UseDomains=</varname></term>
+        <listitem><para>As in the [DHCPv4] section.</para>
+
+        <xi:include href="version-info.xml" xpointer="v256"/></listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+
+  <refsect1>
+    <title>[DHCPServer] Section Options</title>
+
+    <para>This section configures the default setting of the DHCP server. The following options are available
+    in the [DHCPServer] section:</para>
+
+    <variablelist class='network-directives'>
+      <varlistentry>
+        <term><varname>PersistLeases=</varname></term>
+        <listitem>
+          <para>Specifies the default value for per-network <varname>PersistLeases=</varname>.
+          Takes a boolean. See for details in
+          <citerefentry><refentrytitle>systemd.network</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
+          Defaults to <literal>yes</literal>.</para>
+
+          <xi:include href="version-info.xml" xpointer="v256"/>
+        </listitem>
+      </varlistentry>
     </variablelist>
   </refsect1>
 
diff --git a/man/notify-selfcontained-example.c b/man/notify-selfcontained-example.c
new file mode 100644 (file)
index 0000000..6bbe4f2
--- /dev/null
@@ -0,0 +1,188 @@
+/* SPDX-License-Identifier: MIT-0 */
+
+/* Implement the systemd notify protocol without external dependencies.
+ * Supports both readiness notification on startup and on reloading,
+ * according to the protocol defined at:
+ * https://www.freedesktop.org/software/systemd/man/latest/sd_notify.html
+ * This protocol is guaranteed to be stable as per:
+ * https://systemd.io/PORTABILITY_AND_STABILITY/ */
+
+#define _GNU_SOURCE 1
+#include <errno.h>
+#include <inttypes.h>
+#include <signal.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <time.h>
+#include <unistd.h>
+
+#define _cleanup_(f) __attribute__((cleanup(f)))
+
+static void closep(int *fd) {
+  if (!fd || *fd < 0)
+    return;
+
+  close(*fd);
+  *fd = -1;
+}
+
+static int notify(const char *message) {
+  union sockaddr_union {
+    struct sockaddr sa;
+    struct sockaddr_un sun;
+  } socket_addr = {
+    .sun.sun_family = AF_UNIX,
+  };
+  size_t path_length, message_length;
+  _cleanup_(closep) int fd = -1;
+  const char *socket_path;
+
+  /* Verify the argument first */
+  if (!message)
+    return -EINVAL;
+
+  message_length = strlen(message);
+  if (message_length == 0)
+    return -EINVAL;
+
+  /* If the variable is not set, the protocol is a noop */
+  socket_path = getenv("NOTIFY_SOCKET");
+  if (!socket_path)
+    return 0; /* Not set? Nothing to do */
+
+  /* Only AF_UNIX is supported, with path or abstract sockets */
+  if (socket_path[0] != '/' && socket_path[0] != '@')
+    return -EAFNOSUPPORT;
+
+  path_length = strlen(socket_path);
+  /* Ensure there is room for NUL byte */
+  if (path_length >= sizeof(socket_addr.sun.sun_path))
+    return -E2BIG;
+
+  memcpy(socket_addr.sun.sun_path, socket_path, path_length);
+
+  /* Support for abstract socket */
+  if (socket_addr.sun.sun_path[0] == '@')
+    socket_addr.sun.sun_path[0] = 0;
+
+  fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0);
+  if (fd < 0)
+    return -errno;
+
+  if (connect(fd, &socket_addr.sa, offsetof(struct sockaddr_un, sun_path) + path_length) != 0)
+    return -errno;
+
+  ssize_t written = write(fd, message, message_length);
+  if (written != (ssize_t) message_length)
+    return written < 0 ? -errno : -EPROTO;
+
+  return 1; /* Notified! */
+}
+
+static int notify_ready(void) {
+  return notify("READY=1");
+}
+
+static int notify_reloading(void) {
+  /* A buffer with length sufficient to format the maximum UINT64 value. */
+  char reload_message[sizeof("RELOADING=1\nMONOTONIC_USEC=18446744073709551615")];
+  struct timespec ts;
+  uint64_t now;
+
+  /* Notify systemd that we are reloading, including a CLOCK_MONOTONIC timestamp in usec
+   * so that the program is compatible with a Type=notify-reload service. */
+
+  if (clock_gettime(CLOCK_MONOTONIC, &ts) < 0)
+    return -errno;
+
+  if (ts.tv_sec < 0 || ts.tv_nsec < 0 ||
+      (uint64_t) ts.tv_sec > (UINT64_MAX - (ts.tv_nsec / 1000ULL)) / 1000000ULL)
+    return -EINVAL;
+
+  now = (uint64_t) ts.tv_sec * 1000000ULL + (uint64_t) ts.tv_nsec / 1000ULL;
+
+  if (snprintf(reload_message, sizeof(reload_message), "RELOADING=1\nMONOTONIC_USEC=%" PRIu64, now) < 0)
+    return -EINVAL;
+
+  return notify(reload_message);
+}
+
+static int notify_stopping(void) {
+  return notify("STOPPING=1");
+}
+
+static volatile sig_atomic_t reloading = 0;
+static volatile sig_atomic_t terminating = 0;
+
+static void signal_handler(int sig) {
+  if (sig == SIGHUP)
+    reloading = 1;
+  else if (sig == SIGINT || sig == SIGTERM)
+    terminating = 1;
+}
+
+int main(int argc, char **argv) {
+  struct sigaction sa = {
+    .sa_handler = signal_handler,
+    .sa_flags = SA_RESTART,
+  };
+  int r;
+
+  /* Setup signal handlers */
+  sigemptyset(&sa.sa_mask);
+  sigaction(SIGHUP, &sa, NULL);
+  sigaction(SIGINT, &sa, NULL);
+  sigaction(SIGTERM, &sa, NULL);
+
+  /* Do more service initialization work here … */
+
+  /* Now that all the preparations steps are done, signal readiness */
+
+  r = notify_ready();
+  if (r < 0) {
+    fprintf(stderr, "Failed to notify readiness to $NOTIFY_SOCKET: %s\n", strerror(-r));
+    return EXIT_FAILURE;
+  }
+
+  while (!terminating) {
+    if (reloading) {
+      reloading = false;
+
+      /* As a separate but related feature, we can also notify the manager
+       * when reloading configuration. This allows accurate state-tracking,
+       * and also automated hook-in of 'systemctl reload' without having to
+       * specify manually an ExecReload= line in the unit file. */
+
+      r = notify_reloading();
+      if (r < 0) {
+        fprintf(stderr, "Failed to notify reloading to $NOTIFY_SOCKET: %s\n", strerror(-r));
+        return EXIT_FAILURE;
+      }
+
+      /* Do some reconfiguration work here … */
+
+      r = notify_ready();
+      if (r < 0) {
+        fprintf(stderr, "Failed to notify readiness to $NOTIFY_SOCKET: %s\n", strerror(-r));
+        return EXIT_FAILURE;
+      }
+    }
+
+    /* Do some daemon work here … */
+    sleep(5);
+  }
+
+  r = notify_stopping();
+  if (r < 0) {
+    fprintf(stderr, "Failed to report termination to $NOTIFY_SOCKET: %s\n", strerror(-r));
+    return EXIT_FAILURE;
+  }
+
+  /* Do some shutdown work here … */
+
+  return EXIT_SUCCESS;
+}
index 9b49c610d55c6223dfe29aa79cad2bf6282fbf5a..c68995d95248f797e108e98071fc1d527bafb532 100644 (file)
@@ -286,8 +286,7 @@ node /org/freedesktop/portable1 {
       <para><function>DetachImageWithExtensions()</function> detaches a portable image from the system.
       This method is a superset of <function>DetachImage()</function> with the addition of
       a list of extensions as input parameter, which were overlaid on top of the main
-      image via <function>AttachImageWithExtensions()</function>.
-      The <varname>flag</varname> parameter is currently unused and reserved for future purposes.</para>
+      image via <function>AttachImageWithExtensions()</function>.</para>
 
       <para><function>ReattachImage()</function> combines the effects of the
       <function>AttachImage()</function> method and the <function>DetachImage()</function> method.
@@ -304,7 +303,7 @@ node /org/freedesktop/portable1 {
       image. For more details on this functionality, see the <varname>MountImages=</varname> entry on
       <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry>
       and <citerefentry><refentrytitle>systemd-sysext</refentrytitle><manvolnum>8</manvolnum></citerefentry>.
-      The <varname>flag</varname> parameter is currently unused and reserved for future purposes</para>
+      </para>
 
       <para><function>RemoveImage()</function> removes the image with the specified name.</para>
 
index 4e43b4ba20dc70b9c2a485766b9548b98a6179c1..25b98c7ace406b894bd2d6126f177d4f15e744bc 100644 (file)
@@ -340,6 +340,10 @@ node /org/freedesktop/systemd1 {
       @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
       readonly t FinishTimestampMonotonic = ...;
       @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t SoftRebootStartTimestamp = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t SoftRebootStartTimestampMonotonic = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
       readonly t SecurityStartTimestamp = ...;
       @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
       readonly t SecurityStartTimestampMonotonic = ...;
@@ -556,6 +560,8 @@ node /org/freedesktop/systemd1 {
       readonly i DefaultOOMScoreAdjust = ...;
       @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
       readonly s CtrlAltDelBurstAction = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly u SoftRebootsCount = ...;
   };
   interface org.freedesktop.DBus.Peer { ... };
   interface org.freedesktop.DBus.Introspectable { ... };
@@ -1033,6 +1039,10 @@ node /org/freedesktop/systemd1 {
 
     <variablelist class="dbus-property" generated="True" extra-ref="FinishTimestampMonotonic"/>
 
+    <variablelist class="dbus-property" generated="True" extra-ref="SoftRebootStartTimestamp"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="SoftRebootStartTimestampMonotonic"/>
+
     <variablelist class="dbus-property" generated="True" extra-ref="SecurityStartTimestamp"/>
 
     <variablelist class="dbus-property" generated="True" extra-ref="SecurityStartTimestampMonotonic"/>
@@ -1243,6 +1253,8 @@ node /org/freedesktop/systemd1 {
 
     <variablelist class="dbus-property" generated="True" extra-ref="CtrlAltDelBurstAction"/>
 
+    <variablelist class="dbus-property" generated="True" extra-ref="SoftRebootsCount"/>
+
     <!--End of Autogenerated section-->
 
     <refsect2>
@@ -1733,17 +1745,19 @@ node /org/freedesktop/systemd1 {
       <varname>KernelTimestamp</varname>, <varname>KernelTimestampMonotonic</varname>,
       <varname>InitRDTimestamp</varname>, <varname>InitRDTimestampMonotonic</varname>,
       <varname>UserspaceTimestamp</varname>, <varname>UserspaceTimestampMonotonic</varname>,
-      <varname>FinishTimestamp</varname>, and <varname>FinishTimestampMonotonic</varname> encode
-      <constant>CLOCK_REALTIME</constant> and <constant>CLOCK_MONOTONIC</constant> microsecond timestamps
-      taken when the firmware first began execution, when the boot loader first began execution, when the
-      kernel first began execution, when the initrd first began execution, when the main systemd instance
-      began execution and finally, when all queued startup jobs finished execution. These values are useful
-      for determining boot-time performance. Note that as monotonic time begins with the kernel startup, the
-      <varname>KernelTimestampMonotonic</varname> timestamp will always be 0 and
-      <varname>FirmwareTimestampMonotonic</varname> and <varname>LoaderTimestampMonotonic</varname> are to
-      be read as negative values. Also, not all fields are always available, depending on the used firmware,
-      boot loader or initrd implementation. In these cases the respective pairs of timestamps are both 0,
-      indicating that no data is available.</para>
+      <varname>FinishTimestamp</varname>, <varname>FinishTimestampMonotonic</varname>,
+      <varname>SoftRebootStartTimestamp</varname> and <varname>SoftRebootStartTimestampMonotonic</varname>
+      encode <constant>CLOCK_REALTIME</constant> and <constant>CLOCK_MONOTONIC</constant> microsecond
+      timestamps taken when the firmware first began execution, when the boot loader first began execution,
+      when the kernel first began execution, when the initrd first began execution, when the main systemd
+      instance began execution, when all queued startup jobs finished execution and finally, when a
+      <citerefentry><refentrytitle>systemd-soft-reboot.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+      operation first began execution. These values are useful for determining boot-time performance. Note
+      that as monotonic time begins with the kernel startup, the <varname>KernelTimestampMonotonic</varname>
+      timestamp will always be 0 and <varname>FirmwareTimestampMonotonic</varname> and
+      <varname>LoaderTimestampMonotonic</varname> are to be read as negative values. Also, not all fields
+      are always available, depending on the used firmware, boot loader or initrd implementation. In these
+      cases the respective pairs of timestamps are both 0, indicating that no data is available.</para>
 
       <para><varname>UnitsLoadTimestamp</varname> and <varname>UnitsLoadTimestampMonotonic</varname> encode
       <constant>CLOCK_REALTIME</constant> and <constant>CLOCK_MONOTONIC</constant> microseconds timestamps
@@ -1777,6 +1791,9 @@ node /org/freedesktop/systemd1 {
       <para><varname>UnitPath</varname> encodes the currently active unit file search path. It is an array of
       file system paths encoded as strings.</para>
 
+      <para><varname>SoftRebootsCount</varname> encodes how many soft-reboots were successfully completed
+      since the last full boot. Starts at <literal>0</literal>.</para>
+
       <para><varname>Virtualization</varname> contains a short ID string describing the virtualization
       technology the system runs in. On bare-metal hardware this is the empty string. Otherwise, it contains
       an identifier such as <literal>kvm</literal>, <literal>vmware</literal> and so on. For a full list of
@@ -11982,7 +11999,10 @@ $ gdbus introspect --system --dest org.freedesktop.systemd1 \
       <function>QueueSignalUnit()</function>,
       <function>SoftReboot()</function>, and
       <function>DumpUnitFileDescriptorStore()</function> were added in version 254.</para>
-      <para><function>StartAuxiliaryScope()</function> was added in version 256.</para>
+      <para><function>StartAuxiliaryScope()</function>,
+      <varname>SoftRebootStartTimestamp</varname>,
+      <varname>SoftRebootStartTimestampMonotonic</varname> and
+      <varname>SoftRebootsCount</varname> were added in version 256.</para>
     </refsect2>
     <refsect2>
       <title>Unit Objects</title>
index a357dd659a84773b2fdff8ade11066c4f89d7450..994f20bcf4e172f95e6b9c24f832a6e96423c3e2 100644 (file)
@@ -2,7 +2,7 @@
 
 #include <stdio.h>
 #include <stdlib.h>
-#include <sd-path.h>
+#include <systemd/sd-path.h>
 
 int main(void) {
   int r;
index 9651112f9780950b2126fdee4736b34d5df35398..bcfaf7ced15ca310f0e51011357363ec651635c5 100644 (file)
 
       <varlistentry>
         <term><option>-p</option> <replaceable>PROFILE</replaceable></term>
-        <term><option>--profile=</option><replaceable>PROFILE</replaceable></term>
+        <term><option>--profile=<replaceable>PROFILE</replaceable></option></term>
 
         <listitem><para>When attaching an image, select the profile to use. By default the <literal>default</literal>
         profile is used. For details about profiles, see below.</para>
       </varlistentry>
 
       <varlistentry>
-        <term><option>--extension=</option><replaceable>PATH</replaceable></term>
+        <term><option>--extension=<replaceable>PATH</replaceable></option></term>
 
         <listitem><para>Add an additional image <replaceable>PATH</replaceable> as an overlay on
         top of <replaceable>IMAGE</replaceable> when attaching/detaching. This argument can be specified
index f73dd073f9871a60ccee5c2d40c1698e08a6f269..15e8d3f51b29ca832b718b6636b26ce3da4f8421 100644 (file)
@@ -21,8 +21,7 @@
 #define MEMBER      "GetUnitByPID"
 
 static int log_error(int error, const char *message) {
-  errno = -error;
-  fprintf(stderr, "%s: %m\n", message);
+  fprintf(stderr, "%s: %s\n", message, strerror(-error));
   return error;
 }
 
index 0b89318736aeb5060235d5100be2fd062ac6f726..737244feb0d4841e2abd73da9bb93946baa4dde5 100644 (file)
@@ -21,8 +21,7 @@
 #define MEMBER      "GetUnitByPID"
 
 static int log_error(int error, const char *message) {
-  errno = -error;
-  fprintf(stderr, "%s: %m\n", message);
+  fprintf(stderr, "%s: %s\n", message, strerror(-error));
   return error;
 }
 
index 91db8a91d42db94b5c836d7db5a51be9f988ab07..0a2ae6249146bef47bb4c1c5bb06f39f6ca6c2c0 100644 (file)
         <xi:include href="version-info.xml" xpointer="v255"/></listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><varname>DefaultSubvolume=</varname></term>
+
+        <listitem><para>Takes an absolute path specifying the default subvolume within the new filesystem.
+        Note that this setting does not create the subvolume itself, that can be configured with
+        <varname>Subvolumes=</varname>.</para>
+
+        <para>Note that this option only takes effect if the target filesystem supports subvolumes, such as
+        <literal>btrfs</literal>.</para>
+
+        <para>Note that due to limitations of <literal>mkfs.btrfs</literal>, this option is only supported
+        when running with <option>--offline=no</option>.</para>
+
+        <xi:include href="version-info.xml" xpointer="v256"/></listitem>
+      </varlistentry>
+
       <varlistentry>
         <term><varname>Encrypt=</varname></term>
 
index afa4ca77ebaedaf18c63d2ba59f402d964e260d8..ed656b48d72f2e8992e8822ca8d283c2b8644812 100644 (file)
 
       <varlistentry>
         <term><option>-i</option> <replaceable>INTERFACE</replaceable></term>
-        <term><option>--interface=</option><replaceable>INTERFACE</replaceable></term>
+        <term><option>--interface=<replaceable>INTERFACE</replaceable></option></term>
 
         <listitem><para>Specifies the network interface to execute the query on. This may either be specified as numeric
         interface index or as network interface string (e.g. <literal>en0</literal>). Note that this option has no
 
       <varlistentry>
         <term><option>-p</option> <replaceable>PROTOCOL</replaceable></term>
-        <term><option>--protocol=</option><replaceable>PROTOCOL</replaceable></term>
+        <term><option>--protocol=<replaceable>PROTOCOL</replaceable></option></term>
 
         <listitem><para>Specifies the network protocol for the query. May be one of <literal>dns</literal>
         (i.e. classic unicast DNS), <literal>llmnr</literal> (<ulink
 
       <varlistentry>
         <term><option>-t</option> <replaceable>TYPE</replaceable></term>
-        <term><option>--type=</option><replaceable>TYPE</replaceable></term>
+        <term><option>--type=<replaceable>TYPE</replaceable></option></term>
         <term><option>-c</option> <replaceable>CLASS</replaceable></term>
-        <term><option>--class=</option><replaceable>CLASS</replaceable></term>
+        <term><option>--class=<replaceable>CLASS</replaceable></option></term>
 
         <listitem><para>When used in conjunction with the <command>query</command> command, specifies the DNS
         resource record type (e.g. <constant class='dns'>A</constant>, <constant class='dns'>AAAA</constant>,
       </varlistentry>
 
       <varlistentry>
-        <term><option>--service-address=</option><replaceable>BOOL</replaceable></term>
+        <term><option>--service-address=<replaceable>BOOL</replaceable></option></term>
 
         <listitem><para>Takes a boolean parameter. If true (the default), when doing a service lookup with
         <option>--service</option> the hostnames contained in the <constant class='dns'>SRV</constant>
       </varlistentry>
 
       <varlistentry>
-        <term><option>--service-txt=</option><replaceable>BOOL</replaceable></term>
+        <term><option>--service-txt=<replaceable>BOOL</replaceable></option></term>
 
         <listitem><para>Takes a boolean parameter. If true (the default), when doing a DNS-SD service lookup
         with <option>--service</option> the <constant class='dns'>TXT</constant> service metadata record is
       </varlistentry>
 
       <varlistentry>
-        <term><option>--cname=</option><replaceable>BOOL</replaceable></term>
+        <term><option>--cname=<replaceable>BOOL</replaceable></option></term>
 
         <listitem><para>Takes a boolean parameter. If true (the default), DNS <constant
         class='dns'>CNAME</constant> or <constant class='dns'>DNAME</constant> redirections are
       </varlistentry>
 
       <varlistentry>
-        <term><option>--validate=</option><replaceable>BOOL</replaceable></term>
+        <term><option>--validate=<replaceable>BOOL</replaceable></option></term>
 
         <listitem><para>Takes a boolean parameter; used in conjunction with <command>query</command>. If true
         (the default), DNSSEC validation is applied as usual — under the condition that it is enabled for the
       </varlistentry>
 
       <varlistentry>
-        <term><option>--synthesize=</option><replaceable>BOOL</replaceable></term>
+        <term><option>--synthesize=<replaceable>BOOL</replaceable></option></term>
 
         <listitem><para>Takes a boolean parameter; used in conjunction with <command>query</command>. If true
         (the default), select domains are resolved on the local system, among them
       </varlistentry>
 
       <varlistentry>
-        <term><option>--cache=</option><replaceable>BOOL</replaceable></term>
+        <term><option>--cache=<replaceable>BOOL</replaceable></option></term>
 
         <listitem><para>Takes a boolean parameter; used in conjunction with <command>query</command>. If true
         (the default), lookups use the local DNS resource record cache. If false, lookups are routed to the
       </varlistentry>
 
       <varlistentry>
-        <term><option>--zone=</option><replaceable>BOOL</replaceable></term>
+        <term><option>--zone=<replaceable>BOOL</replaceable></option></term>
 
         <listitem><para>Takes a boolean parameter; used in conjunction with <command>query</command>. If true
         (the default), lookups are answered from locally registered LLMNR or mDNS resource records, if
       </varlistentry>
 
       <varlistentry>
-        <term><option>--trust-anchor=</option><replaceable>BOOL</replaceable></term>
+        <term><option>--trust-anchor=<replaceable>BOOL</replaceable></option></term>
 
         <listitem><para>Takes a boolean parameter; used in conjunction with <command>query</command>. If true
         (the default), lookups for DS and DNSKEY are answered from the local DNSSEC trust anchors if
       </varlistentry>
 
       <varlistentry>
-        <term><option>--network=</option><replaceable>BOOL</replaceable></term>
+        <term><option>--network=<replaceable>BOOL</replaceable></option></term>
 
         <listitem><para>Takes a boolean parameter; used in conjunction with <command>query</command>. If true
         (the default), lookups are answered via DNS, LLMNR or mDNS network requests if they cannot be
       </varlistentry>
 
       <varlistentry>
-        <term><option>--search=</option><replaceable>BOOL</replaceable></term>
+        <term><option>--search=<replaceable>BOOL</replaceable></option></term>
 
         <listitem><para>Takes a boolean parameter. If true (the default), any specified single-label
         hostnames will be searched in the domains configured in the search domain list, if it is
       </varlistentry>
 
       <varlistentry>
-        <term><option>--legend=</option><replaceable>BOOL</replaceable></term>
+        <term><option>--legend=<replaceable>BOOL</replaceable></option></term>
 
         <listitem><para>Takes a boolean parameter. If true (the default), column headers and meta information about the
         query response are shown. Otherwise, this output is suppressed.</para>
       </varlistentry>
 
       <varlistentry>
-        <term><option>--stale-data=</option><replaceable>BOOL</replaceable></term>
+        <term><option>--stale-data=<replaceable>BOOL</replaceable></option></term>
 
         <listitem><para>Takes a boolean parameter; used in conjunction with <command>query</command>. If true
         (the default), lookups are answered with stale data (expired resource records) if
       </varlistentry>
 
       <varlistentry>
-        <term><option>--relax-single-label=</option><replaceable>BOOL</replaceable></term>
+        <term><option>--relax-single-label=<replaceable>BOOL</replaceable></option></term>
 
         <listitem><para>Takes a boolean parameter; used in conjunction with <command>query</command>. If
         true, rules regarding routing of single-label names are relaxed. Defaults to false. By default,
index e483196c5c5d0c93f7b7497fb743bf433b0a27a3..cf2be55bbded1c0b14723e3c031cc4c0653702d4 100644 (file)
@@ -944,7 +944,7 @@ manpages = [
  ['systemd-hibernate-resume-generator', '8', [], 'ENABLE_HIBERNATE'],
  ['systemd-hibernate-resume.service',
   '8',
-  ['systemd-hibernate-resume'],
+  ['systemd-hibernate-clear.service', 'systemd-hibernate-resume'],
   'ENABLE_HIBERNATE'],
  ['systemd-homed.service', '8', ['systemd-homed'], 'ENABLE_HOMED'],
  ['systemd-hostnamed.service', '8', ['systemd-hostnamed'], 'ENABLE_HOSTNAMED'],
@@ -999,6 +999,7 @@ manpages = [
  ['systemd-measure', '1', [], 'HAVE_TPM2 HAVE_BLKID HAVE_OPENSSL'],
  ['systemd-modules-load.service', '8', ['systemd-modules-load'], 'HAVE_KMOD'],
  ['systemd-mount', '1', ['systemd-umount'], ''],
+ ['systemd-mountfsd.service', '8', ['systemd-mountfsd'], 'ENABLE_MOUNTFSD'],
  ['systemd-network-generator.service', '8', ['systemd-network-generator'], ''],
  ['systemd-networkd-wait-online.service',
   '8',
@@ -1007,6 +1008,10 @@ manpages = [
  ['systemd-networkd.service', '8', ['systemd-networkd'], 'ENABLE_NETWORKD'],
  ['systemd-notify', '1', [], ''],
  ['systemd-nspawn', '1', [], ''],
+ ['systemd-nsresourced.service',
+  '8',
+  ['systemd-nsresourced'],
+  'ENABLE_NSRESOURCED'],
  ['systemd-oomd.service', '8', ['systemd-oomd'], 'ENABLE_OOMD'],
  ['systemd-path', '1', [], ''],
  ['systemd-pcrlock',
index 8bb4f33e867ab4e71dbf924fdb2267fa0e8be2ce..07a24f24cc6ced7f4983f0ef0dcf29feafad6cd7 100644 (file)
@@ -3,13 +3,14 @@
 #include <systemd/sd-bus.h>
 
 int append_strings_to_message(sd_bus_message *m, const char *const *arr) {
+  const char *s;
   int r;
 
   r = sd_bus_message_open_container(m, 'a', "s");
   if (r < 0)
     return r;
 
-  for (const char *s = *arr; *s; s++) {
+  for (s = *arr; *s; s++) {
     r = sd_bus_message_append(m, "s", s);
     if (r < 0)
       return r;
index d143cb4a0f7ecc36bd148ffff14bb784557d203c..6fc18e71e377604d46ea8d67ad48970c19bb5e2e 100644 (file)
     — are fully thread-safe and may be called from multiple threads in parallel.</para>
   </refsect1>
 
+  <refsect1>
+    <title>Optional dependencies</title>
+
+    <para>Depending on which build-time options are enabled, functions that operate on
+    <structname>sd_journal</structname> objects might cause optional shared libraries to be dynamically
+    loaded via
+    <citerefentry project='man-pages'><refentrytitle>dlopen</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+    such as decompression libraries (xz, lz4, zstd) or cryptographic libraries (gcrypt).
+    </para>
+  </refsect1>
+
   <xi:include href="libsystemd-pkgconfig.xml" />
 
   <refsect1>
index 9b162eb6cc355eada1368c1e821878a8355255e5..3836f5e642aa0863d144448b43e64dbae23cc319 100644 (file)
@@ -3,7 +3,7 @@
 #include <errno.h>
 #include <string.h>
 #include <unistd.h>
-#include <sd-bus.h>
+#include <systemd/sd-bus.h>
 
 int writer_with_negative_errno_return(int fd, sd_bus_error *error) {
   const char *message = "Hello, World!\n";
@@ -14,5 +14,5 @@ int writer_with_negative_errno_return(int fd, sd_bus_error *error) {
 
   /* On error, initialize the error structure, and also propagate the errno
    * value that write(2) set for us. */
-  return sd_bus_error_set_errnof(error, errno, "Failed to write to fd %i: %m", fd);
+  return sd_bus_error_set_errnof(error, errno, "Failed to write to fd %i: %s", fd, strerror(errno));
 }
index c0818689f29be9d754dbe3091c0856d47e592f16..fc7c3b1a724a29924350f2882f5f20512151e6c6 100644 (file)
 
 #define _cleanup_(f) __attribute__((cleanup(f)))
 
-#define check(x) ({                             \
-  int _r = (x);                                 \
-  errno = _r < 0 ? -_r : 0;                     \
-  printf(#x ": %m\n");                          \
-  if (_r < 0)                                   \
-    return EXIT_FAILURE;                        \
-  })
+static int log_error(int r, const char *str) {
+  fprintf(stderr, "%s failed: %s\n", str, strerror(-r));
+  return r;
+}
 
 typedef struct object {
   const char *example;
@@ -90,13 +87,24 @@ static const sd_bus_vtable vtable[] = {
 static int setup(object *o);
 
 static int on_disconnect(sd_bus_message *message, void *userdata, sd_bus_error *ret_error) {
-  check(setup((object *)userdata));
-  return 0;
+  int r;
+
+  r = setup((object *)userdata);
+  if (r < 0) {
+    object *o = userdata;
+    r = sd_event_exit(*o->event, r);
+    if (r < 0)
+      return log_error(r, "sd_event_exit()");
+  }
+
+  return 1;
 }
 
 /* Ensure the event loop exits with a clear error if acquiring the well-known
  * service name fails */
 static int request_name_callback(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) {
+  int r;
+
   if (!sd_bus_message_is_method_error(m, NULL))
     return 1;
 
@@ -105,21 +113,27 @@ static int request_name_callback(sd_bus_message *m, void *userdata, sd_bus_error
   if (sd_bus_error_has_names(error, SD_BUS_ERROR_TIMEOUT, SD_BUS_ERROR_NO_REPLY))
     return 1; /* The bus is not available, try again later */
 
-  printf("Failed to request name: %s\n", error->message);
+  fprintf(stderr, "Failed to request name: %s\n", error->message);
   object *o = userdata;
-  check(sd_event_exit(*o->event, -sd_bus_error_get_errno(error)));
+  r = sd_event_exit(*o->event, -sd_bus_error_get_errno(error));
+  if (r < 0)
+    return log_error(r, "sd_event_exit()");
 
   return 1;
 }
 
 static int setup(object *o) {
+  int r;
+
   /* If we are reconnecting, then the bus object needs to be closed, detached
    * from the event loop and recreated.
    * https://www.freedesktop.org/software/systemd/man/sd_bus_detach_event.html
    * https://www.freedesktop.org/software/systemd/man/sd_bus_close_unref.html
    */
   if (*o->bus) {
-    check(sd_bus_detach_event(*o->bus));
+    r = sd_bus_detach_event(*o->bus);
+    if (r < 0)
+      return log_error(r, "sd_bus_detach_event()");
     *o->bus = sd_bus_close_unref(*o->bus);
   }
 
@@ -135,55 +149,75 @@ static int setup(object *o) {
    * https://www.freedesktop.org/software/systemd/man/sd_bus_set_connected_signal.html
    * https://www.freedesktop.org/software/systemd/man/sd_bus_start.html
    */
-  check(sd_bus_new(o->bus));
-  check(sd_bus_set_address(*o->bus, "unix:path=/run/dbus/system_bus_socket"));
-  check(sd_bus_set_bus_client(*o->bus, 1));
-  check(sd_bus_negotiate_creds(*o->bus, 1, SD_BUS_CREDS_UID|SD_BUS_CREDS_EUID|SD_BUS_CREDS_EFFECTIVE_CAPS));
-  check(sd_bus_set_watch_bind(*o->bus, 1));
-  check(sd_bus_start(*o->bus));
+  r = sd_bus_new(o->bus);
+  if (r < 0)
+    return log_error(r, "sd_bus_new()");
+  r = sd_bus_set_address(*o->bus, "unix:path=/run/dbus/system_bus_socket");
+  if (r < 0)
+    return log_error(r, "sd_bus_set_address()");
+  r = sd_bus_set_bus_client(*o->bus, 1);
+  if (r < 0)
+    return log_error(r, "sd_bus_set_bus_client()");
+  r = sd_bus_negotiate_creds(*o->bus, 1, SD_BUS_CREDS_UID|SD_BUS_CREDS_EUID|SD_BUS_CREDS_EFFECTIVE_CAPS);
+  if (r < 0)
+    return log_error(r, "sd_bus_negotiate_creds()");
+  r = sd_bus_set_watch_bind(*o->bus, 1);
+  if (r < 0)
+    return log_error(r, "sd_bus_set_watch_bind()");
+  r = sd_bus_start(*o->bus);
+  if (r < 0)
+    return log_error(r, "sd_bus_start()");
 
   /* Publish an interface on the bus, specifying our well-known object access
    * path and public interface name.
    * https://www.freedesktop.org/software/systemd/man/sd_bus_add_object.html
    * https://dbus.freedesktop.org/doc/dbus-tutorial.html
    */
-  check(sd_bus_add_object_vtable(*o->bus,
-                                 NULL,
-                                 "/org/freedesktop/ReconnectExample",
-                                 "org.freedesktop.ReconnectExample",
-                                 vtable,
-                                 o));
+  r = sd_bus_add_object_vtable(*o->bus,
+                               NULL,
+                               "/org/freedesktop/ReconnectExample",
+                               "org.freedesktop.ReconnectExample",
+                               vtable,
+                               o);
+  if (r < 0)
+    return log_error(r, "sd_bus_add_object_vtable()");
   /* By default the service is only assigned an ephemeral name. Also add a
    * well-known one, so that clients know whom to call. This needs to be
    * asynchronous, as D-Bus might not be yet available. The callback will check
    * whether the error is expected or not, in case it fails.
    * https://www.freedesktop.org/software/systemd/man/sd_bus_request_name.html
    */
-  check(sd_bus_request_name_async(*o->bus,
-                                  NULL,
-                                  "org.freedesktop.ReconnectExample",
-                                  0,
-                                  request_name_callback,
-                                  o));
+  r = sd_bus_request_name_async(*o->bus,
+                                NULL,
+                                "org.freedesktop.ReconnectExample",
+                                0,
+                                request_name_callback,
+                                o);
+  if (r < 0)
+    return log_error(r, "sd_bus_request_name_async()");
   /* When D-Bus is disconnected this callback will be invoked, which will set up
    * the connection again. This needs to be asynchronous, as D-Bus might not yet
    * be available.
    * https://www.freedesktop.org/software/systemd/man/sd_bus_match_signal_async.html
    */
-  check(sd_bus_match_signal_async(*o->bus,
-                                  NULL,
-                                  "org.freedesktop.DBus.Local",
-                                  NULL,
-                                  "org.freedesktop.DBus.Local",
-                                  "Disconnected",
-                                  on_disconnect,
-                                  NULL,
-                                  o));
+  r = sd_bus_match_signal_async(*o->bus,
+                                NULL,
+                                "org.freedesktop.DBus.Local",
+                                NULL,
+                                "org.freedesktop.DBus.Local",
+                                "Disconnected",
+                                on_disconnect,
+                                NULL,
+                                o);
+  if (r < 0)
+    return log_error(r, "sd_bus_match_signal_async()");
   /* Attach the bus object to the event loop so that calls and signals are
    * processed.
    * https://www.freedesktop.org/software/systemd/man/sd_bus_attach_event.html
    */
-  check(sd_bus_attach_event(*o->bus, *o->event, 0));
+  r = sd_bus_attach_event(*o->bus, *o->event, 0);
+  if (r < 0)
+    return log_error(r, "sd_bus_attach_event()");
 
   return 0;
 }
@@ -199,28 +233,42 @@ int main(int argc, char **argv) {
     .bus = &bus,
     .event = &event,
   };
+  int r;
 
   /* Create an event loop data structure, with default parameters.
    * https://www.freedesktop.org/software/systemd/man/sd_event_default.html
    */
-  check(sd_event_default(&event));
+  r = sd_event_default(&event);
+  if (r < 0)
+    return log_error(r, "sd_event_default()");
 
   /* By default the event loop will terminate when all sources have disappeared,
    * so we have to keep it 'occupied'. Register signal handling to do so.
    * https://www.freedesktop.org/software/systemd/man/sd_event_add_signal.html
    */
-  check(sd_event_add_signal(event, NULL, SIGINT|SD_EVENT_SIGNAL_PROCMASK, NULL, NULL));
-  check(sd_event_add_signal(event, NULL, SIGTERM|SD_EVENT_SIGNAL_PROCMASK, NULL, NULL));
+  r = sd_event_add_signal(event, NULL, SIGINT|SD_EVENT_SIGNAL_PROCMASK, NULL, NULL);
+  if (r < 0)
+    return log_error(r, "sd_event_add_signal(SIGINT)");
+
+  r = sd_event_add_signal(event, NULL, SIGTERM|SD_EVENT_SIGNAL_PROCMASK, NULL, NULL);
+  if (r < 0)
+    return log_error(r, "sd_event_add_signal(SIGTERM)");
 
-  check(setup(&o));
+  r = setup(&o);
+  if (r < 0)
+    return EXIT_FAILURE;
 
   /* Enter the main loop, it will exit only on sigint/sigterm.
    * https://www.freedesktop.org/software/systemd/man/sd_event_loop.html
    */
-  check(sd_event_loop(event));
+  r = sd_event_loop(event);
+  if (r < 0)
+    return log_error(r, "sd_event_loop()");
 
   /* https://www.freedesktop.org/software/systemd/man/sd_bus_release_name.html */
-  check(sd_bus_release_name(bus, "org.freedesktop.ReconnectExample"));
+  r = sd_bus_release_name(bus, "org.freedesktop.ReconnectExample");
+  if (r < 0)
+    return log_error(r, "sd_bus_release_name()");
 
   return 0;
 }
index 1e611fe6d86e0cf37872720070e54209c6c899b6..e1c4073e7fc3292e47b6d0c67f1df5a96ac6edfa 100644 (file)
     privileged port (i.e.: lower than 1024), as an attempt to address concerns that unprivileged processes in
     the guest might try to send malicious notifications to the host, driving it to make destructive decisions
     based on them.</para>
+
+    <para>Note that, while using this library should be preferred in order to avoid code duplication, it is
+    also possible to reimplement the simple readiness notification protocol without external dependencies,
+    as demonstrated in the following self-contained example:
+    <programlisting><xi:include href="notify-selfcontained-example.c" parse="text"/></programlisting></para>
   </refsect1>
 
   <refsect1>
 
       <programlisting>
 sd_notifyf(0, "READY=1\n"
-           "STATUS=Processing requests…\n"
-           "MAINPID=%lu",
+              "STATUS=Processing requests…\n"
+              "MAINPID=%lu",
            (unsigned long) getpid());</programlisting>
     </example>
 
@@ -540,7 +545,7 @@ sd_notifyf(0, "READY=1\n"
 
       <programlisting>
 sd_notifyf(0, "STATUS=Failed to start up: %s\n"
-           "ERRNO=%i",
+              "ERRNO=%i",
            strerror_r(errnum, (char[1024]){}, 1024),
            errnum);</programlisting>
     </example>
index aaafa98bb784ef8eb0f990d850ce0264bf09995b..d181f90b8378b35d9cb1b01109076b363affe521 100644 (file)
@@ -36,7 +36,7 @@
   </varlistentry>
 
   <varlistentry id='legend'>
-    <term><option>--legend=</option><replaceable>BOOL</replaceable></term>
+    <term><option>--legend=<replaceable>BOOL</replaceable></option></term>
 
     <listitem>
       <para>Enable or disable printing of the legend, i.e. column headers and the footer with hints. The
@@ -73,7 +73,7 @@
   </varlistentry>
 
   <varlistentry id='json'>
-    <term><option>--json=</option><replaceable>MODE</replaceable></term>
+    <term><option>--json=<replaceable>MODE</replaceable></option></term>
 
     <listitem><para>Shows output formatted as JSON. Expects one of <literal>short</literal> (for the
     shortest possible output without any redundant whitespace or line breaks), <literal>pretty</literal>
index ce560bf2e5bc4a4e9b38ba4c34e4a9bb80fe5b7b..ca101d5b75cd937cd1f73a055812d4aae47eccfe 100644 (file)
@@ -2418,7 +2418,7 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
       </varlistentry>
 
       <varlistentry>
-        <term><option>--kill-value=</option><replaceable>INT</replaceable></term>
+        <term><option>--kill-value=<replaceable>INT</replaceable></option></term>
 
         <listitem><para>If used with the <command>kill</command> command, enqueues a signal along with the
         specified integer value parameter to the specified process(es). This operation is only available for
@@ -2766,7 +2766,7 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
       </varlistentry>
 
       <varlistentry>
-        <term><option>--drop-in=</option><replaceable>NAME</replaceable></term>
+        <term><option>--drop-in=<replaceable>NAME</replaceable></option></term>
 
         <listitem><para>When used with <command>edit</command>, use <replaceable>NAME</replaceable> as the
         drop-in file name instead of <filename>override.conf</filename>.</para>
index ca108c5cf9d35f00c45d13764266e18d17d985e1..9a4b0cada41acd88b8a583ee1967ecf40fc7ac13 100644 (file)
@@ -1056,7 +1056,7 @@ x86-64      native</programlisting>
       </varlistentry>
 
       <varlistentry>
-        <term><option>--fuzz=</option><replaceable>timespan</replaceable></term>
+        <term><option>--fuzz=<replaceable>timespan</replaceable></option></term>
 
         <listitem><para>When used in conjunction with the
         <command>critical-chain</command> command (see above), also
index e58d64c52ba699152ab23076937f8987bb5c439a..5ef10c2c0722f51bad184e843bb66e02e3cdd2c8 100644 (file)
@@ -58,7 +58,7 @@
       </varlistentry>
 
       <varlistentry>
-        <term><option>--tty=</option><replaceable></replaceable></term>
+        <term><option>--tty=<replaceable></replaceable></option></term>
 
         <listitem><para>Specify the TTY to output to. By default <command>systemd-bsod</command> will
         automatically find a free VT to display the message on. If this option is specified a TTY may be
index 169d1a1da23e3d72bac8eae106fbafcc217e76a4..c8419d357a14bdb74ec2ec21712a49935adb65bf 100644 (file)
       </varlistentry>
 
       <varlistentry>
-        <term><option>--name=</option><replaceable>name</replaceable></term>
+        <term><option>--name=<replaceable>name</replaceable></option></term>
 
         <listitem><para>When specified with the <command>encrypt</command> command controls the credential
         name to embed in the encrypted credential data. If not specified the name is chosen automatically
       </varlistentry>
 
       <varlistentry>
-        <term><option>--timestamp=</option><replaceable>timestamp</replaceable></term>
+        <term><option>--timestamp=<replaceable>timestamp</replaceable></option></term>
 
         <listitem><para>When specified with the <command>encrypt</command> command controls the timestamp to
         embed into the encrypted credential. Defaults to the current time. Takes a timestamp specification in
       </varlistentry>
 
       <varlistentry>
-        <term><option>--not-after=</option><replaceable>timestamp</replaceable></term>
+        <term><option>--not-after=<replaceable>timestamp</replaceable></option></term>
 
         <listitem><para>When specified with the <command>encrypt</command> command controls the time when the
         credential shall not be used anymore. This embeds the specified timestamp in the encrypted
       </varlistentry>
 
       <varlistentry>
-        <term><option>--tpm2-device=</option><replaceable>PATH</replaceable></term>
+        <term><option>--tpm2-device=<replaceable>PATH</replaceable></option></term>
 
         <listitem><para>Controls the TPM2 device to use. Expects a device node path referring to the TPM2
         chip (e.g. <filename>/dev/tpmrm0</filename>). Alternatively the special value <literal>auto</literal>
       </varlistentry>
 
       <varlistentry>
-        <term><option>--tpm2-pcrs=</option><replaceable>PCR<optional>+PCR...</optional></replaceable></term>
+        <term><option>--tpm2-pcrs=<replaceable>PCR<optional>+PCR...</optional></replaceable></option></term>
 
         <listitem><para>Configures the TPM2 PCRs (Platform Configuration Registers) to bind the encryption
         key to. Takes a <literal>+</literal> separated list of numeric PCR indexes in the range 0…23. If not
       </varlistentry>
 
       <varlistentry>
-        <term><option>--tpm2-public-key=</option><replaceable>PATH</replaceable></term>
-        <term><option>--tpm2-public-key-pcrs=</option><replaceable>PCR<optional>+PCR...</optional></replaceable></term>
+        <term><option>--tpm2-public-key=<replaceable>PATH</replaceable></option></term>
+        <term><option>--tpm2-public-key-pcrs=<replaceable>PCR<optional>+PCR...</optional></replaceable></option></term>
 
         <listitem><para>Configures a TPM2 signed PCR policy to bind encryption to, for use with the
         <command>encrypt</command> command. The <option>--tpm2-public-key=</option> option accepts a path to
       </varlistentry>
 
       <varlistentry>
-        <term><option>--tpm2-signature=</option><replaceable>PATH</replaceable></term>
+        <term><option>--tpm2-signature=<replaceable>PATH</replaceable></option></term>
 
         <listitem><para>Takes a path to a TPM2 PCR signature file as generated by the
         <citerefentry><refentrytitle>systemd-measure</refentrytitle><manvolnum>1</manvolnum></citerefentry>
         <xi:include href="version-info.xml" xpointer="v252"/></listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><option>--allow-null</option></term>
+
+        <listitem><para>Allow decrypting credentials that use an empty key.</para>
+
+        <xi:include href="version-info.xml" xpointer="v256"/></listitem>
+      </varlistentry>
+
       <varlistentry>
         <term><option>--quiet</option></term>
         <term><option>-q</option></term>
index c687ac31bb1daf2bf2dabce877732ca5c6c910ba..a3a2c610eeb2375f9669777af02d1ed853d2d757 100644 (file)
       </varlistentry>
 
       <varlistentry>
-        <term><option>--unlock-key-file=</option><replaceable>PATH</replaceable></term>
+        <term><option>--unlock-key-file=<replaceable>PATH</replaceable></option></term>
 
         <listitem><para>Use a file instead of a password/passphrase read from stdin to unlock the volume.
         Expects the PATH to the file containing your key to unlock the volume. Currently there is nothing like
       </varlistentry>
 
       <varlistentry>
-        <term><option>--unlock-fido2-device=</option><replaceable>PATH</replaceable></term>
+        <term><option>--unlock-fido2-device=<replaceable>PATH</replaceable></option></term>
 
         <listitem><para>Use a FIDO2 device instead of a password/passphrase read from stdin to unlock the
         volume. Expects a <filename>hidraw</filename> device referring to the FIDO2 device (e.g.
       </varlistentry>
 
       <varlistentry>
-        <term><option>--unlock-tpm2-device=</option><replaceable>PATH</replaceable></term>
+        <term><option>--unlock-tpm2-device=<replaceable>PATH</replaceable></option></term>
 
         <listitem><para>Use a TPM2 device instead of a password/passhprase read from stdin to unlock the
         volume. Expects a device node path referring to the TPM2 chip (e.g. <filename>/dev/tpmrm0</filename>).
       </varlistentry>
 
       <varlistentry>
-        <term><option>--pkcs11-token-uri=</option><replaceable>URI</replaceable></term>
+        <term><option>--pkcs11-token-uri=<replaceable>URI</replaceable></option></term>
 
         <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 or a public key on the token. The URI must also be suitable
       </varlistentry>
 
       <varlistentry>
-        <term><option>--fido2-credential-algorithm=</option><replaceable>STRING</replaceable></term>
+        <term><option>--fido2-credential-algorithm=<replaceable>STRING</replaceable></option></term>
         <listitem><para>Specify COSE algorithm used in credential generation. The default value is
         <literal>es256</literal>. Supported values are <literal>es256</literal>, <literal>rs256</literal>
         and <literal>eddsa</literal>.</para>
       </varlistentry>
 
       <varlistentry>
-        <term><option>--fido2-device=</option><replaceable>PATH</replaceable></term>
+        <term><option>--fido2-device=<replaceable>PATH</replaceable></option></term>
 
         <listitem><para>Enroll a FIDO2 security token that implements the <literal>hmac-secret</literal>
         extension (e.g. a YubiKey). Expects a <filename>hidraw</filename> device referring to the FIDO2
       </varlistentry>
 
       <varlistentry>
-        <term><option>--fido2-with-client-pin=</option><replaceable>BOOL</replaceable></term>
+        <term><option>--fido2-with-client-pin=<replaceable>BOOL</replaceable></option></term>
 
         <listitem><para>When enrolling a FIDO2 security token, controls whether to require the user to enter
         a PIN when unlocking the volume (the FIDO2 <literal>clientPin</literal> feature). Defaults to
       </varlistentry>
 
       <varlistentry>
-        <term><option>--fido2-with-user-presence=</option><replaceable>BOOL</replaceable></term>
+        <term><option>--fido2-with-user-presence=<replaceable>BOOL</replaceable></option></term>
 
         <listitem><para>When enrolling a FIDO2 security token, controls whether to require the user to
         verify presence (tap the token, the FIDO2 <literal>up</literal> feature) when unlocking the volume.
       </varlistentry>
 
       <varlistentry>
-        <term><option>--fido2-with-user-verification=</option><replaceable>BOOL</replaceable></term>
+        <term><option>--fido2-with-user-verification=<replaceable>BOOL</replaceable></option></term>
 
         <listitem><para>When enrolling a FIDO2 security token, controls whether to require user verification
         when unlocking the volume (the FIDO2 <literal>uv</literal> feature). Defaults to
       </varlistentry>
 
       <varlistentry>
-        <term><option>--tpm2-device=</option><replaceable>PATH</replaceable></term>
+        <term><option>--tpm2-device=<replaceable>PATH</replaceable></option></term>
 
         <listitem><para>Enroll a TPM2 security chip. Expects a device node path referring to the TPM2 chip
         (e.g. <filename>/dev/tpmrm0</filename>). Alternatively the special value <literal>auto</literal> may
       </varlistentry>
 
       <varlistentry>
-        <term><option>--tpm2-device-key=</option><replaceable>PATH</replaceable></term>
+        <term><option>--tpm2-device-key=<replaceable>PATH</replaceable></option></term>
 
         <listitem><para>Enroll a TPM2 security chip using its public key. Expects a path referring to the
         TPM2 public key in TPM2B_PUBLIC format. This cannot be used with <option>--tpm2-device=</option>, as
       </varlistentry>
 
       <varlistentry>
-        <term><option>--tpm2-seal-key-handle=</option><replaceable>HANDLE</replaceable></term>
+        <term><option>--tpm2-seal-key-handle=<replaceable>HANDLE</replaceable></option></term>
 
         <listitem><para>Configures which parent key to use for sealing, using the TPM handle (index) of the
         key. This is used to "seal" (encrypt) a secret and must be used later to "unseal" (decrypt) the
       </varlistentry>
 
       <varlistentry>
-        <term><option>--tpm2-pcrs=</option><replaceable>PCR<optional>+PCR...</optional></replaceable></term>
+        <term><option>--tpm2-pcrs=<replaceable>PCR<optional>+PCR...</optional></replaceable></option></term>
 
         <listitem><para>Configures the TPM2 PCRs (Platform Configuration Registers) to bind to when
         enrollment is requested via <option>--tpm2-device=</option>. Takes a list of PCR entries, where each
       </varlistentry>
 
       <varlistentry>
-        <term><option>--tpm2-with-pin=</option><replaceable>BOOL</replaceable></term>
+        <term><option>--tpm2-with-pin=<replaceable>BOOL</replaceable></option></term>
 
         <listitem><para>When enrolling a TPM2 device, controls whether to require the user to enter a PIN
         when unlocking the volume in addition to PCR binding, based on TPM2 policy authentication. Defaults
       </varlistentry>
 
       <varlistentry>
-        <term><option>--tpm2-public-key=</option><replaceable>PATH</replaceable></term>
-        <term><option>--tpm2-public-key-pcrs=</option><replaceable>PCR<optional>+PCR...</optional></replaceable></term>
-        <term><option>--tpm2-signature=</option><replaceable>PATH</replaceable></term>
+        <term><option>--tpm2-public-key=<replaceable>PATH</replaceable></option></term>
+        <term><option>--tpm2-public-key-pcrs=<replaceable>PCR<optional>+PCR...</optional></replaceable></option></term>
+        <term><option>--tpm2-signature=<replaceable>PATH</replaceable></option></term>
 
         <listitem><para>Configures a TPM2 signed PCR policy to bind encryption to. The
         <option>--tpm2-public-key=</option> option accepts a path to a PEM encoded RSA public key, to bind
       </varlistentry>
 
       <varlistentry>
-        <term><option>--tpm2-pcrlock=</option><replaceable>PATH</replaceable></term>
+        <term><option>--tpm2-pcrlock=<replaceable>PATH</replaceable></option></term>
 
         <listitem><para>Configures a TPM2 pcrlock policy to bind encryption to. Expects a path to a pcrlock
         policy file as generated by the
       </varlistentry>
 
       <varlistentry>
-        <term><option>--wipe-slot=</option><replaceable>SLOT<optional>,SLOT...</optional></replaceable></term>
+        <term><option>--wipe-slot=<replaceable>SLOT<optional>,SLOT...</optional></replaceable></option></term>
 
         <listitem><para>Wipes one or more LUKS2 key slots. Takes a comma separated list of numeric slot
         indexes, or the special strings <literal>all</literal> (for wiping all key slots),
index 5d1d5b7e605086e27e0c36924aaf999095e4d861..af3615175e58b46ce4649e49322e2946b2850cb0 100644 (file)
@@ -6,7 +6,7 @@
 %entities;
 ]>
 <!-- SPDX-License-Identifier: LGPL-2.1-or-later -->
-<refentry id="systemd-debug-generator">
+<refentry id="systemd-debug-generator" xmlns:xi="http://www.w3.org/2001/XInclude">
 
   <refentryinfo>
     <title>systemd-debug-generator</title>
     <citerefentry><refentrytitle>systemd.generator</refentrytitle><manvolnum>7</manvolnum></citerefentry>.</para>
   </refsect1>
 
+  <refsect1>
+    <title>System Credentials</title>
+
+    <variablelist class='system-credentials'>
+      <varlistentry>
+        <term><varname>systemd.extra-unit.*</varname></term>
+
+        <listitem><para>Credentials prefixed with <literal>systemd.extra-unit.</literal> specify additional
+        units to add to the final system. Note that these additional units are added to both the initrd and
+        the final system. <varname>ConditionPathExists=!/etc/initrd-release</varname> can be used to make
+        sure the unit is conditioned out in the initrd.</para>
+
+        <xi:include href="version-info.xml" xpointer="v256"/></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>systemd.unit-dropin.*</varname></term>
+
+        <listitem><para>Credentials prefixed with <literal>systemd.unit-dropin.</literal> add drop-ins for
+        the corresponding units in the final system. Each credential must be suffixed with the full unit name
+        including the unit extension. Its contents must be a valid unit drop-in file. Only one drop-in per
+        unit can be specified. The name of the generated drop-in will be
+        <literal>50-credential.conf</literal>. Note that these additional drop-ins are added to both the
+        initrd and the final system.</para>
+
+        <xi:include href="version-info.xml" xpointer="v256"/></listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+
   <refsect1>
     <title>See Also</title>
     <para><simplelist type="inline">
index f6cdefdd3a0fb6fe3e7b6533080ead8daa581adf..c0c545cc9cf7342d14d0bca0941f59eab98abc14 100644 (file)
 
   <refnamediv>
     <refname>systemd-hibernate-resume.service</refname>
+    <refname>systemd-hibernate-clear.service</refname>
     <refname>systemd-hibernate-resume</refname>
     <refpurpose>Resume from hibernation</refpurpose>
   </refnamediv>
 
   <refsynopsisdiv>
     <para><filename>systemd-hibernate-resume.service</filename></para>
+    <para><filename>systemd-hibernate-clear.service</filename></para>
     <para><filename>/usr/lib/systemd/systemd-hibernate-resume</filename></para>
   </refsynopsisdiv>
 
     <filename>/sys/power/resume</filename>, along with the offset in memory pages
     (<filename>/sys/power/resume_offset</filename>) if supported.</para>
 
+    <para>The resume device node is either passed directly through arguments, or automatically acquired
+    from kernel command line options and/or <varname>HibernateLocation</varname> EFI variable. The latter
+    will normally be cleared by <filename>systemd-hibernate-resume.service</filename> on resumption.
+    If a stale variable is detected, it would be cleared by
+    <filename>systemd-hibernate-clear.service</filename>.</para>
+
     <para>Failing to initiate a resume is not an error condition. It may mean that there was
     no resume image (e. g. if the system has been simply powered off and not hibernated).
     In such cases, the boot is ordinarily continued.</para>
index c979ce2f4643a930092b32ee330af486e0f437aa..1707e0c7f3b3449cf9862c880e056151cd2653bf 100644 (file)
       </varlistentry>
 
       <varlistentry>
-        <term><option>--tpm2-device=</option><replaceable>PATH</replaceable></term>
+        <term><option>--tpm2-device=<replaceable>PATH</replaceable></option></term>
 
         <listitem><para>Controls which TPM2 device to use. Expects a device node path referring to the TPM2
         chip (e.g. <filename>/dev/tpmrm0</filename>). Alternatively the special value <literal>auto</literal>
       </varlistentry>
 
       <varlistentry>
-        <term><option>--phase=</option><replaceable>PHASE</replaceable></term>
+        <term><option>--phase=<replaceable>PHASE</replaceable></option></term>
 
         <listitem><para>Controls which boot phases to calculate expected PCR 11 values for. This takes a
         series of colon-separated strings that encode boot "paths" for entering a specific phase of the boot
       </varlistentry>
 
       <varlistentry>
-        <term><option>--append=</option><replaceable>PATH</replaceable></term>
+        <term><option>--append=<replaceable>PATH</replaceable></option></term>
 
         <listitem><para>When generating a PCR JSON signature (via the <command>sign</command> command),
         combine it with a previously generated PCR JSON signature, and output it as one. The specified path
diff --git a/man/systemd-mountfsd.service.xml b/man/systemd-mountfsd.service.xml
new file mode 100644 (file)
index 0000000..25c1568
--- /dev/null
@@ -0,0 +1,70 @@
+<?xml version='1.0'?> <!--*-nxml-*-->
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
+  "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+<!-- SPDX-License-Identifier: LGPL-2.1-or-later -->
+
+<refentry id="systemd-mountfsd.service" conditional='ENABLE_MOUNTFSD'>
+
+  <refentryinfo>
+    <title>systemd-mountfsd.service</title>
+    <productname>systemd</productname>
+  </refentryinfo>
+
+  <refmeta>
+    <refentrytitle>systemd-mountfsd.service</refentrytitle>
+    <manvolnum>8</manvolnum>
+  </refmeta>
+
+  <refnamediv>
+    <refname>systemd-mountfsd.service</refname>
+    <refname>systemd-mountfsd</refname>
+    <refpurpose>Disk Image File System Mount Service</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv>
+    <para><filename>systemd-mountfsd.service</filename></para>
+    <para><filename>/usr/lib/systemd/systemd-mountfsd</filename></para>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>Description</title>
+
+    <para><command>systemd-mountfsd</command> is a system service that dissects disk images, and returns mount
+    file descriptors for the file systems contained therein to clients, via a Varlink IPC API.</para>
+
+    <para>The disk images provided must contain a raw file system image or must follow the <ulink
+    url="https://uapi-group.org/specifications/specs/discoverable_partitions_specification/">Discoverable
+    Partitions Specification</ulink>. Before mounting any file systems authenticity of the disk image is
+    established in one or a combination of the following ways:</para>
+
+    <orderedlist>
+      <listitem><para>If the disk image is located in a regular file in one of the directories
+      <filename>/var/lib/machines/</filename>, <filename>/var/lib/portables/</filename>,
+      <filename>/var/lib/extensions/</filename>, <filename>/var/lib/confexts/</filename> or their
+      counterparts in the <filename>/etc/</filename>, <filename>/run/</filename>,
+      <filename>/usr/lib/</filename> it is assumed to be trusted.</para></listitem>
+
+      <listitem><para>If the disk image contains a Verity enabled disk image, along with a signature
+      partition with a key in the kernel keyring or in <filename>/etc/verity.d/</filename> (and related
+      directories) the disk image is considered trusted.</para></listitem>
+    </orderedlist>
+
+    <para>This service provides one <ulink url="https://varlink.org/">Varlink</ulink> service:
+    <constant>io.systemd.MountFileSystem</constant> which accepts a file descriptor to a regular file or
+    block device, and returns a number of file descriptors referring to an <function>fsmount()</function>
+    file descriptor the client may then attach to a path of their choice.</para>
+
+    <para>The returned mounts are automatically allowlisted in the per-user-namespace allowlist maintained by
+    <citerefentry><refentrytitle>systemd-nsresourced.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>.</para>
+
+    <para>The file systems are automatically fsck'ed before mounting.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>See Also</title>
+    <para>
+      <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>systemd-nsresourced.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+    </para>
+  </refsect1>
+</refentry>
index 59d608028a70a0659859103356ea2f2ca6558a12..8962f1a05f3e0a365558969ada816a0b3027959e 100644 (file)
 
     <variablelist class='system-credentials'>
       <varlistentry>
-        <term><varname>network.netdev.*</varname></term>
+        <term><varname>network.conf.*</varname></term>
         <term><varname>network.link.*</varname></term>
+        <term><varname>network.netdev.*</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>networkd.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
         <citerefentry><refentrytitle>systemd.link</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
+        <citerefentry><refentrytitle>systemd.netdev</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
index eac903f684226608640920ec0b8133949f84e413..e696a2ba9a1cd6d346fcf51a4f0bb80d1434aaed 100644 (file)
@@ -61,7 +61,7 @@
     <variablelist>
       <varlistentry>
         <term><option>-i</option> <replaceable>INTERFACE</replaceable><optional>:<replaceable>MIN_OPERSTATE</replaceable><optional>:<replaceable>MAX_OPERSTATE</replaceable></optional></optional></term>
-        <term><option>--interface=</option><replaceable>INTERFACE</replaceable><optional>:<replaceable>MIN_OPERSTATE</replaceable><optional>:<replaceable>MAX_OPERSTATE</replaceable></optional></optional></term>
+        <term><option>--interface=<replaceable>INTERFACE</replaceable><optional>:<replaceable>MIN_OPERSTATE</replaceable><optional>:<replaceable>MAX_OPERSTATE</replaceable></optional></optional></option></term>
 
         <listitem><para>Network interface to wait for before deciding if the system is online. This
         is useful when a system has several interfaces which will be configured, but a particular
@@ -80,7 +80,7 @@
       </varlistentry>
 
       <varlistentry>
-        <term><option>--ignore=</option><replaceable>INTERFACE</replaceable></term>
+        <term><option>--ignore=<replaceable>INTERFACE</replaceable></option></term>
 
         <listitem><para>Network interfaces to be ignored when deciding
         if the system is online. By default, only the loopback
@@ -92,7 +92,7 @@
 
       <varlistentry>
         <term><option>-o</option> <replaceable>MIN_OPERSTATE</replaceable><optional>:<replaceable>MAX_OPERSTATE</replaceable></optional></term>
-        <term><option>--operational-state=</option><replaceable>MIN_OPERSTATE</replaceable><optional>:<replaceable>MAX_OPERSTATE</replaceable></optional></term>
+        <term><option>--operational-state=<replaceable>MIN_OPERSTATE</replaceable><optional>:<replaceable>MAX_OPERSTATE</replaceable></optional></option></term>
 
         <listitem><para>Takes a minimum operational state and an optional maximum operational state.
         Please see <citerefentry><refentrytitle>networkctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>
       </varlistentry>
 
       <varlistentry>
-        <term><option>--timeout=</option><replaceable>SECS</replaceable></term>
+        <term><option>--timeout=<replaceable>SECS</replaceable></option></term>
 
         <listitem><para>Fail the service if the network is not online
         by the time the timeout elapses. A timeout of 0 disables the
index edbd0b9bf51b007173069b57c34a7f3c00363f55..55bb8c59cf3ddadb4339da35128fcfae6014a66c 100644 (file)
       </varlistentry>
 
       <varlistentry>
-        <term><option>--uid=</option><replaceable>USER</replaceable></term>
+        <term><option>--uid=<replaceable>USER</replaceable></option></term>
 
         <listitem><para>Set the user ID to send the notification from. Takes a UNIX user name or numeric UID. When
         specified the notification message will be sent with the specified UID as sender, in place of the user the
index 4b9c252784f8c13f4f0bdc873965e23d47561293..ec0492c26df9b9d90056d6208abadadbcd474dda 100644 (file)
     keeps track of running containers, and provides programming interfaces to interact with them.</para>
   </refsect1>
 
+  <refsect1>
+    <title>Unprivileged Operation</title>
+
+    <para><command>systemd-nspawn</command> may be invoked with or without privileges. The full functionality
+    is currently only available when invoked with privileges. When invoked without privileges, various
+    limitations apply, including, but not limited to:</para>
+
+    <itemizedlist>
+      <listitem><para>Only disk image based containers are supported (i.e. <option>--image=</option>).
+      Directory based ones (i.e. <option>--directory=</option>) are not supported.</para></listitem>
+
+      <listitem><para>Machine registration via <option>--machine=</option> is not supported.</para></listitem>
+
+      <listitem><para>Only <option>--private-network</option> and <option>--network-veth</option> networking modes are supported.</para></listitem>
+    </itemizedlist>
+
+    <para>When running in unprivileged mode, some needed functionality is provided via
+    <citerefentry><refentrytitle>systemd-mountfsd.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+    and
+    <citerefentry><refentrytitle>systemd-nsresourced.service</refentrytitle><manvolnum>8</manvolnum></citerefentry></para>
+  </refsect1>
+
   <refsect1>
     <title>Options</title>
 
       </varlistentry>
 
       <varlistentry>
-        <term><option>--settings=</option><replaceable>MODE</replaceable></term>
+        <term><option>--settings=<replaceable>MODE</replaceable></option></term>
 
         <listitem><para>Controls whether
         <command>systemd-nspawn</command> shall search for and use
 
       <varlistentry>
         <term><option>--volatile</option></term>
-        <term><option>--volatile=</option><replaceable>MODE</replaceable></term>
+        <term><option>--volatile=<replaceable>MODE</replaceable></option></term>
 
         <listitem><para>Boots the container in volatile mode. When no mode parameter is passed or when mode is
         specified as <option>yes</option>, full volatile mode is enabled. This means the root directory is mounted as a
@@ -1670,7 +1692,7 @@ After=sys-subsystem-net-devices-ens1.device</programlisting>
 
     <variablelist>
       <varlistentry>
-        <term><option>--console=</option><replaceable>MODE</replaceable></term>
+        <term><option>--console=<replaceable>MODE</replaceable></option></term>
 
         <listitem><para>Configures how to set up standard input, output and error output for the container
         payload, as well as the <filename>/dev/console</filename> device for the container. Takes one of
@@ -1736,8 +1758,8 @@ After=sys-subsystem-net-devices-ens1.device</programlisting>
 
       <variablelist>
         <varlistentry>
-          <term><option>--load-credential=</option><replaceable>ID</replaceable>:<replaceable>PATH</replaceable></term>
-          <term><option>--set-credential=</option><replaceable>ID</replaceable>:<replaceable>VALUE</replaceable></term>
+          <term><option>--load-credential=<replaceable>ID</replaceable>:<replaceable>PATH</replaceable></option></term>
+          <term><option>--set-credential=<replaceable>ID</replaceable>:<replaceable>VALUE</replaceable></option></term>
 
           <listitem><para>Pass a credential to the container. These two options correspond to the
           <varname>LoadCredential=</varname> and <varname>SetCredential=</varname> settings in unit files. See
@@ -1910,6 +1932,8 @@ After=sys-subsystem-net-devices-ens1.device</programlisting>
       <member><citerefentry><refentrytitle>systemd.slice</refentrytitle><manvolnum>5</manvolnum></citerefentry></member>
       <member><citerefentry><refentrytitle>machinectl</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
       <member><citerefentry><refentrytitle>importctl</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
+      <member><citerefentry><refentrytitle>systemd-mountfsd.service</refentrytitle><manvolnum>8</manvolnum></citerefentry></member>
+      <member><citerefentry><refentrytitle>systemd-nsresourced.service</refentrytitle><manvolnum>8</manvolnum></citerefentry></member>
       <member><citerefentry project='url'><refentrytitle url='https://btrfs.readthedocs.io/en/latest/btrfs.html'>btrfs</refentrytitle><manvolnum>8</manvolnum></citerefentry></member>
     </simplelist></para>
   </refsect1>
diff --git a/man/systemd-nsresourced.service.xml b/man/systemd-nsresourced.service.xml
new file mode 100644 (file)
index 0000000..d0a561e
--- /dev/null
@@ -0,0 +1,81 @@
+<?xml version='1.0'?> <!--*-nxml-*-->
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
+  "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+<!-- SPDX-License-Identifier: LGPL-2.1-or-later -->
+
+<refentry id="systemd-nsresourced.service" conditional='ENABLE_NSRESOURCED'>
+
+  <refentryinfo>
+    <title>systemd-nsresourced.service</title>
+    <productname>systemd</productname>
+  </refentryinfo>
+
+  <refmeta>
+    <refentrytitle>systemd-nsresourced.service</refentrytitle>
+    <manvolnum>8</manvolnum>
+  </refmeta>
+
+  <refnamediv>
+    <refname>systemd-nsresourced.service</refname>
+    <refname>systemd-nsresourced</refname>
+    <refpurpose>User Namespace Resource Delegation Service</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv>
+    <para><filename>systemd-nsresourced.service</filename></para>
+    <para><filename>/usr/lib/systemd/systemd-nsresourced</filename></para>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>Description</title>
+
+    <para><command>systemd-nsresourced</command> is a system service that permits transient delegation of a a
+    UID/GID range to a user namespace (see <citerefentry
+    project='man-pages'><refentrytitle>user_namespaces</refentrytitle><manvolnum>7</manvolnum></citerefentry>)
+    allocated by a client, via a Varlink IPC API.</para>
+
+    <para>Unprivileged clients may allocate a user namespace, and then request a UID/GID range to be assigned
+    to it via this service. The user namespace may then be used to run containers and other sandboxes, and/or
+    apply it to an id-mapped mount.</para>
+
+    <para>Allocations of UIDs/GIDs this way are transient: when a user namespace goes away, its UID/GID range
+    is returned to the pool of available ranges. In order to ensure that clients cannot gain persistency in
+    their transient UID/GID range a BPF-LSM based policy is enforced that ensures that user namespaces set up
+    this way can only write to file systems they allocate themselves or that are explicitly allowlisted via
+    <command>systemd-nsresourced</command>.</para>
+
+    <para><command>systemd-nsresourced</command> automatically ensures that any registered UID ranges show up
+    in the system's NSS database via the <ulink url="https://systemd.io/USER_GROUP_API">User/Group Record
+    Lookup API via Varlink</ulink>.</para>
+
+    <para>Currently, only UID/GID ranges consisting of either exactly 1 or exactly 65536 UIDs/GIDs can be
+    registered with this service. Moreover, UIDs and GIDs are always allocated together, and
+    symmetrically.</para>
+
+    <para>The service provides API calls to allowlist mounts (referenced via their mount file descriptors as
+    per Linux <function>fsmount()</function> API), to pass ownership of a cgroup subtree to the user
+    namespace and to delegate a virtual Ethernet device pair to the user namespace. When used in combination
+    this is sufficient to implement fully unprivileged container environments, as implemented by
+    <citerefentry><refentrytitle>systemd-nspawn</refentrytitle><manvolnum>1</manvolnum></citerefentry>, fully
+    unprivileged <varname>RootImage=</varname> (see
+    <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry>) or
+    fully unprivileged disk image tools such as
+    <citerefentry><refentrytitle>systemd-dissect</refentrytitle><manvolnum>1</manvolnum></citerefentry>.</para>
+
+    <para>This service provides one <ulink url="https://varlink.org/">Varlink</ulink> service:
+    <constant>io.systemd.NamespaceResource</constant> allows registering user namespaces, and assign mounts,
+    cgroups and network interfaces to it.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>See Also</title>
+    <para>
+      <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>systemd-mountfsd.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>systemd-nspawn</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>systemd-dissect</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+      <citerefentry project='man-pages'><refentrytitle>user_namespaces</refentrytitle><manvolnum>7</manvolnum></citerefentry>
+    </para>
+  </refsect1>
+</refentry>
index e4ee09412731acf9effe479e66dea30ae65b49f6..9c54663d3eb56b1217bab3ae354020c6a6ab4e3f 100644 (file)
       </varlistentry>
 
       <varlistentry>
-        <term><option>--tpm2-device=</option><replaceable>PATH</replaceable></term>
+        <term><option>--tpm2-device=<replaceable>PATH</replaceable></option></term>
 
         <listitem><para>Controls which TPM2 device to use. Expects a device node path referring to the TPM2
         chip (e.g. <filename>/dev/tpmrm0</filename>). Alternatively the special value <literal>auto</literal>
index e719bd9687f66df088b3e24ef1b86f2493a6972b..8f48081097bed0c624a97ec14850e809b0925fcf 100644 (file)
       </varlistentry>
 
       <varlistentry>
-        <term><option>--tpm2-device-key=</option><replaceable>PATH</replaceable></term>
-        <term><option>--tpm2-seal-key-handle=</option><replaceable>HANDLE</replaceable></term>
+        <term><option>--tpm2-device-key=<replaceable>PATH</replaceable></option></term>
+        <term><option>--tpm2-seal-key-handle=<replaceable>HANDLE</replaceable></option></term>
 
         <listitem><para>Configures a TPM2 SRK key to bind encryption to. See
         <citerefentry><refentrytitle>systemd-cryptenroll</refentrytitle><manvolnum>1</manvolnum></citerefentry>
       </varlistentry>
 
       <varlistentry>
-        <term><option>--tpm2-public-key=</option><replaceable>PATH</replaceable></term>
-        <term><option>--tpm2-public-key-pcrs=</option><replaceable>PCR<optional>+PCR...</optional></replaceable></term>
+        <term><option>--tpm2-public-key=<replaceable>PATH</replaceable></option></term>
+        <term><option>--tpm2-public-key-pcrs=<replaceable>PCR<optional>+PCR...</optional></replaceable></option></term>
 
         <listitem><para>Configures a TPM2 signed PCR policy to bind encryption to. See
         <citerefentry><refentrytitle>systemd-cryptenroll</refentrytitle><manvolnum>1</manvolnum></citerefentry>
       </varlistentry>
 
       <varlistentry>
-        <term><option>--tpm2-pcrlock=</option><replaceable>PATH</replaceable></term>
+        <term><option>--tpm2-pcrlock=<replaceable>PATH</replaceable></option></term>
 
         <listitem><para>Configures a TPM2 pcrlock policy to bind encryption to. See
         <citerefentry><refentrytitle>systemd-cryptenroll</refentrytitle><manvolnum>1</manvolnum></citerefentry>
       </varlistentry>
 
       <varlistentry>
-        <term><option>--split=</option><replaceable>BOOL</replaceable></term>
+        <term><option>--split=<replaceable>BOOL</replaceable></option></term>
 
         <listitem><para>Enables generation of split artifacts from partitions configured with
         <varname>SplitName=</varname>. If enabled, for each partition with <varname>SplitName=</varname> set,
       </varlistentry>
 
       <varlistentry>
-        <term><option>--include-partitions=</option><replaceable>PARTITIONS</replaceable></term>
-        <term><option>--exclude-partitions=</option><replaceable>PARTITIONS</replaceable></term>
+        <term><option>--include-partitions=<replaceable>PARTITIONS</replaceable></option></term>
+        <term><option>--exclude-partitions=<replaceable>PARTITIONS</replaceable></option></term>
 
         <listitem><para>These options specify which partition types <command>systemd-repart</command> should
         operate on. If <option>--include-partitions=</option> is used, all partitions that aren't specified
       </varlistentry>
 
       <varlistentry>
-        <term><option>--defer-partitions=</option><replaceable>PARTITIONS</replaceable></term>
+        <term><option>--defer-partitions=<replaceable>PARTITIONS</replaceable></option></term>
 
         <listitem><para>This option specifies for which partition types <command>systemd-repart</command>
         should defer. All partitions that are deferred using this option are still taken into account when
       </varlistentry>
 
       <varlistentry>
-        <term><option>--sector-size=</option><replaceable>BYTES</replaceable></term>
+        <term><option>--sector-size=<replaceable>BYTES</replaceable></option></term>
 
         <listitem><para>This option allows configuring the sector size of the image produced by
         <command>systemd-repart</command>. It takes a value that is a power of <literal>2</literal> between
       </varlistentry>
 
       <varlistentry>
-        <term><option>--architecture=</option><replaceable>ARCH</replaceable></term>
+        <term><option>--architecture=<replaceable>ARCH</replaceable></option></term>
 
         <listitem><para>This option allows overriding the architecture used for architecture specific
         partition types. For example, if set to <literal>arm64</literal> a partition type of
       </varlistentry>
 
       <varlistentry>
-        <term><option>--offline=</option><replaceable>BOOL</replaceable></term>
+        <term><option>--offline=<replaceable>BOOL</replaceable></option></term>
 
         <listitem><para>Instructs <command>systemd-repart</command> to build the image offline. Takes a
         boolean or <literal>auto</literal>. Defaults to <literal>auto</literal>. If enabled, the image is
       </varlistentry>
 
       <varlistentry>
-        <term><option>--copy-from=</option><replaceable>IMAGE</replaceable></term>
+        <term><option>--copy-from=<replaceable>IMAGE</replaceable></option></term>
 
         <listitem><para>Instructs <command>systemd-repart</command> to synthesize partition definitions from
         the partition table in the given image. This option can be specified multiple times to synthesize
       </varlistentry>
 
       <varlistentry>
-        <term><option>--copy-source=</option><replaceable>PATH</replaceable></term>
+        <term><option>--copy-source=<replaceable>PATH</replaceable></option></term>
         <term><option>-s</option> <replaceable>PATH</replaceable></term>
 
         <listitem><para>Specifies a source directory all <varname>CopyFiles=</varname> source paths shall be
       </varlistentry>
 
       <varlistentry>
-        <term><option>--make-ddi=</option><replaceable>TYPE</replaceable></term>
+        <term><option>--make-ddi=<replaceable>TYPE</replaceable></option></term>
 
         <listitem><para>Takes one of <literal>sysext</literal>, <literal>confext</literal> or
         <literal>portable</literal>. Generates a Discoverable Disk Image (DDI) for a system extension
       </varlistentry>
 
       <varlistentry>
-        <term><option>--generate-fstab=</option><replaceable>PATH</replaceable></term>
+        <term><option>--generate-fstab=<replaceable>PATH</replaceable></option></term>
 
         <listitem><para>Specifies a path where to write fstab entries for the mountpoints configured with
         <option>MountPoint=</option> in the root directory specified with <option>--copy-source=</option> or
       </varlistentry>
 
       <varlistentry>
-        <term><option>--generate-crypttab=</option><replaceable>PATH</replaceable></term>
+        <term><option>--generate-crypttab=<replaceable>PATH</replaceable></option></term>
 
         <listitem><para>Specifies a path where to write crypttab entries for the encrypted volumes configured
         with <option>EncryptedVolume=</option> in the root directory specified with
index 411577d519c9592d31d1f0629550f219479177fd..623e614b6ab852b14eca9404fda9f1d3b8b60020 100644 (file)
         <xi:include href="version-info.xml" xpointer="v240"/></listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><varname>SuspendState=</varname></term>
+
+        <listitem><para>The string to be written to <filename>/sys/power/state</filename> by <citerefentry>
+        <refentrytitle>systemd-suspend.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>.
+        More than one value can be specified by separating multiple values with whitespace. They will be
+        tried in turn, until one is written without error. If none of the writes succeed, the operation will
+        be aborted.</para>
+
+        <para>The allowed set of values is determined by the kernel and is shown in the file itself (use
+        <command>cat /sys/power/state</command> to display). See <ulink
+        url="https://docs.kernel.org/admin-guide/pm/sleep-states.html#basic-sysfs-interfaces-for-system-suspend-and-hibernation">
+          Basic sysfs Interfaces for System Suspend and Hibernation</ulink> for more details.</para>
+
+        <para>
+        <citerefentry><refentrytitle>systemd-suspend-then-hibernate.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+        uses this value when suspending.</para>
+
+        <xi:include href="version-info.xml" xpointer="v203"/></listitem>
+      </varlistentry>
+
       <varlistentry>
         <term><varname>HibernateMode=</varname></term>
 
       </varlistentry>
 
       <varlistentry>
-        <term><varname>SuspendState=</varname></term>
+        <term><varname>MemorySleepMode=</varname></term>
 
-        <listitem><para>The string to be written to <filename>/sys/power/state</filename> by <citerefentry>
-        <refentrytitle>systemd-suspend.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>.
+        <listitem><para>The string to be written to <filename>/sys/power/mem_sleep</filename>
+        when <option>SuspendState=mem</option> or <command>hybrid-sleep</command> is used.
         More than one value can be specified by separating multiple values with whitespace. They will be
         tried in turn, until one is written without error. If none of the writes succeed, the operation will
-        be aborted.</para>
+        be aborted. Defaults to empty, i.e. the kernel default or kernel command line option
+        <varname>mem_sleep_default=</varname> is respected.</para>
 
         <para>The allowed set of values is determined by the kernel and is shown in the file itself (use
-        <command>cat /sys/power/state</command> to display). See <ulink
-        url="https://docs.kernel.org/admin-guide/pm/sleep-states.html#basic-sysfs-interfaces-for-system-suspend-and-hibernation">
+        <command>cat /sys/power/mem_sleep</command> to display). See the kernel documentation page
+        <ulink url="https://docs.kernel.org/admin-guide/pm/sleep-states.html#basic-sysfs-interfaces-for-system-suspend-and-hibernation">
           Basic sysfs Interfaces for System Suspend and Hibernation</ulink> for more details.</para>
 
-        <para>
-        <citerefentry><refentrytitle>systemd-suspend-then-hibernate.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
-        uses this value when suspending.</para>
-
-        <xi:include href="version-info.xml" xpointer="v203"/></listitem>
+        <xi:include href="version-info.xml" xpointer="v256"/></listitem>
       </varlistentry>
 
       <varlistentry>
index 4d0a581c009d6f91348d541272d2e94e3f74e615..4c29fa2c66568b6a4a8e95cc728e1833e2e9087e 100644 (file)
       </varlistentry>
 
       <varlistentry>
-        <term><option>--fdname=</option><replaceable>NAME</replaceable><optional>:<replaceable>NAME</replaceable>…</optional></term>
+        <term><option>--fdname=<replaceable>NAME</replaceable><optional>:<replaceable>NAME</replaceable>…</optional></option></term>
 
         <listitem><para>Specify names for the file descriptors passed. This is equivalent to setting
         <varname>FileDescriptorName=</varname> in socket unit files, and enables use of
index 5a6dffd2c580cb75a5d162d6b150c81b75f061d3..de032f48cbec4f622668157f680b8aca5b1459e6 100644 (file)
       </varlistentry>
 
       <varlistentry>
-        <term><option>--mutable=</option><replaceable>BOOL</replaceable>|<replaceable>auto</replaceable>|<replaceable>import</replaceable></term>
+        <term><option>--mutable=<replaceable>BOOL</replaceable>|<replaceable>auto</replaceable>|<replaceable>import</replaceable></option></term>
         <listitem><para>Set mutable mode.</para>
 
         <variablelist>
       </varlistentry>
 
       <varlistentry>
-        <term><option>--noexec=</option><replaceable>BOOL</replaceable></term>
+        <term><option>--noexec=<replaceable>BOOL</replaceable></option></term>
 
         <listitem><para>When merging configuration extensions into <filename>/etc/</filename> the
         <literal>MS_NOEXEC</literal> mount flag is used by default. This option can be used to disable
index e6611d04e7a26d01eb557c09c11d0c579ffa11b7..88f13d05b61c58877a090c6f7b6b3ec1f711fc64 100644 (file)
         <term><varname>ReloadLimitIntervalSec=</varname></term>
         <term><varname>ReloadLimitBurst=</varname></term>
 
-        <listitem><para>Rate limiting for daemon-reload requests. Default to unset, and any number of daemon-reload
-        operations can be requested at any time. <varname>ReloadLimitIntervalSec=</varname> takes a value in seconds
-        to configure the rate limit window, and <varname>ReloadLimitBurst=</varname> takes a positive integer to
-        configure the maximum allowed number of reloads within the configured time window.</para>
+        <listitem><para>Rate limiting for daemon-reload and (since v256) daemon-reexec requests. The setting
+        applies to both operations, but the rate limits are tracked separately. Defaults to unset, and any
+        number of operations can be requested at any time. <varname>ReloadLimitIntervalSec=</varname> takes
+        a value in seconds to configure the rate limit window, and <varname>ReloadLimitBurst=</varname>
+        takes a positive integer to configure the maximum allowed number of operations within the configured
+        time window.</para>
 
         <xi:include href="version-info.xml" xpointer="v253"/></listitem>
       </varlistentry>
index 90ddc9b6740ba3afde539932cd1eb0bd5cf660cd..f9649b2f30a829ef1278308f0dbc0c2f9e56e6ab 100644 (file)
@@ -31,8 +31,8 @@
 
   <refsect1>
     <title>Description</title>
-    <para><command>systemd-vmspawn</command> may be used to start a virtual machine from an OS image. In many ways it is similar to <citerefentry
-    project='man-pages'><refentrytitle>systemd-nspawn</refentrytitle><manvolnum>1</manvolnum></citerefentry>, but
+    <para><command>systemd-vmspawn</command> may be used to start a virtual machine from an OS image. In many ways it is similar to <citerefentry>
+    <refentrytitle>systemd-nspawn</refentrytitle><manvolnum>1</manvolnum></citerefentry>, but
     launches a full virtual machine instead of using namespaces.</para>
 
     <para>File descriptors for <filename>/dev/kvm</filename> and <filename>/dev/vhost-vsock</filename> can be
 
       <variablelist>
         <varlistentry>
-          <term><option>--cpus=</option><replaceable>CPUS</replaceable></term>
+          <term><option>--cpus=<replaceable>CPUS</replaceable></option></term>
 
           <listitem><para>Configures the number of CPUs to start the virtual machine with.
           Defaults to 1.</para>
         </varlistentry>
 
         <varlistentry>
-          <term><option>--ram=</option><replaceable>BYTES</replaceable></term>
+          <term><option>--ram=<replaceable>BYTES</replaceable></option></term>
 
           <listitem><para>Configures the amount of memory to start the virtual machine with.
           Defaults to 2G.</para>
         </varlistentry>
 
         <varlistentry>
-          <term><option>--kvm=</option><replaceable>BOOL</replaceable></term>
+          <term><option>--kvm=<replaceable>BOOL</replaceable></option></term>
 
           <listitem><para>Configures whether to use KVM. If the option is not specified KVM support will be
           detected automatically. If true, KVM is always used, and if false, KVM is never used.</para>
         </varlistentry>
 
         <varlistentry>
-          <term><option>--vsock=</option><replaceable>BOOL</replaceable></term>
+          <term><option>--vsock=<replaceable>BOOL</replaceable></option></term>
 
           <listitem>
             <para>Configure whether to use VSOCK networking.</para>
         </varlistentry>
 
         <varlistentry>
-          <term><option>--vsock-cid=</option><replaceable>CID</replaceable></term>
+          <term><option>--vsock-cid=<replaceable>CID</replaceable></option></term>
 
           <listitem>
             <para>Configure vmspawn to use a specific CID for the guest.</para>
         </varlistentry>
 
         <varlistentry>
-          <term><option>--tpm=</option><replaceable>BOOL</replaceable></term>
+          <term><option>--tpm=<replaceable>BOOL</replaceable></option></term>
 
           <listitem>
             <para>Configure whether to use VM with a virtual TPM or not.</para>
 
-            <para>If the option is not specified vmspawn will detect the presence of <citerefentry project='man-pages'>
+            <para>If the option is not specified vmspawn will detect the presence of <citerefentry project='debian'>
             <refentrytitle>swtpm</refentrytitle><manvolnum>8</manvolnum></citerefentry> and use it if available.
-            If yes is specified <citerefentry project='man-pages'><refentrytitle>swtpm</refentrytitle><manvolnum>8</manvolnum></citerefentry>
-            is always used, and vice versa if no is set <citerefentry project='man-pages'><refentrytitle>swtpm</refentrytitle>
+            If yes is specified <citerefentry project='debian'><refentrytitle>swtpm</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+            is always used, and vice versa if no is set <citerefentry project='debian'><refentrytitle>swtpm</refentrytitle>
             <manvolnum>8</manvolnum></citerefentry> is never used.</para>
 
             <para>Note: the virtual TPM used may change in future.</para>
         </varlistentry>
 
         <varlistentry>
-          <term><option>--linux=</option><replaceable>PATH</replaceable></term>
+          <term><option>--linux=<replaceable>PATH</replaceable></option></term>
 
           <listitem>
             <para>Set the linux kernel image to use for direct kernel boot.</para>
         </varlistentry>
 
         <varlistentry>
-          <term><option>--initrd=</option><replaceable>PATH</replaceable></term>
+          <term><option>--initrd=<replaceable>PATH</replaceable></option></term>
 
           <listitem>
             <para>Set the initrd to use for direct kernel boot.</para>
         </varlistentry>
 
         <varlistentry>
-          <term><option>--firmware=</option><replaceable>PATH</replaceable></term>
+          <term><option>--firmware=<replaceable>PATH</replaceable></option></term>
 
           <listitem><para>Takes an absolute path, or a relative path beginning with
           <filename>./</filename>. Specifies a JSON firmware definition file, which allows selecting the
         </varlistentry>
 
         <varlistentry>
-          <term><option>--secure-boot=</option><replaceable>BOOL</replaceable></term>
+          <term><option>--discard-disk=<replaceable>BOOL</replaceable></option></term>
+
+          <listitem><para>Controls whether qemu processes discard requests from the VM.
+          This prevents long running VMs from using more disk space than required.
+          This is enabled by default.</para>
+
+          <xi:include href="version-info.xml" xpointer="v256"/></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><option>--secure-boot=<replaceable>BOOL</replaceable></option></term>
 
           <listitem><para>Configure whether to search for firmware which supports Secure Boot.</para>
 
 
       <variablelist>
         <varlistentry>
-          <term><option>--private-users=</option><replaceable>UID_SHIFT[:UID_RANGE]</replaceable></term>
+          <term><option>--private-users=<replaceable>UID_SHIFT[:UID_RANGE]</replaceable></option></term>
 
           <listitem><para>Controls user namespacing under <option>--directory=</option>.
-          If enabled, <citerefentry project='man-pages'><refentrytitle>virtiofsd</refentrytitle><manvolnum>1</manvolnum></citerefentry>
-          is instructed to map user and group ids (UIDs and GIDs). This involves mapping the private UIDs/GIDs used in the virtual machine
-          (starting with the virtual machine's root user 0 and up) to a range of UIDs/GIDs on the host that are not used for other
-          purposes (usually in the range beyond the host's UID/GID 65536).</para>
+          If enabled, <command>virtiofsd</command> is instructed to map user and group ids (UIDs and GIDs).
+          This involves mapping the private UIDs/GIDs used in the virtual machine (starting with the virtual machine's
+          root user 0 and up) to a range of UIDs/GIDs on the host that are not used for other purposes (usually in the
+          range beyond the host's UID/GID 65536).</para>
 
           <para>If one or two colon-separated numbers are specified, user namespacing is turned on. <replaceable>UID_SHIFT</replaceable>
           specifies the first host UID/GID to map, <replaceable>UID_RANGE</replaceable> is optional and specifies number of host
       <variablelist>
 
         <varlistentry>
-          <term><option>--bind=</option><replaceable>PATH</replaceable></term>
-          <term><option>--bind-ro=</option><replaceable>PATH</replaceable></term>
+          <term><option>--bind=<replaceable>PATH</replaceable></option></term>
+          <term><option>--bind-ro=<replaceable>PATH</replaceable></option></term>
 
           <listitem><para>Mount a directory from the host into the virtual machine. Takes one of: a path
           argument — in which case the specified path will be mounted from the host to the same path in the virtual machine, or
         </varlistentry>
 
         <varlistentry>
-          <term><option>--extra-drive=</option><replaceable>PATH</replaceable></term>
+          <term><option>--extra-drive=<replaceable>PATH</replaceable></option></term>
 
           <listitem><para>Takes a disk image or block device on the host and supplies it to the virtual machine as another drive.</para>
 
 
       <variablelist>
         <varlistentry>
-          <term><option>--forward-journal=</option><replaceable>FILE|DIR</replaceable></term>
+          <term><option>--forward-journal=<replaceable>FILE|DIR</replaceable></option></term>
 
           <listitem><para>Forward the virtual machine's journal to the host.
           <citerefentry><refentrytitle>systemd-journal-remote</refentrytitle><manvolnum>8</manvolnum></citerefentry>
         </varlistentry>
 
         <varlistentry>
-          <term><option>--pass-ssh-key=</option><replaceable>BOOL</replaceable></term>
+          <term><option>--pass-ssh-key=<replaceable>BOOL</replaceable></option></term>
 
           <listitem><para>By default an SSH key is generated to allow <command>systemd-vmspawn</command> to open
           a D-Bus connection to the VM's systemd bus. Setting this to "no" will disable SSH key generation.</para>
         </varlistentry>
 
         <varlistentry>
-          <term><option>--ssh-key-type=</option><replaceable>TYPE</replaceable></term>
+          <term><option>--ssh-key-type=<replaceable>TYPE</replaceable></option></term>
 
           <listitem><para>Configures the type of SSH key to generate, see
-          <citerefentry><refentrytitle>ssh-keygen</refentrytitle><manvolnum>1</manvolnum></citerefentry>
+          <citerefentry project="man-pages"><refentrytitle>ssh-keygen</refentrytitle><manvolnum>1</manvolnum></citerefentry>
           for more information.</para>
 
           <para>By default <literal>ed25519</literal> keys are generated, however <literal>rsa</literal> keys
 
       <variablelist>
         <varlistentry>
-          <term><option>--console=</option><replaceable>MODE</replaceable></term>
+          <term><option>--console=<replaceable>MODE</replaceable></option></term>
 
           <listitem><para>Configures how to set up the console of the VM. Takes one of
           <literal>interactive</literal>, <literal>read-only</literal>, <literal>native</literal>,
 
       <variablelist>
         <varlistentry>
-          <term><option>--load-credential=</option><replaceable>ID</replaceable>:<replaceable>PATH</replaceable></term>
-          <term><option>--set-credential=</option><replaceable>ID</replaceable>:<replaceable>VALUE</replaceable></term>
+          <term><option>--load-credential=<replaceable>ID</replaceable>:<replaceable>PATH</replaceable></option></term>
+          <term><option>--set-credential=<replaceable>ID</replaceable>:<replaceable>VALUE</replaceable></option></term>
 
           <listitem><para>Pass a credential to the virtual machine. These two options correspond to the
           <varname>LoadCredential=</varname> and <varname>SetCredential=</varname> settings in unit files. See
@@ -511,7 +521,7 @@ $ systemd-vmspawn --image=image.raw
     <title>See Also</title>
     <para><simplelist type="inline">
       <member><citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
-      <member><citerefentry><refentrytitle>mkosi</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
+      <member><citerefentry project='debian'><refentrytitle>mkosi</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
       <member><citerefentry><refentrytitle>machinectl</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
       <member><citerefentry><refentrytitle>importctl</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
     </simplelist></para>
index 05f5f20f44b300d87c420ebbd7cac1c39a05895a..64b7b07fc7fa666c3f7ba72f7b81131c9d7890cc 100644 (file)
@@ -2369,12 +2369,12 @@ RestrictNamespaces=~cgroup net</programlisting>
         units, it only enables sharing of the <filename>/tmp/</filename> and <filename>/var/tmp/</filename>
         directories.</para>
 
-        <para>Other file system namespace unit settings — <varname>PrivateMounts=</varname>,
-        <varname>PrivateTmp=</varname>, <varname>PrivateDevices=</varname>, <varname>ProtectSystem=</varname>,
-        <varname>ProtectHome=</varname>, <varname>ReadOnlyPaths=</varname>, <varname>InaccessiblePaths=</varname>,
-        <varname>ReadWritePaths=</varname>, … — also enable file system namespacing in a fashion equivalent to this
-        option. Hence it is primarily useful to explicitly request this behaviour if none of the other settings are
-        used.</para>
+        <para>Other file system namespace unit settings — <varname>PrivateTmp=</varname>,
+        <varname>PrivateDevices=</varname>, <varname>ProtectSystem=</varname>,
+        <varname>ProtectHome=</varname>, <varname>ReadOnlyPaths=</varname>,
+        <varname>InaccessiblePaths=</varname>, <varname>ReadWritePaths=</varname>, … — also enable file
+        system namespacing in a fashion equivalent to this option. Hence it is primarily useful to explicitly
+        request this behaviour if none of the other settings are used.</para>
 
         <xi:include href="system-or-user-ns.xml" xpointer="singular"/>
 
@@ -3175,8 +3175,8 @@ StandardInputData=V2XigLJyZSBubyBzdHJhbmdlcnMgdG8gbG92ZQpZb3Uga25vdyB0aGUgcnVsZX
         <literal>\x7efoobar</literal> would add a pattern matching <literal>~foobar</literal> to the allow list.</para>
 
         <para>Log messages are tested against denied patterns (if any), then against allowed patterns
-        (if any). If a log message matches any of the denied patterns, it will be discarded, whatever the
-        allowed patterns. Then, remaining log messages are tested against allowed patterns. Messages matching
+        (if any). If a log message matches any of the denied patterns, it is discarded immediately without considering
+        allowed patterns. Remaining log messages are tested against allowed patterns. Messages matching
         against none of the allowed pattern are discarded. If no allowed patterns are defined, then all
         messages are processed directly after going through denied filters.</para>
 
index 4e9afc3674ea1b2937bd38298b9f98fcb56959af..4fba78895069dc071ccca276047ef2722e497837 100644 (file)
         <listitem>
           <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>).
+          (see <citerefentry project='man-pages'><refentrytitle>wg</refentrytitle><manvolnum>8</manvolnum></citerefentry>).
           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
         <term><varname>PublicKey=</varname></term>
         <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>)
+          (see <citerefentry project='man-pages'><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 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>
index e143aebb1bd39a59c384b074c602d9bf1768da89..38ab30fb4d4a41f21bd23d7b83eb9cac8d87c91e 100644 (file)
           Defaults to <literal>no</literal>. Further settings for the DHCP server may be set in the
           [DHCPServer] section described below.</para>
 
-          <para>Even if this is enabled, the DHCP server will not be started automatically. It will be
-          started after <filename>systemd-networkd-persistent-storage.service</filename> is started, which
-          calls <command>networkctl persistent-storage yes</command>. See
+          <para>Even if this is enabled, the DHCP server will not be started automatically and wait for the
+          persistent storage being ready to load/save leases in the storage, unless
+          <varname>RelayTarget=</varname> or <varname>PersistLeases=no</varname> are specified in the
+          [DHCPServer] section. It will be started after
+          <filename>systemd-networkd-persistent-storage.service</filename> is started, which calls
+          <command>networkctl persistent-storage yes</command>. See
           <citerefentry><refentrytitle>networkctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>
           for more details.</para>
 
@@ -2567,7 +2570,9 @@ NFTSet=prefix:netdev:filter:eth_ipv4_prefix</programlisting>
           effect of the <option>Domains=</option> setting. If set to <option>route</option>, the domain name
           received from the DHCP server will be used for routing DNS queries only, but not for searching,
           similarly to the effect of the <option>Domains=</option> setting when the argument is prefixed with
-          <literal>~</literal>. Defaults to false.</para>
+          <literal>~</literal>. When unspecified, the value specified in the same setting in
+          <citerefentry><refentrytitle>networkd.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
+          which defaults to <literal>no</literal>, will be used.</para>
 
           <para>It is recommended to enable this option only on trusted networks, as setting this
           affects resolution of all hostnames, in particular of single-label names. It is generally
@@ -3188,6 +3193,16 @@ NFTSet=prefix:netdev:filter:eth_ipv4_prefix</programlisting>
     with the <varname>IPv6AcceptRA=</varname> setting described above:</para>
 
     <variablelist class='network-directives'>
+      <varlistentry>
+        <term><varname>UseRedirect=</varname></term>
+        <listitem>
+          <para>When true (the default), Redirect message sent by the current first-hop router will be
+          accepted, and configures routes to redirected nodes will be configured.</para>
+
+          <xi:include href="version-info.xml" xpointer="v256"/>
+        </listitem>
+      </varlistentry>
+
       <varlistentry>
         <term><varname>Token=</varname></term>
         <listitem>
@@ -3903,6 +3918,22 @@ ServerAddress=192.168.0.1/24</programlisting>
         </listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><varname>PersistLeases=</varname></term>
+        <listitem>
+          <para>Takes a boolean. When true, the DHCP server will load and save leases in the persistent
+          storage. When false, the DHCP server will neither load nor save leases in the persistent storage.
+          Hence, bound leases will be lost when the interface is reconfigured e.g. by
+          <command>networkctl reconfigure</command>, or <filename>systemd-networkd.service</filename>
+          is restarted. That may cause address conflict on the network. So, please take an extra care when
+          disable this setting. When unspecified, the value specified in the same setting in
+          <citerefentry><refentrytitle>networkd.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
+          which defaults to <literal>yes</literal>, will be used.</para>
+
+          <xi:include href="version-info.xml" xpointer="v256"/>
+        </listitem>
+      </varlistentry>
+
     </variablelist>
   </refsect1>
 
index 67a3930a28a578be78fb360cd5d3375802822033..9a0c8166effae381973fd19970bc1ad73c6c0d1e 100644 (file)
@@ -1728,7 +1728,7 @@ SystemdService=simple-dbus-service.service</programlisting>
 Description=Simple notifying service
 
 [Service]
-Type=notify
+Type=notify-reload
 ExecStart=/usr/sbin/simple-notifying-service
 
 [Install]
@@ -1746,6 +1746,16 @@ WantedBy=multi-user.target</programlisting>
       <citerefentry><refentrytitle>systemd.kill</refentrytitle><manvolnum>5</manvolnum></citerefentry>
       for details on how you can influence the way systemd terminates
       the service.</para>
+
+      <para>To avoid code duplication, it is preferable to use
+      <citerefentry><refentrytitle>sd_notify</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+      when possible, especially when other APIs provided by
+      <citerefentry><refentrytitle>libsystemd</refentrytitle><manvolnum>3</manvolnum></citerefentry> are
+      also used, but note that the notification protocol is very simple and guaranteed to be stable as per
+      the <ulink url="https://systemd.io/PORTABILITY_AND_STABILITY/">Interface Portability and Stability
+      Promise</ulink>, so it can be reimplemented by services with no external dependencies. For a
+      self-contained example, see
+      <citerefentry><refentrytitle>sd_notify</refentrytitle><manvolnum>3</manvolnum></citerefentry>.</para>
     </example>
   </refsect1>
 
index 50871f7a749efb66a521e68000ca479cd10cfa8c..8dc7641dde8e9cd72ce239efe7aeb848812f5eb1 100644 (file)
 
       <varlistentry>
         <term><varname>MaxConnections=</varname></term>
-        <listitem><para>The maximum number of connections to
-        simultaneously run services instances for, when
-        <option>Accept=yes</option> is set. If more concurrent
-        connections are coming in, they will be refused until at least
-        one existing connection is terminated. This setting has no
-        effect on sockets configured with
-        <option>Accept=no</option> or datagram sockets. Defaults to
-        64.</para></listitem>
+        <listitem><para>The maximum number of connections to simultaneously run services instances for, when
+        <option>Accept=yes</option> is set. If more concurrent connections are coming in, they will be refused
+        until at least one existing connection is terminated. This setting has no effect on sockets configured
+        with <option>Accept=no</option> or datagram sockets. Defaults to 64.</para></listitem>
       </varlistentry>
 
       <varlistentry>
         <listitem><para>The maximum number of connections for a service per source IP address (in case of
         IPv4/IPv6), per source CID (in case of <constant>AF_VSOCK</constant>), or source UID (in case of
         <constant>AF_UNIX</constant>). This is very similar to the <varname>MaxConnections=</varname>
-        directive above. Disabled by default.</para>
+        directive above. Defaults to 0, i.e. disabled.</para>
 
         <xi:include href="version-info.xml" xpointer="v232"/>
         </listitem>
       <varlistentry>
         <term><varname>TCPCongestion=</varname></term>
         <listitem><para>Takes a string value. Controls the TCP congestion algorithm used by this
-        socket. Should be one of <literal>westwood</literal>, <literal>veno</literal>,
+        socket. Should be one of <literal>westwood</literal>, <literal>reno</literal>,
         <literal>cubic</literal>, <literal>lp</literal> or any other available algorithm supported by the IP
         stack. This setting applies only to stream sockets.</para></listitem>
       </varlistentry>
index 2a345c47b4c26c159ba5ca916ecb53b9f57b3119..e8e2985a42b26e11c1b8fca909292984d6f5e8f4 100644 (file)
       </varlistentry>
 
       <varlistentry>
-        <term><varname>network.netdev.*</varname></term>
+        <term><varname>network.conf.*</varname></term>
         <term><varname>network.link.*</varname></term>
+        <term><varname>network.netdev.*</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-network-generator.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>.
+          These credentials should contain valid
+          <citerefentry><refentrytitle>networkd.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
           <citerefentry><refentrytitle>systemd.link</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
+          <citerefentry><refentrytitle>systemd.netdev</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
           <citerefentry><refentrytitle>systemd.network</refentrytitle><manvolnum>5</manvolnum></citerefentry>
-          for details.</para>
+          configuration data. From each matching credential a separate file is created. Example: the contents
+          of a credential <filename>network.link.50-foobar</filename> will be copied into a 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
           <xi:include href="version-info.xml" xpointer="v256"/>
         </listitem>
       </varlistentry>
+
+      <varlistentry>
+        <term><varname>systemd.extra-unit.*</varname></term>
+        <term><varname>systemd.unit-dropin.*</varname></term>
+
+        <listitem><para>These credentials specify extra units and drop-ins to add to the system. For details
+        see <citerefentry><refentrytitle>systemd-debug-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>.</para>
+
+        <xi:include href="version-info.xml" xpointer="v256"/></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>udev.conf.*</varname></term>
+        <term><varname>udev.rules.*</varname></term>
+
+        <listitem>
+          <para>Configures udev configuration file and udev rules. Read by
+          <filename>systemd-udev-load-credentials.service</filename>, which invokes
+          <command>udevadm control --load-credentials</command>. These credentials directly translate to a
+          matching
+          <citerefentry><refentrytitle>udev.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry> or
+          <citerefentry><refentrytitle>udev</refentrytitle><manvolnum>7</manvolnum></citerefentry> rules
+          file. Example: the contents of a credential
+          <filename>udev.conf.50-foobar</filename> will be copied into a file
+          <filename>/run/udev/udev.conf.d/50-foobar.conf</filename>, and
+          <filename>udev.rules.50-foobar</filename> will be copied into a file
+          <filename>/run/udev/rules.d/50-foobar.rules</filename>. See
+          <citerefentry><refentrytitle>udev</refentrytitle><manvolnum>7</manvolnum></citerefentry>,
+          <citerefentry><refentrytitle>udev.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>, and
+          <citerefentry><refentrytitle>udevadm</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+          for details.</para>
+
+          <xi:include href="version-info.xml" xpointer="v256"/>
+        </listitem>
+      </varlistentry>
     </variablelist>
   </refsect1>
 
index cb798af34c0ec3b8a5c31c0211d3e6d0a92fde16..abfcc499f088fa619d18b3a55b4fe748064ad5aa 100644 (file)
         </varlistentry>
 
         <varlistentry>
-          <term><option>--crash-vt=</option><replaceable>VT</replaceable></term>
+          <term><option>--crash-vt=<replaceable>VT</replaceable></option></term>
 
           <listitem><para>Switch to a specific virtual console (VT) on crash. This switch has no effect when
           running as user instance. Same as <varname>systemd.crash_chvt=</varname> above (but not the
index 76807b92e5cf5f365b06cd93c96cdfe2aea5de3a..0aabfa625ae40add2bebc0365716edeba6791199 100644 (file)
@@ -538,7 +538,7 @@ w- /proc/sys/vm/swappiness - - - - 10</programlisting></para>
       Base64 decoding is applied to the credential contents.</para>
 
       <para>Note that for all line types that result in creation of any kind of file node
-      (i.e. <varname>f</varname>/<varname>F</varname>,
+      (i.e. <varname>f</varname>,
       <varname>d</varname>/<varname>D</varname>/<varname>v</varname>/<varname>q</varname>/<varname>Q</varname>,
       <varname>p</varname>, <varname>L</varname>, <varname>c</varname>/<varname>b</varname> and <varname>C</varname>)
       leading directories are implicitly created if needed, owned by root with an access mode of 0755. In order to
@@ -697,7 +697,7 @@ d /tmp/foo/bar - - - bmA:1h -</programlisting></para>
 
       <para>For <varname>L</varname> lines determines the destination path of the symlink. For <varname>c</varname> and
       <varname>b</varname>, determines the major/minor of the device node, with major and minor formatted as integers,
-      separated by <literal>:</literal>, e.g.  <literal>1:3</literal>. For <varname>f</varname>, <varname>F</varname>,
+      separated by <literal>:</literal>, e.g. <literal>1:3</literal>. For <varname>f</varname>
       and <varname>w</varname>, the argument may be used to specify a short string that is written to the file,
       suffixed by a newline. For <varname>C</varname>, specifies the source file or directory. For <varname>t</varname>
       and <varname>T</varname>, determines extended attributes to be set. For <varname>a</varname> and
index 643321b0a23a30dd7256a4932d44a7641da37a88..b515f3c79c05a0a42e17e9dd4a639addaadea072 100644 (file)
         </varlistentry>
         <varlistentry>
           <term><option>-m</option></term>
-          <term><option>--children-max=</option><replaceable>value</replaceable></term>
+          <term><option>--children-max=<replaceable>value</replaceable></option></term>
           <listitem>
             <para>Set the maximum number of events, systemd-udevd will handle at the same time. When 0 is
             specified, then the maximum is determined based on the system resources.</para>
         </varlistentry>
         <varlistentry>
           <term><option>-t</option></term>
-          <term><option>--timeout=</option><replaceable>seconds</replaceable></term>
+          <term><option>--timeout=<replaceable>seconds</replaceable></option></term>
           <listitem>
             <para>The maximum number of seconds to wait for a reply from systemd-udevd.</para>
           </listitem>
         </varlistentry>
 
+        <varlistentry>
+          <term><option>--load-credentials</option></term>
+          <listitem>
+            <para>When specified, the following credentials are used when passed in:</para>
+
+            <variablelist>
+              <varlistentry>
+                <term><varname>udev.conf.*</varname></term>
+                <listitem>
+                  <para>These credentials should contain valid
+                  <citerefentry><refentrytitle>udev.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+                  configuration data. From each matching credential a separate file is created. Example: a
+                  passed credential <filename>udev.conf.50-foobar</filename> will be copied into a
+                  configuration file <filename>/run/udev/udev.conf.d/50-foobar.conf</filename>.</para>
+
+                  <xi:include href="version-info.xml" xpointer="v256"/>
+                </listitem>
+              </varlistentry>
+              <varlistentry>
+                <term><varname>udev.rules.*</varname></term>
+                <listitem>
+                  <para>These credentials should contain valid
+                  <citerefentry><refentrytitle>udev</refentrytitle><manvolnum>7</manvolnum></citerefentry>
+                  rules. From each matching credential a separate file is created. Example: a passed credential
+                  <filename>udev.rules.50-foobar</filename> will be copied into a configuration file
+                  <filename>/run/udev/rules.d/50-foobar.rules</filename>.</para>
+
+                  <xi:include href="version-info.xml" xpointer="v256"/>
+                </listitem>
+              </varlistentry>
+            </variablelist>
+
+            <para>Note, this <emphasis>does not</emphasis> imply <option>--reload</option> option. So, if
+            <command>systemd-udevd</command> is already running, please consider to also specify
+            <option>-reload</option> to make the copied udev rules files used by
+            <command>systemd-udevd</command>.</para>
+
+            <xi:include href="version-info.xml" xpointer="v256"/>
+          </listitem>
+        </varlistentry>
+
         <xi:include href="standard-options.xml" xpointer="help" />
       </variablelist>
     </refsect2>
index 479893217ff620907d5beca41f97d925020cea4c..a7b438ad91b52aa267280ebf4669d09a66196917 100644 (file)
@@ -53,7 +53,7 @@
     <variablelist>
 
       <varlistentry>
-        <term><option>--output=</option><replaceable>MODE</replaceable></term>
+        <term><option>--output=<replaceable>MODE</replaceable></option></term>
 
         <listitem><para>Choose the output mode, takes one of <literal>classic</literal>,
         <literal>friendly</literal>, <literal>table</literal>, <literal>json</literal>. If
@@ -74,7 +74,7 @@
       </varlistentry>
 
       <varlistentry>
-        <term><option>--json=</option><replaceable>FORMAT</replaceable></term>
+        <term><option>--json=<replaceable>FORMAT</replaceable></option></term>
 
         <listitem><para>Selects JSON output mode (like <option>--output=json</option>) and selects the
         precise display mode. Takes one of <literal>pretty</literal> or <literal>short</literal>. If
@@ -86,7 +86,7 @@
       </varlistentry>
 
       <varlistentry>
-        <term><option>--service=</option><replaceable>SERVICE</replaceable><optional>:<replaceable>SERVICE…</replaceable></optional></term>
+        <term><option>--service=<replaceable>SERVICE</replaceable><optional>:<replaceable>SERVICE…</replaceable></optional></option></term>
         <term><option>-s</option> <replaceable>SERVICE</replaceable>:<replaceable>SERVICE…</replaceable></term>
 
         <listitem><para>Controls which services to query for users/groups. Takes a list of one or more
@@ -97,7 +97,7 @@
       </varlistentry>
 
       <varlistentry>
-        <term><option>--with-nss=</option><replaceable>BOOL</replaceable></term>
+        <term><option>--with-nss=<replaceable>BOOL</replaceable></option></term>
 
         <listitem><para>Controls whether to include classic glibc/NSS user/group lookups in the output. If
         <option>--with-nss=no</option> is used any attempts to resolve or enumerate users/groups provided
       </varlistentry>
 
       <varlistentry>
-        <term><option>--with-varlink=</option><replaceable>BOOL</replaceable></term>
+        <term><option>--with-varlink=<replaceable>BOOL</replaceable></option></term>
 
         <listitem><para>Controls whether to include Varlink user/group lookups in the output, i.e. those done
         via the <ulink url="https://systemd.io/USER_GROUP_API">User/Group Record Lookup API via
       </varlistentry>
 
       <varlistentry>
-        <term><option>--with-dropin=</option><replaceable>BOOL</replaceable></term>
+        <term><option>--with-dropin=<replaceable>BOOL</replaceable></option></term>
 
         <listitem><para>Controls whether to include user/group lookups in the output that are defined using
         drop-in files in <filename>/etc/userdb/</filename>, <filename>/run/userdb/</filename>,
       </varlistentry>
 
       <varlistentry>
-        <term><option>--synthesize=</option><replaceable>BOOL</replaceable></term>
+        <term><option>--synthesize=<replaceable>BOOL</replaceable></option></term>
 
         <listitem><para>Controls whether to synthesize records for the root and nobody users/groups if they
         aren't defined otherwise. By default (or <literal>yes</literal>) such records are implicitly
       </varlistentry>
 
       <varlistentry>
-        <term><option>--multiplexer=</option><replaceable>BOOL</replaceable></term>
+        <term><option>--multiplexer=<replaceable>BOOL</replaceable></option></term>
 
         <listitem><para>Controls whether to do lookups via the multiplexer service (if specified as true, the
         default) or do lookups in the client (if specified as false). Using the multiplexer service is
index 22add03a9d5a964c0ff6cca4357c737f30116616..eefc3d1de0eea2e11a46a772179a75b5f4bbddd4 100644 (file)
       </varlistentry>
 
       <varlistentry>
-        <term><option>--json=</option><replaceable>MODE</replaceable></term>
+        <term><option>--json=<replaceable>MODE</replaceable></option></term>
 
         <listitem>
           <para>Selects the JSON output formatting, one of <literal>pretty</literal> (for nicely indented,
index e3346a80211432557c6824a0a0b4c1ddc7308489..2e8994471a03df0b3088d4288fca01e6e82c1581 100644 (file)
@@ -1,5 +1,6 @@
 /* SPDX-License-Identifier: MIT-0 */
 
+#define _GNU_SOURCE 1
 #include <errno.h>
 #include <stdbool.h>
 #include <stddef.h>
 
 #define _cleanup_(f) __attribute__((cleanup(f)))
 
-#define check(x) ({                             \
-  int r = (x);                                  \
-  errno = r < 0 ? -r : 0;                       \
-  printf(#x ": %m\n");                          \
-  if (r < 0)                                    \
-    return EXIT_FAILURE;                        \
-  })
-
 typedef struct object {
   char *name;
   uint32_t number;
 } object;
 
 static int method(sd_bus_message *m, void *userdata, sd_bus_error *error) {
+  int r;
+
   printf("Got called with userdata=%p\n", userdata);
 
   if (sd_bus_message_is_method_call(m,
@@ -31,8 +26,17 @@ static int method(sd_bus_message *m, void *userdata, sd_bus_error *error) {
     return 1;
 
   const char *string;
-  check(sd_bus_message_read(m, "s", &string));
-  check(sd_bus_reply_method_return(m, "s", string));
+  r = sd_bus_message_read(m, "s", &string);
+  if (r < 0) {
+    fprintf(stderr, "sd_bus_message_read() failed: %s\n", strerror(-r));
+    return 0;
+  }
+
+  r = sd_bus_reply_method_return(m, "s", string);
+  if (r < 0) {
+    fprintf(stderr, "sd_bus_reply_method_return() failed: %s\n", strerror(-r));
+    return 0;
+  }
 
   return 1;
 }
@@ -84,28 +88,55 @@ static const sd_bus_vtable vtable[] = {
 
 int main(int argc, char **argv) {
   _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
+  int r;
 
   sd_bus_default(&bus);
 
   object object = { .number = 666 };
-  check((object.name = strdup("name")) != NULL);
+  object.name = strdup("name");
+  if (!object.name) {
+    fprintf(stderr, "OOM\n");
+    return EXIT_FAILURE;
+  }
 
-  check(sd_bus_add_object_vtable(bus, NULL,
-                                 "/org/freedesktop/systemd/VtableExample",
-                                 "org.freedesktop.systemd.VtableExample",
-                                 vtable,
-                                 &object));
+  r = sd_bus_add_object_vtable(bus, NULL,
+                               "/org/freedesktop/systemd/VtableExample",
+                               "org.freedesktop.systemd.VtableExample",
+                               vtable,
+                               &object);
+  if (r < 0) {
+    fprintf(stderr, "sd_bus_add_object_vtable() failed: %s\n", strerror(-r));
+    return EXIT_FAILURE;
+  }
 
-  check(sd_bus_request_name(bus,
-                            "org.freedesktop.systemd.VtableExample",
-                            0));
+  r = sd_bus_request_name(bus,
+                          "org.freedesktop.systemd.VtableExample",
+                          0);
+  if (r < 0) {
+    fprintf(stderr, "sd_bus_request_name() failed: %s\n", strerror(-r));
+    return EXIT_FAILURE;
+  }
 
   for (;;) {
-    check(sd_bus_wait(bus, UINT64_MAX));
-    check(sd_bus_process(bus, NULL));
+    r = sd_bus_wait(bus, UINT64_MAX);
+    if (r < 0) {
+      fprintf(stderr, "sd_bus_wait() failed: %s\n", strerror(-r));
+      return EXIT_FAILURE;
+    }
+
+    r = sd_bus_process(bus, NULL);
+    if (r < 0) {
+      fprintf(stderr, "sd_bus_process() failed: %s\n", strerror(-r));
+      return EXIT_FAILURE;
+    }
+  }
+
+  r = sd_bus_release_name(bus, "org.freedesktop.systemd.VtableExample");
+  if (r < 0) {
+    fprintf(stderr, "sd_bus_release_name() failed: %s\n", strerror(-r));
+    return EXIT_FAILURE;
   }
 
-  check(sd_bus_release_name(bus, "org.freedesktop.systemd.VtableExample"));
   free(object.name);
 
   return 0;
index 897b39386904a7e75b9dfeef374903b64c64a369..1a160960cc6f4d2c5d1cb00908fc0bbd7a7bcc0f 100644 (file)
@@ -210,6 +210,10 @@ if sshdconfdir == ''
         sshdconfdir = sysconfdir / 'ssh/sshd_config.d'
 endif
 
+sshdprivsepdir = get_option('sshdprivsepdir')
+conf.set10('CREATE_SSHDPRIVSEPDIR', sshdprivsepdir != 'no' and not sshdprivsepdir.startswith('/usr/'))
+conf.set('SSHDPRIVSEPDIR', sshdprivsepdir, description : 'SSH privilege separation directory')
+
 libcryptsetup_plugins_dir = get_option('libcryptsetup-plugins-dir')
 if libcryptsetup_plugins_dir == ''
         libcryptsetup_plugins_dir = libdir / 'cryptsetup'
@@ -268,6 +272,8 @@ conf.set_quoted('SYSTEMD_TEST_DATA',                          testdata_dir)
 conf.set_quoted('SYSTEMD_TTY_ASK_PASSWORD_AGENT_BINARY_PATH', bindir / 'systemd-tty-ask-password-agent')
 conf.set_quoted('SYSTEMD_UPDATE_HELPER_PATH',                 libexecdir / 'systemd-update-helper')
 conf.set_quoted('SYSTEMD_USERWORK_PATH',                      libexecdir / 'systemd-userwork')
+conf.set_quoted('SYSTEMD_MOUNTWORK_PATH',                     libexecdir / 'systemd-mountwork')
+conf.set_quoted('SYSTEMD_NSRESOURCEWORK_PATH',                libexecdir / 'systemd-nsresourcework')
 conf.set_quoted('SYSTEMD_VERITYSETUP_PATH',                   libexecdir / 'systemd-veritysetup')
 conf.set_quoted('SYSTEM_CONFIG_UNIT_DIR',                     pkgsysconfdir / 'system')
 conf.set_quoted('SYSTEM_DATA_UNIT_DIR',                       systemunitdir)
@@ -1205,6 +1211,7 @@ libkmod = dependency('libkmod',
                      version : '>= 15',
                      required : get_option('kmod'))
 conf.set10('HAVE_KMOD', libkmod.found())
+libkmod_cflags = libkmod.partial_dependency(includes: true, compile_args: true)
 
 libxenctrl = dependency('xencontrol',
                         version : '>= 4.9',
@@ -1319,6 +1326,9 @@ if not have
         # link to neither of the libs if one is not found
         libgcrypt = []
         libgpg_error = []
+        libgcrypt_cflags = []
+else
+        libgcrypt_cflags = libgcrypt.partial_dependency(includes: true, compile_args: true)
 endif
 conf.set10('HAVE_GCRYPT', have)
 
@@ -1589,6 +1599,8 @@ conf.set10('ENABLE_REMOTE', have)
 feature = get_option('vmspawn').disable_auto_if(conf.get('BUILD_MODE_DEVELOPER') == 0)
 conf.set10('ENABLE_VMSPAWN', feature.allowed())
 
+conf.set10('DEFAULT_MOUNTFSD_TRUSTED_DIRECTORIES', get_option('default-mountfsd-trusted-directories'))
+
 foreach term : ['analyze',
                 'backlight',
                 'binfmt',
@@ -1609,8 +1621,10 @@ foreach term : ['analyze',
                 'localed',
                 'logind',
                 'machined',
+                'mountfsd',
                 'networkd',
                 'nscd',
+                'nsresourced',
                 'nss-myhostname',
                 'nss-systemd',
                 'oomd',
@@ -1694,6 +1708,7 @@ if conf.get('BPF_FRAMEWORK') == 1
         bpf_gcc_flags = [
                 '-std=gnu11',
                 '-fno-stack-protector',
+                '-fno-ssa-phiopt',
                 '-O2',
                 '-mcpu=v3',
                 '-mco-re',
@@ -1830,6 +1845,79 @@ conf.set10('ENABLE_UKIFY', want_ukify)
 #####################################################################
 
 check_efi_alignment_py = find_program('tools/check-efi-alignment.py')
+
+#####################################################################
+
+use_provided_vmlinux_h = false
+use_generated_vmlinux_h = false
+provided_vmlinux_h_path = get_option('vmlinux-h-path')
+
+# For the more complex BPF programs we really want a vmlinux.h (which is arch
+# specific, but only somewhat bound to kernel version). Ideally the kernel
+# development headers would ship that, but right now they don't. Hence address
+# this in two ways:
+#
+# 1. Provide a vmlinux.h at build time
+# 2. Generate the file on the fly where possible (which requires /sys/ to be mounted)
+#
+# We generally prefer the former (to support reproducible builds), but will
+# fallback to the latter.
+
+if conf.get('BPF_FRAMEWORK') == 1
+        enable_vmlinux_h = get_option('vmlinux-h')
+
+        if enable_vmlinux_h == 'auto'
+                if provided_vmlinux_h_path != ''
+                        use_provided_vmlinux_h = true
+                elif fs.exists('/sys/kernel/btf/vmlinux') and \
+                                bpftool.found() and \
+                                (host_machine.cpu_family() == build_machine.cpu_family()) and \
+                                host_machine.cpu_family() in ['x86_64', 'aarch64']
+
+                        # We will only generate a vmlinux.h from the running
+                        # kernel if the host and build machine are of the same
+                        # family. Also for now we focus on x86_64 and aarch64,
+                        # since other archs don't seem to be ready yet.
+
+                        use_generated_vmlinux_h = true
+                endif
+        elif enable_vmlinux_h == 'provided'
+                use_provided_vmlinux_h = true
+        elif enable_vmlinux_h == 'generated'
+                if not fs.exists('/sys/kernel/btf/vmlinux')
+                        error('BTF data from kernel not available (/sys/kernel/btf/vmlinux missing), cannot generate vmlinux.h, but was asked to.')
+                endif
+                if not bpftool.found()
+                        error('bpftool not available, cannot generate vmlinux.h, but was asked to.')
+                endif
+                use_generated_vmlinux_h = true
+        endif
+endif
+
+if use_provided_vmlinux_h
+        if not fs.exists(provided_vmlinux_h_path)
+                error('Path to provided vmlinux.h does not exist.')
+        endif
+        vmlinux_h_dependency = []
+        bpf_o_unstripped_cmd += ['-I' + fs.parent(provided_vmlinux_h_path)]
+        message('Using provided @0@'.format(provided_vmlinux_h_path))
+elif use_generated_vmlinux_h
+        vmlinux_h_dependency = custom_target(
+                'vmlinux.h',
+                output: 'vmlinux.h',
+                command : [ bpftool, 'btf', 'dump', 'file', '/sys/kernel/btf/vmlinux', 'format', 'c' ],
+                capture : true)
+
+        bpf_o_unstripped_cmd += ['-I' + fs.parent(vmlinux_h_dependency.full_path())]
+        message('Using generated @0@'.format(vmlinux_h_dependency.full_path()))
+else
+        message('Using neither provided nor generated vmlinux.h, some features will not be available.')
+endif
+
+conf.set10('HAVE_VMLINUX_H', use_provided_vmlinux_h or use_generated_vmlinux_h)
+
+#####################################################################
+
 check_version_history_py = find_program('tools/check-version-history.py')
 elf2efi_py = find_program('tools/elf2efi.py')
 export_dbus_interfaces_py = find_program('tools/dbus_exporter.py')
@@ -1951,8 +2039,7 @@ libsystemd = shared_library(
         include_directories : libsystemd_includes,
         link_args : ['-shared',
                      '-Wl,--version-script=' + libsystemd_sym_path],
-        link_with : [libbasic,
-                     libbasic_gcrypt],
+        link_with : [libbasic],
         link_whole : [libsystemd_static],
         dependencies : [librt,
                         threads,
@@ -1968,7 +2055,6 @@ install_libsystemd_static = static_library(
         'systemd',
         libsystemd_sources,
         basic_sources,
-        basic_gcrypt_sources,
         fundamental_sources,
         include_directories : libsystemd_includes,
         build_by_default : static_libsystemd != 'false',
@@ -1979,7 +2065,7 @@ install_libsystemd_static = static_library(
         dependencies : [libblkid,
                         libcap,
                         libdl,
-                        libgcrypt,
+                        libgcrypt_cflags,
                         liblz4_cflags,
                         libmount,
                         libopenssl,
@@ -2194,11 +2280,13 @@ subdir('src/locale')
 subdir('src/login')
 subdir('src/machine')
 subdir('src/machine-id-setup')
+subdir('src/mountfsd')
 subdir('src/modules-load')
 subdir('src/mount')
 subdir('src/network')
 subdir('src/notify')
 subdir('src/nspawn')
+subdir('src/nsresourced')
 subdir('src/nss-myhostname')
 subdir('src/nss-mymachines')
 subdir('src/nss-resolve')
@@ -2722,6 +2810,7 @@ summary({
         'PAM modules directory' :           pamlibdir,
         'PAM configuration directory' :     pamconfdir,
         'ssh server configuration directory' : sshdconfdir,
+        'ssh server privilege separation directory' : sshdprivsepdir,
         'ssh client configuration directory' : sshconfdir,
         'libcryptsetup plugins directory' : libcryptsetup_plugins_dir,
         'RPM macros directory' :            rpmmacrosdir,
index 3a3ab6e7c1a3c1b6c0f7384aac08e6166efeff27..41a524b0dcb5fd2552d0e555fb93a6ee6eed07b4 100644 (file)
@@ -124,6 +124,8 @@ option('portabled', type : 'boolean',
        description : 'install the systemd-portabled stack')
 option('sysext', type : 'boolean',
        description : 'install the systemd-sysext stack')
+option('mountfsd', type : 'boolean',
+       description : 'install the systemd-mountfsd stack')
 option('userdb', type : 'boolean',
        description : 'install the systemd-userdbd stack')
 option('homed', type : 'feature', deprecated : { 'true' : 'enabled', 'false' : 'disabled' },
@@ -140,6 +142,8 @@ option('remote', type : 'feature', deprecated : { 'true' : 'enabled', 'false' :
        description : 'support for "journal over the network"')
 option('create-log-dirs', type : 'boolean',
        description : 'create /var/log/journal{,/remote}')
+option('nsresourced', type : 'boolean',
+       description : 'install the systemd-nsresourced stack')
 option('nss-myhostname', type : 'boolean',
        description : 'install nss-myhostname module')
 option('nss-mymachines', type : 'feature', deprecated : { 'true' : 'enabled', 'false' : 'disabled' },
@@ -215,6 +219,8 @@ 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('sshdprivsepdir', type : 'string',
+       description : 'directory for SSH privilege separation ["no" disables]', value : '/run/sshd')
 option('libcryptsetup-plugins-dir', type : 'string',
        description : 'directory for libcryptsetup plugins')
 option('docdir', type : 'string',
@@ -517,6 +523,13 @@ option('analyze', type: 'boolean', value: true,
        description : 'install systemd-analyze')
 
 option('bpf-compiler', type : 'combo', choices : ['clang', 'gcc'],
-    description: 'compiler used to build BPF programs')
+       description : 'compiler used to build BPF programs')
 option('bpf-framework', type : 'feature', deprecated : { 'true' : 'enabled', 'false' : 'disabled' },
-    description: 'build BPF programs from source code in restricted C')
+       description : 'build BPF programs from source code in restricted C')
+option('vmlinux-h', type : 'combo', choices : ['auto', 'provided', 'generated', 'disabled'],
+       description : 'which vmlinux.h to use')
+option('vmlinux-h-path', type : 'string', value : '',
+       description : 'path to vmlinux.h to use')
+
+option('default-mountfsd-trusted-directories', type : 'boolean', value: false,
+       description : 'controls whether mountfsd should apply a relaxed policy on DDIs in system DDI directories')
index a5953d7613639eccf7c34ea3414c97d2f9015836..ee7fa6e2a48d1b65319bc8e8b7415b2ee5a6d30d 100644 (file)
@@ -2,7 +2,7 @@
 
 [Config]
 Images=system
-MinimumVersion=21
+MinimumVersion=23~devel
 
 [Output]
 @OutputDirectory=mkosi.output
@@ -19,12 +19,11 @@ BuildSourcesEphemeral=yes
 
 [Host]
 @Incremental=yes
-# TODO: Drop to 2G again once the next Noble kernel update ships and we can use linux-image-virtual.
-@QemuMem=4G
 @RuntimeSize=8G
+@RuntimeBuildSources=yes
 ToolsTreePackages=virtiofsd
 KernelCommandLineExtra=systemd.crash_shell
-                       systemd.log_level=debug
+                       systemd.log_level=debug,console:info
                        systemd.log_ratelimit_kmsg=0
                        systemd.journald.forward_to_console
                        systemd.journald.max_level_console=warning
diff --git a/mkosi.images/base/mkosi.conf b/mkosi.images/base/mkosi.conf
deleted file mode 100644 (file)
index aed8bb5..0000000
+++ /dev/null
@@ -1,55 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-
-[Output]
-Format=directory
-
-[Content]
-Autologin=yes
-# Make sure we build the default initrd as part of the base image as it will have access to the systemd and
-# udev rpms which are built by the build scripts that are part of the base image.
-Bootable=yes
-# we want to build the UKI as part of the system image so make sure none are built here.
-Bootloader=none
-CleanPackageMetadata=no
-
-Packages=
-        acl
-        bash-completion
-        coreutils
-        diffutils
-        dnsmasq
-        dosfstools
-        e2fsprogs
-        findutils
-        gcc # Sanitizer libraries
-        gdb
-        git
-        grep
-        gzip
-        jq
-        kbd
-        kexec-tools
-        kmod
-        less
-        man
-        mtools
-        nano
-        nftables
-        openssl
-        python3
-        qrencode
-        rsync
-        sed
-        socat
-        strace
-        systemd
-        tar
-        tmux
-        tree
-        udev
-        util-linux
-        valgrind
-        wireguard-tools
-        xfsprogs
-        zsh
-        zstd
diff --git a/mkosi.images/base/mkosi.conf.d/10-arch/initrd/mkosi.postinst b/mkosi.images/base/mkosi.conf.d/10-arch/initrd/mkosi.postinst
deleted file mode 100755 (executable)
index ad4fe6e..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-#!/bin/bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -e
-
-mkosi-install systemd systemd-sysvcompat
diff --git a/mkosi.images/base/mkosi.conf.d/10-arch/mkosi.prepare b/mkosi.images/base/mkosi.conf.d/10-arch/mkosi.prepare
deleted file mode 100755 (executable)
index 24c91e5..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
-#!/bin/bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -e
-
-if [ ! -f "pkg/$PKG_SUBDIR/PKGBUILD" ]; then
-    echo "PKGBUILD not found at pkg/$PKG_SUBDIR/PKGBUILD, run mkosi once with -ff to make sure the PKGBUILD is cloned" >&2
-    exit 1
-fi
-
-if [ "$1" = "final" ]; then
-    # We get depends and optdepends from .SRCINFO as getting them from the PKGBUILD is rather complex.
-    sed --expression 's/^[ \t]*//' "pkg/$PKG_SUBDIR/.SRCINFO" |
-        grep --regexp '^depends =' --regexp '^optdepends =' |
-        sed --expression 's/^depends = //' --expression 's/^optdepends = //' --expression 's/:.*//' |
-        xargs --delimiter '\n' mkosi-install
-else
-    # We get makedepends from the PKGBUILD as .SRCINFO can't encode conditional dependencies depending on
-    # whether some environment variable is set or not.
-    # shellcheck source=/dev/null
-    UPSTREAM=1 . "pkg/$PKG_SUBDIR/PKGBUILD"
-
-    # shellcheck disable=SC2154
-    mkosi-install "${makedepends[@]}"
-fi
diff --git a/mkosi.images/base/mkosi.conf.d/10-centos-fedora/initrd/mkosi.postinst b/mkosi.images/base/mkosi.conf.d/10-centos-fedora/initrd/mkosi.postinst
deleted file mode 100755 (executable)
index 0b7a4cb..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-#!/bin/bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -e
-
-mkosi-install systemd systemd-udev
diff --git a/mkosi.images/base/mkosi.conf.d/10-centos/mkosi.conf b/mkosi.images/base/mkosi.conf.d/10-centos/mkosi.conf
deleted file mode 100644 (file)
index 0f57319..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-
-[Match]
-Distribution=centos
-
-[Content]
-Environment=
-        PKG_SUBDIR="centos"
-
-Packages=
-        kernel-modules # For squashfs support
-        rpmautospec-rpm-macros
diff --git a/mkosi.images/base/mkosi.conf.d/10-debian-ubuntu/mkosi.conf b/mkosi.images/base/mkosi.conf.d/10-debian-ubuntu/mkosi.conf
deleted file mode 100644 (file)
index 07c9b3a..0000000
+++ /dev/null
@@ -1,61 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-
-[Match]
-Distribution=|debian
-Distribution=|ubuntu
-
-[Config]
-InitrdInclude=initrd/
-
-[Content]
-Environment=
-        PKG_SUBDIR="debian"
-        SYSTEMD_PACKAGES="systemd
-                          systemd-userdbd
-                          systemd-oomd
-                          systemd-sysv
-                          systemd-tests
-                          systemd-timesyncd
-                          systemd-resolved
-                          systemd-homed
-                          systemd-coredump
-                          systemd-journal-remote
-                          systemd-container
-                          systemd-boot
-                          systemd-ukify
-                          udev"
-
-Packages=
-        ^libtss2-esys-[0-9.]+-0$
-        ^libtss2-mu-[0-9.]+-0$
-        apt
-        btrfs-progs
-        cryptsetup-bin
-        dbus-broker
-        default-dbus-session-bus
-        dmsetup
-        f2fs-tools
-        fdisk
-        iproute2
-        isc-dhcp-server
-        libcap-ng-utils
-        libtss2-rc0
-        libtss2-tcti-device0
-        man-db
-        netcat-openbsd
-        openssh-client
-        openssh-server
-        passwd
-        policykit-1
-        procps
-        quota
-        sbsigntool
-        tzdata
-        xxd
-
-InitrdPackages=
-        btrfs-progs
-        tpm2-tools
-
-BuildPackages=
-        dpkg-dev
diff --git a/mkosi.images/base/mkosi.conf.d/10-debian-ubuntu/mkosi.prepare b/mkosi.images/base/mkosi.conf.d/10-debian-ubuntu/mkosi.prepare
deleted file mode 100755 (executable)
index ae0d6fd..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-#!/bin/bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -e
-
-if [ "$1" = "final" ]; then
-    exit 0
-fi
-
-if [ ! -d "pkg/$PKG_SUBDIR/debian" ]; then
-    echo "deb rules not found at pkg/$PKG_SUBDIR/debian, run mkosi once with -ff to make sure the rules are cloned" >&2
-    exit 1
-fi
-
-cd "pkg/$PKG_SUBDIR"
-DEB_BUILD_PROFILES="pkg.systemd.upstream" apt-get build-dep .
diff --git a/mkosi.images/base/mkosi.conf.d/10-ubuntu/mkosi.conf b/mkosi.images/base/mkosi.conf.d/10-ubuntu/mkosi.conf
deleted file mode 100644 (file)
index 85126a1..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-
-[Match]
-Distribution=ubuntu
-
-[Content]
-Packages=
-        # We would like to use linux-virtual but it does not have support for SMBIOS credentials.
-        linux-image-generic
-        linux-tools-common
-        linux-tools-generic
diff --git a/mkosi.images/base/mkosi.extra/usr/share/factory/mkosi/gdbinit.d/systemd.gdb b/mkosi.images/base/mkosi.extra/usr/share/factory/mkosi/gdbinit.d/systemd.gdb
deleted file mode 100644 (file)
index 26f882b..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-set debuginfod enabled off
-set build-id-verbose 0
-set substitute-path ../src /root/src/systemd
diff --git a/mkosi.images/base/mkosi.postinst b/mkosi.images/base/mkosi.postinst
deleted file mode 100755 (executable)
index caaf253..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-#!/bin/bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -e
-
-# shellcheck disable=SC2086
-mkosi-install $SYSTEMD_PACKAGES
diff --git a/mkosi.images/base/mkosi.sync b/mkosi.images/base/mkosi.sync
deleted file mode 100755 (executable)
index 72b5cf7..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-#!/bin/bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -e
-
-if [ -z "$(ls --almost-all "pkg/$PKG_SUBDIR")" ] || [ -f "pkg/$PKG_SUBDIR/.git" ]; then
-    git submodule sync "pkg/$PKG_SUBDIR"
-    git submodule update --init "pkg/$PKG_SUBDIR"
-fi
diff --git a/mkosi.images/system/mkosi.clean b/mkosi.images/system/mkosi.clean
new file mode 100755 (executable)
index 0000000..cb716c4
--- /dev/null
@@ -0,0 +1,4 @@
+#!/bin/bash
+set -e
+
+rm -f "$OUTPUTDIR"/*.{rpm,deb,pkg.tar}
index 9937ad3a982277d94b9234d36a48eb2c9d878532..ed09d841b843bf4af049571d6a3b9dee24c35253 100644 (file)
@@ -1,17 +1,50 @@
 # SPDX-License-Identifier: LGPL-2.1-or-later
 
-[Config]
-Dependencies=base
-
-[Distribution]
-CacheOnly=metadata
-
 [Output]
 @Format=directory
 
 [Content]
-BaseTrees=%O/base
-Initrds=%O/base.initrd
+Autologin=yes
+Packages=
+        acl
+        bash-completion
+        coreutils
+        diffutils
+        dnsmasq
+        dosfstools
+        e2fsprogs
+        findutils
+        gdb
+        grep
+        gzip
+        jq
+        kbd
+        kexec-tools
+        kmod
+        less
+        man
+        mtools
+        nano
+        nftables
+        openssl
+        python3
+        qrencode
+        rsync
+        sed
+        socat
+        strace
+        systemd
+        tar
+        tmux
+        tree
+        udev
+        util-linux
+        valgrind
+        which
+        wireguard-tools
+        xfsprogs
+        zsh
+        zstd
 
 [Validation]
 @SecureBoot=yes
similarity index 69%
rename from mkosi.images/base/mkosi.conf.d/10-arch/mkosi.build.chroot
rename to mkosi.images/system/mkosi.conf.d/10-arch/mkosi.build.chroot
index 1c5f582701863147d26cc1c3eb090cf1879fdbfe..fb8558036023efdf7f23d1a4d0866eece81affca 100755 (executable)
@@ -2,17 +2,20 @@
 # SPDX-License-Identifier: LGPL-2.1-or-later
 set -ex
 
-if [ ! -f "pkg/$PKG_SUBDIR/PKGBUILD" ]; then
-    echo "PKGBUILD not found at pkg/$PKG_SUBDIR/PKGBUILD, run mkosi once with -ff to make sure the PKGBUILD is cloned" >&2
+# shellcheck source=/dev/null
+. /usr/lib/os-release
+
+if [ ! -f "pkg/$ID/PKGBUILD" ]; then
+    echo "PKGBUILD not found at pkg/$ID/PKGBUILD, run mkosi once with -ff to make sure the PKGBUILD is cloned" >&2
     exit 1
 fi
 
 # We can't configure the source or build directory so we use bind mounts instead to make sure they are in the
 # expected locations.
-mount --mkdir --bind "$SRCDIR" "pkg/$PKG_SUBDIR/systemd-stable/"
-mount --mkdir --bind "$BUILDDIR" "pkg/$PKG_SUBDIR/build/"
+mount --mkdir --bind "$SRCDIR" "pkg/$ID/systemd-stable/"
+mount --mkdir --bind "$BUILDDIR" "pkg/$ID/build/"
 # Because we run with --noextract we are responsible for making sure the source files appear in src/.
-mount --mkdir --rbind "$PWD/pkg/$PKG_SUBDIR" "pkg/$PKG_SUBDIR/src/"
+mount --mkdir --rbind "$PWD/pkg/$ID" "pkg/$ID/src/"
 
 # shellcheck source=/dev/null
 . /etc/makepkg.conf
@@ -23,7 +26,17 @@ mount --mkdir --rbind "$PWD/pkg/$PKG_SUBDIR" "pkg/$PKG_SUBDIR/src/"
 # tmpfs during the build script so these changes don't end up in the image itself.
 tee --append /etc/makepkg.conf >/dev/null <<EOF
 CFLAGS="$CFLAGS -Og"
-OPTIONS=(!strip docs !libtool !staticlibs emptydirs !zipman purge !debug !lto)
+OPTIONS=(
+    docs
+    !libtool
+    !staticlibs
+    emptydirs
+    !zipman
+    purge
+    $( ((WITH_DEBUG)) && echo strip || echo !strip)
+    $( ((WITH_DEBUG)) && echo debug || echo !debug)
+    !lto
+)
 EOF
 
 # Linting the PKGBUILD takes multiple seconds every build so avoid that by nuking all the linting functions.
@@ -35,13 +48,13 @@ else
     TS="${SOURCE_DATE_EPOCH:-$(date +%s)}"
 fi
 
-sed --in-place "pkg/$PKG_SUBDIR/PKGBUILD" \
+sed --in-place "pkg/$ID/PKGBUILD" \
     --expression "s/^_tag=.*/_tag=$(cat meson.version)/" \
     --expression "s/^pkgrel=.*/pkgrel=$(date "+%Y%m%d%H%M%S" --date "@$TS")/"
 
 # We get around makepkg's root check by setting EUID to something else.
 # shellcheck disable=SC2046
-env --chdir="pkg/$PKG_SUBDIR" \
+env --chdir="pkg/$ID" \
     EUID=123 \
     makepkg \
     --noextract \
@@ -49,7 +62,9 @@ env --chdir="pkg/$PKG_SUBDIR" \
     --force \
     _systemd_UPSTREAM=1 \
     _systemd_QUIET=1 \
-    BUILDDIR="$PWD/pkg/$PKG_SUBDIR" \
-    PKGDEST="$PACKAGEDIR" \
+    BUILDDIR="$PWD/pkg/$ID" \
+    PKGDEST="$OUTPUTDIR" \
     PKGEXT=".pkg.tar" \
     MESON_EXTRA_CONFIGURE_OPTIONS="-D mode=developer -D b_sanitize=${SANITIZERS:-none}"
+
+cp "$OUTPUTDIR"/*.pkg.tar "$PACKAGEDIR"
similarity index 57%
rename from mkosi.images/base/mkosi.conf.d/10-arch/mkosi.conf
rename to mkosi.images/system/mkosi.conf.d/10-arch/mkosi.conf
index 9cb45cc954d8d3f67d89c58af7b9b97ca0384d41..92d58292f4c7919a390484f720652de1d23825fd 100644 (file)
@@ -3,24 +3,26 @@
 [Match]
 Distribution=arch
 
-[Config]
-InitrdInclude=initrd/
-
-# TODO: Switch to https://gitlab.archlinux.org/archlinux/packaging/packages/systemd once
-# https://gitlab.archlinux.org/archlinux/packaging/packages/systemd/-/merge_requests/8 is merged.
 [Content]
-Environment=
-        PKG_SUBDIR="arch"
-        SYSTEMD_PACKAGES="systemd systemd-ukify systemd-sysvcompat systemd-resolvconf"
+VolatilePackages=
+        systemd
+        systemd-ukify
+        systemd-sysvcompat
+        systemd-resolvconf
+        systemd-tests
 
 Packages=
         bpf
         btrfs-progs
         compsize
         cryptsetup
-        dbus
+        dbus-broker
+        dbus-broker-units
+        debugedit
         dhcp
         f2fs-tools
+        fakeroot
+        git
         gnutls
         iproute
         linux
@@ -29,8 +31,8 @@ Packages=
         openssh
         openssl
         pacman
+        pkgconf
         polkit
-        qrencode
         quota-tools
         sbsigntools
         shadow
@@ -41,7 +43,6 @@ InitrdPackages=
         btrfs-progs
         tpm2-tools
 
-BuildPackages=
-        fakeroot
-        pkgconf
-        debugedit
+InitrdVolatilePackages=
+        systemd
+        systemd-sysvcompat
diff --git a/mkosi.images/system/mkosi.conf.d/10-arch/mkosi.prepare b/mkosi.images/system/mkosi.conf.d/10-arch/mkosi.prepare
new file mode 100755 (executable)
index 0000000..8ed5c75
--- /dev/null
@@ -0,0 +1,29 @@
+#!/bin/bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -e
+
+if [ "$1" = "build" ]; then
+    exit 0
+fi
+
+# shellcheck source=/dev/null
+. "$BUILDROOT/usr/lib/os-release"
+
+if [ ! -f "pkg/$ID/PKGBUILD" ]; then
+    echo "PKGBUILD not found at pkg/$ID/PKGBUILD, run mkosi once with -ff to make sure the PKGBUILD is cloned" >&2
+    exit 1
+fi
+
+# We get depends and optdepends from .SRCINFO as getting them from the PKGBUILD is rather complex.
+sed --expression 's/^[ \t]*//' "pkg/$ID/.SRCINFO" |
+    grep --regexp '^depends =' --regexp '^optdepends =' |
+    sed --expression 's/^depends = //' --expression 's/^optdepends = //' --expression 's/:.*//' |
+    xargs --delimiter '\n' mkosi-install
+
+# We get makedepends from the PKGBUILD as .SRCINFO can't encode conditional dependencies depending on
+# whether some environment variable is set or not.
+# shellcheck source=/dev/null
+_systemd_UPSTREAM=1 . "pkg/$ID/PKGBUILD"
+
+# shellcheck disable=SC2154
+mkosi-install "${makedepends[@]}"
similarity index 63%
rename from mkosi.images/base/mkosi.conf.d/10-centos-fedora/mkosi.build.chroot
rename to mkosi.images/system/mkosi.conf.d/10-centos-fedora/mkosi.build.chroot
index 73c1910f8b641d0c731ce510ebceb7dc65c63ac0..e2d20d4b87ee84981a11a251662ae4fb6b0fabea 100755 (executable)
@@ -2,8 +2,11 @@
 # SPDX-License-Identifier: LGPL-2.1-or-later
 set -e
 
-if [ ! -f "pkg/$PKG_SUBDIR/systemd.spec" ]; then
-    echo "spec not found at pkg/$PKG_SUBDIR/systemd.spec, run mkosi once with -ff to make sure the spec is cloned" >&2
+# shellcheck source=/dev/null
+. /usr/lib/os-release
+
+if [ ! -f "pkg/$ID/systemd.spec" ]; then
+    echo "spec not found at pkg/$ID/systemd.spec, run mkosi once with -ff to make sure the spec is cloned" >&2
     exit 1
 fi
 
@@ -13,6 +16,15 @@ else
     TS="${SOURCE_DATE_EPOCH:-$(date +%s)}"
 fi
 
+# Fix the %install override so debuginfo packages are generated even when --build-in-place is used.
+# See https://github.com/rpm-software-management/rpm/issues/3042.
+tee --append /usr/lib/rpm/redhat/macros <<'EOF'
+%install %{?_enable_debug_packages:%{debug_package}}\
+%%install\
+%{nil}
+EOF
+
+IFS=
 # TODO: Replace meson_build and meson_install overrides with "--undefine __meson_verbose" once
 # https://github.com/mesonbuild/meson/pull/12835 is available.
 # shellcheck disable=SC2046
@@ -20,22 +32,25 @@ rpmbuild \
     -bb \
     --build-in-place \
     --with upstream \
-    $( ((WITH_TESTS)) || echo --nocheck) \
+    $( ((WITH_TESTS)) || echo "--nocheck") \
+    $( ((WITH_DOCS))  || echo "--without docs") \
     --define "_topdir /var/tmp" \
-    --define "_sourcedir pkg/$PKG_SUBDIR" \
-    --define "_rpmdir $PACKAGEDIR" \
+    --define "_sourcedir pkg/$ID" \
+    --define "_rpmdir $OUTPUTDIR" \
     ${BUILDDIR:+--define} \
     ${BUILDDIR:+"_vpath_builddir $BUILDDIR"} \
     --define "_build_name_fmt %%{NAME}-%%{VERSION}-%%{RELEASE}.%%{ARCH}.rpm" \
     --define "_binary_payload w.ufdio" \
-    --define "debug_package %{nil}" \
+    $( ((WITH_DEBUG)) || echo --define) \
+    $( ((WITH_DEBUG)) || echo "debug_package %{nil}") \
     --define "version_override $(cat meson.version)" \
     --define "release_override $(date "+%Y%m%d%H%M%S" --date "@$TS")" \
     --define "_distro_extra_cflags -Og" \
     --define "meson_build %{shrink:%{__meson} compile -C %{_vpath_builddir} -j %{_smp_build_ncpus} %{nil}}" \
     --define "meson_install %{shrink:DESTDIR=%{buildroot} %{__meson} install -C %{_vpath_builddir} --no-rebuild --quiet %{nil}}" \
     --define "meson_extra_configure_options -D mode=developer -D b_sanitize=${SANITIZERS:-none}" \
-    --define "__brp_strip %{nil}" \
+    $( ((WITH_DEBUG)) || echo --define) \
+    $( ((WITH_DEBUG)) || echo "__brp_strip %{nil}") \
     --define "__brp_compress %{nil}" \
     --define "__brp_mangle_shebangs %{nil}" \
     --define "__brp_strip_comment_note %{nil}" \
@@ -44,4 +59,7 @@ rpmbuild \
     --define "__elf_exclude_path ^/usr/lib/systemd/tests/unit-tests/.*$" \
     --define "__script_requires %{nil}" \
     --undefine _lto_cflags \
-    "pkg/$PKG_SUBDIR/systemd.spec"
+    --noclean \
+    "pkg/$ID/systemd.spec"
+
+cp "$OUTPUTDIR"/*.rpm "$PACKAGEDIR"
similarity index 51%
rename from mkosi.images/base/mkosi.conf.d/10-centos-fedora/mkosi.conf
rename to mkosi.images/system/mkosi.conf.d/10-centos-fedora/mkosi.conf
index a9ee4a29062a7af0ff66724850c2a5858ff0bc1b..cc9f3e9d11ccba64d7ae0670d473b63c6704efc0 100644 (file)
@@ -4,29 +4,28 @@
 Distribution=|centos
 Distribution=|fedora
 
-[Config]
-InitrdInclude=initrd/
-
 [Content]
-Environment=
-        SYSTEMD_PACKAGES="systemd
-                          systemd-udev
-                          systemd-container
-                          systemd-repart
-                          systemd-resolved
-                          systemd-networkd
-                          systemd-boot
-                          systemd-tests
-                          systemd-ukify
-                          systemd-pam
-                          systemd-oomd-defaults
-                          systemd-journal-remote
-                          systemd-networkd-defaults"
+VolatilePackages=
+        systemd
+        systemd-udev
+        systemd-container
+        systemd-repart
+        systemd-resolved
+        systemd-networkd
+        systemd-boot
+        systemd-tests
+        systemd-ukify
+        systemd-pam
+        systemd-oomd-defaults
+        systemd-journal-remote
+        systemd-networkd-defaults
+
 Packages=
         bpftool
         cryptsetup
         dhcp-server
         dnf
+        git-core
         gnutls
         integritysetup
         iproute
@@ -48,10 +47,16 @@ Packages=
         rpm
         rpm-build
         rpmautospec
+        selinux-policy
+        selinux-policy-targeted
+        setools-console
+        policycoreutils
         util-linux
         vim-common
 
 InitrdPackages=
-        setools
-        selinux-policy
         tpm2-tools
+
+InitrdVolatilePackages=
+        systemd
+        systemd-udev
similarity index 57%
rename from mkosi.images/base/mkosi.conf.d/10-centos-fedora/mkosi.prepare
rename to mkosi.images/system/mkosi.conf.d/10-centos-fedora/mkosi.prepare
index 5bcb1ad481fefaf8065355033956b666f9f1e4f3..8b10b242e0e2ee7392425c87b95e2ead83bda167 100755 (executable)
@@ -2,38 +2,37 @@
 # SPDX-License-Identifier: LGPL-2.1-or-later
 set -e
 
-if [ ! -f "pkg/$PKG_SUBDIR/systemd.spec" ]; then
-    echo "spec not found at pkg/$PKG_SUBDIR/systemd.spec, run mkosi with -ff to make sure the spec is cloned" >&2
-    exit 1
-fi
-
-if [ "$1" = "final" ]; then
-    DEPS="--requires"
-else
-    DEPS="--buildrequires"
+if [ "$1" = "build" ]; then
+    exit 0
 fi
 
-mkosi-chroot \
-    rpmspec \
-    --with upstream \
-    --query \
-    "$DEPS" \
-    --define "_topdir /var/tmp" \
-    --define "_sourcedir pkg/$PKG_SUBDIR" \
-    "pkg/$PKG_SUBDIR/systemd.spec" |
-        grep --invert-match --regexp systemd --regexp /bin/sh --regexp "rpmlib(" --regexp udev |
-        sort --unique |
-        tee /tmp/buildrequires |
-        xargs --delimiter '\n' mkosi-install
+# shellcheck source=/dev/null
+. "$BUILDROOT/usr/lib/os-release"
 
-if [ "$1" = "final" ]; then
-    exit 0
+if [ ! -f "pkg/$ID/systemd.spec" ]; then
+    echo "spec not found at pkg/$ID/systemd.spec, run mkosi with -ff to make sure the spec is cloned" >&2
+    exit 1
 fi
 
+for DEPS in --requires --buildrequires; do
+    mkosi-chroot \
+        rpmspec \
+        --with upstream \
+        --query \
+        "$DEPS" \
+        --define "_topdir /var/tmp" \
+        --define "_sourcedir pkg/$ID" \
+        "pkg/$ID/systemd.spec" |
+            grep --invert-match --regexp systemd --regexp /bin/sh --regexp "rpmlib(" --regexp udev |
+            sort --unique |
+            tee /tmp/buildrequires |
+            xargs --delimiter '\n' mkosi-install
+done
+
 # rpmbuild -br tries to build a source package which means all source files have to exist which isn't the
 # case when using --build-in-place so we get rid of the source file that doesn't exist to make it happy.
 # TODO: Use -bd instead of -br and get rid of this once we don't need to build on CentOS Stream 9 anymore.
-sed '/Source0/d' --in-place "pkg/$PKG_SUBDIR/systemd.spec"
+sed '/Source0/d' --in-place "pkg/$ID/systemd.spec"
 
 until mkosi-chroot \
     rpmbuild \
@@ -41,9 +40,9 @@ until mkosi-chroot \
     --build-in-place \
     --with upstream \
     --define "_topdir /var/tmp" \
-    --define "_sourcedir pkg/$PKG_SUBDIR" \
+    --define "_sourcedir pkg/$ID" \
     --define "_build_name_fmt %%{NAME}-%%{VERSION}-%%{RELEASE}.%%{ARCH}.rpm" \
-    "pkg/$PKG_SUBDIR/systemd.spec"
+    "pkg/$ID/systemd.spec"
 do
     EXIT_STATUS=$?
     if [ $EXIT_STATUS -ne 11 ]; then
index af4862d4b147031ac251a0d823833b2afab1557e..d32eae4952312ecd6e5d0321fb3d525ab67ff11c 100644 (file)
@@ -2,3 +2,8 @@
 
 [Match]
 Distribution=centos
+
+[Content]
+Packages=
+        kernel-modules # For squashfs support
+        rpmautospec-rpm-macros
similarity index 81%
rename from mkosi.images/base/mkosi.conf.d/10-debian-ubuntu/mkosi.build.chroot
rename to mkosi.images/system/mkosi.conf.d/10-debian-ubuntu/mkosi.build.chroot
index 7fb650f5013bdf8f8df235e70309ea4e050ac371..3a89e49c5bb0dfe3190a0527edede831a365342b 100755 (executable)
@@ -2,13 +2,16 @@
 # SPDX-License-Identifier: LGPL-2.1-or-later
 set -e
 
-if [ ! -d "pkg/$PKG_SUBDIR/debian" ]; then
-    echo "deb rules not found at pkg/$PKG_SUBDIR/debian, run mkosi once with -ff to make sure the rules are cloned" >&2
+# shellcheck source=/dev/null
+. /usr/lib/os-release
+
+if [ ! -d "pkg/$ID/debian" ]; then
+    echo "deb rules not found at pkg/$ID/debian, run mkosi once with -ff to make sure the rules are cloned" >&2
     exit 1
 fi
 
 # We transplant the debian/ folder from the deb package sources into the upstream sources.
-mount --mkdir --bind "$SRCDIR/pkg/$PKG_SUBDIR/debian" "$SRCDIR"/debian
+mount --mkdir --bind "$SRCDIR/pkg/$ID/debian" "$SRCDIR"/debian
 
 # We hide the patches/ directory by mounting an empty directory on top so they don't get applied.
 TMP=$(mktemp -d)
@@ -28,7 +31,7 @@ fi
 # Add a new changelog entry to update the version. We use a fixed date since a dynamic one causes a full
 # rebuild every time.
 cat >debian/changelog.new <<EOF
-systemd ($(cat meson.version).$(date "+%Y%m%d%H%M%S" --date "@$TS")) UNRELEASED; urgency=low
+systemd ($(cat meson.version)-$(date "+%Y%m%d%H%M%S" --date "@$TS")) UNRELEASED; urgency=low
 
   * Automatic build from mkosi
 
@@ -39,8 +42,18 @@ cat debian/changelog >>debian/changelog.new
 mv debian/changelog.new debian/changelog
 
 build() {
-    DEB_BUILD_OPTIONS="$( ((WITH_TESTS)) || echo nocheck) $( ((WITH_DOCS)) || echo nodoc) nostrip terse optimize=-lto" \
-    DEB_BUILD_PROFILES="$( ((WITH_TESTS)) || echo nocheck) $( ((WITH_DOCS)) || echo nodoc) pkg.systemd.upstream" \
+    DEB_BUILD_OPTIONS="\
+        $( ((WITH_TESTS)) || echo nocheck) \
+        $( ((WITH_DOCS)) || echo nodoc) \
+        $( ((WITH_DEBUG)) || echo nostrip) \
+        terse
+        optimize=-lto \
+    " \
+    DEB_BUILD_PROFILES="\
+        $( ((WITH_TESTS)) || echo nocheck) \
+        $( ((WITH_DOCS)) || echo nodoc) \
+        pkg.systemd.upstream \
+    " \
     DEB_CFLAGS_APPEND="-Og" \
     DPKG_FORCE="unsafe-io" \
     DPKG_DEB_COMPRESSOR_TYPE="none" \
@@ -86,4 +99,5 @@ if ! build; then
     build
 fi
 
-mv ../*.deb "$PACKAGEDIR"
+cp ../*.deb "$PACKAGEDIR"
+cp ../*.deb "$OUTPUTDIR"
diff --git a/mkosi.images/system/mkosi.conf.d/10-debian-ubuntu/mkosi.conf b/mkosi.images/system/mkosi.conf.d/10-debian-ubuntu/mkosi.conf
new file mode 100644 (file)
index 0000000..7ece677
--- /dev/null
@@ -0,0 +1,62 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+[Match]
+Distribution=|debian
+Distribution=|ubuntu
+
+[Content]
+VolatilePackages=
+        systemd
+        systemd-userdbd
+        systemd-oomd
+        systemd-sysv
+        systemd-tests
+        systemd-timesyncd
+        systemd-resolved
+        systemd-homed
+        systemd-coredump
+        systemd-journal-remote
+        systemd-container
+        systemd-boot
+        systemd-ukify
+        udev
+
+Packages=
+        ^libasan[0-9]+$
+        ^libtss2-esys-[0-9.]+-0$
+        ^libtss2-mu-[0-9.]+-0$
+        ^libubsan[0-9]+$
+        apt
+        btrfs-progs
+        cryptsetup-bin
+        dbus-broker
+        dbus-user-session
+        dmsetup
+        dpkg-dev
+        f2fs-tools
+        fdisk
+        git-core
+        iproute2
+        isc-dhcp-server
+        libcap-ng-utils
+        libtss2-rc0
+        libtss2-tcti-device0
+        man-db
+        netcat-openbsd
+        openssh-client
+        openssh-server
+        passwd
+        policykit-1
+        procps
+        quota
+        sbsigntool
+        tzdata
+        xxd
+
+InitrdPackages=
+        btrfs-progs
+        tpm2-tools
+
+InitrdVolatilePackages=
+        systemd
+        udev
diff --git a/mkosi.images/system/mkosi.conf.d/10-debian-ubuntu/mkosi.prepare b/mkosi.images/system/mkosi.conf.d/10-debian-ubuntu/mkosi.prepare
new file mode 100755 (executable)
index 0000000..c9b69fc
--- /dev/null
@@ -0,0 +1,18 @@
+#!/bin/bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -e
+
+if [ "$1" = "build" ]; then
+    exit 0
+fi
+
+# shellcheck source=/dev/null
+. "$BUILDROOT/usr/lib/os-release"
+
+if [ ! -d "pkg/$ID/debian" ]; then
+    echo "deb rules not found at pkg/$ID/debian, run mkosi once with -ff to make sure the rules are cloned" >&2
+    exit 1
+fi
+
+cd "pkg/$ID"
+DEB_BUILD_PROFILES="pkg.systemd.upstream" apt-get build-dep .
similarity index 84%
rename from mkosi.images/base/mkosi.conf.d/10-fedora/mkosi.conf
rename to mkosi.images/system/mkosi.conf.d/10-fedora/mkosi.conf
index a0b9f6f0fe4e577c15e1209920a214e017d9fa8d..8236040c0e230bf9f9784443cb3a28a7c23cc503 100644 (file)
@@ -4,15 +4,13 @@
 Distribution=fedora
 
 [Content]
-Environment=
-        PKG_SUBDIR="fedora"
-
 Packages=
         btrfs-progs
         compsize
         f2fs-tools
         glibc-langpack-en
         sbsigntools
+        dnf5
 
 InitrdPackages=
         btrfs-progs
similarity index 82%
rename from mkosi.images/base/mkosi.conf.d/10-opensuse/initrd/mkosi.postinst
rename to mkosi.images/system/mkosi.conf.d/10-opensuse/initrd/mkosi.postinst
index 562bebe08e855c2c960bbafc390c77925a1c1246..417132f3dd0e50652d88c779fc8270ced4956fe5 100755 (executable)
@@ -1,9 +1,7 @@
-#!/bin/sh
+#!/bin/bash
 # SPDX-License-Identifier: LGPL-2.1-or-later
 set -e
 
 # OpenSUSE insists on blacklisting erofs by default because its supposedly a legacy filesystem.
 # See https://github.com/openSUSE/suse-module-tools/pull/71
 rm -f "$BUILDROOT/usr/lib/modprobe.d/60-blacklist_fs-erofs.conf"
-
-mkosi-install systemd udev systemd-experimental
similarity index 73%
rename from mkosi.images/base/mkosi.conf.d/10-opensuse/mkosi.build.chroot
rename to mkosi.images/system/mkosi.conf.d/10-opensuse/mkosi.build.chroot
index b809a10bb01dfbd4e3e4864725714629ea293bdd..fe89611e36ed90ac13da0fbe1ba0b32ad357d82f 100755 (executable)
@@ -2,8 +2,12 @@
 # SPDX-License-Identifier: LGPL-2.1-or-later
 set -e
 
-if [ ! -f "pkg/$PKG_SUBDIR/systemd.spec" ]; then
-    echo "spec not found at pkg/$PKG_SUBDIR/systemd.spec, run mkosi once with -ff to make sure the spec is cloned" >&2
+# shellcheck source=/dev/null
+. /usr/lib/os-release
+ID="${ID%-*}"
+
+if [ ! -f "pkg/$ID/systemd.spec" ]; then
+    echo "spec not found at pkg/$ID/systemd.spec, run mkosi once with -ff to make sure the spec is cloned" >&2
     exit 1
 fi
 
@@ -16,9 +20,17 @@ fi
 # The openSUSE filelists hardcode the manpage compression extension. This causes rpmbuild errors since we
 # disable manpage compression as the files cannot be found. Fix the issue by removing the compression
 # extension.
-find "pkg/$PKG_SUBDIR" -name "files.*" -exec sed --in-place 's/\.gz$//' {} \;
+find "pkg/$ID" -name "files.*" -exec sed --in-place 's/\.gz$//' {} \;
+
+# Fix the %install override so debuginfo packages are generated.
+tee --append /usr/lib/rpm/suse/macros <<'EOF'
+%install %{debug_package}\
+%%install\
+%{nil}
+EOF
 
 build() {
+    IFS=
     # TODO: Replace meson_build and meson_install overrides with "--undefine __meson_verbose" once
     # https://github.com/mesonbuild/meson/pull/12835 is available.
     # shellcheck disable=SC2046
@@ -28,13 +40,14 @@ build() {
         --with upstream \
         $( ((WITH_TESTS)) || echo --nocheck) \
         --define "_topdir /var/tmp" \
-        --define "_sourcedir pkg/$PKG_SUBDIR" \
-        --define "_rpmdir $PACKAGEDIR" \
+        --define "_sourcedir pkg/$ID" \
+        --define "_rpmdir $OUTPUTDIR" \
         ${BUILDDIR:+--define} \
         ${BUILDDIR:+"_vpath_builddir $BUILDDIR"} \
         --define "_build_name_fmt %%{NAME}-%%{VERSION}-%%{RELEASE}.%%{ARCH}.rpm" \
         --define "_binary_payload w.ufdio" \
-        --define "debug_package %{nil}" \
+        $( ((WITH_DEBUG)) || echo --define) \
+        $( ((WITH_DEBUG)) || echo "debug_package %{nil}") \
         --define "vendor openSUSE" \
         --define "version_override $(cat meson.version)" \
         --define "release_override $(date "+%Y%m%d%H%M%S" --date "@$TS")" \
@@ -45,8 +58,9 @@ build() {
         --define "__os_install_post /usr/lib/rpm/brp-suse %{nil}" \
         --define "__elf_exclude_path ^/usr/lib/systemd/tests/unit-tests/.*$" \
         --define "__script_requires %{nil}" \
+        --noclean \
         "$@" \
-        "pkg/$PKG_SUBDIR/systemd.spec"
+        "pkg/$ID/systemd.spec"
 }
 
 if ! build; then
@@ -58,6 +72,8 @@ if ! build; then
     # warnings.
     rm systemd.lang
 
-    cat /tmp/unpackaged-files >>"pkg/$PKG_SUBDIR/files.systemd"
+    cat /tmp/unpackaged-files >>"pkg/$ID/files.systemd"
     build --noprep --nocheck
 fi
+
+cp "$OUTPUTDIR"/*.rpm "$PACKAGEDIR"
similarity index 62%
rename from mkosi.images/base/mkosi.conf.d/10-opensuse/mkosi.conf
rename to mkosi.images/system/mkosi.conf.d/10-opensuse/mkosi.conf
index 45947a395b5371f2fe138422c74ed849077293ef..24d69ccc277f46d8998d8331128f37dd484eae3f 100644 (file)
@@ -7,18 +7,17 @@ Distribution=opensuse
 InitrdInclude=initrd/
 
 [Content]
-Environment=
-        PKG_SUBDIR="opensuse"
-        SYSTEMD_PACKAGES="systemd
-                          udev
-                          systemd-experimental
-                          systemd-boot
-                          systemd-container
-                          systemd-homed
-                          systemd-network
-                          systemd-portable
-                          systemd-sysvcompat
-                          systemd-testsuite"
+VolatilePackages=
+        systemd
+        udev
+        systemd-experimental
+        systemd-boot
+        systemd-container
+        systemd-homed
+        systemd-network
+        systemd-portable
+        systemd-sysvcompat
+        systemd-testsuite
 
 # We install gawk, gzip, grep, xz, sed, rsync and docbook-xsl-stylesheets here explicitly so that the busybox
 # versions don't get installed instead.
@@ -28,17 +27,22 @@ Packages=
         cryptsetup
         dbus-broker
         device-mapper
-        distribution-release
         docbook-xsl-stylesheets
         f2fs-tools
         gawk
+        git-core
         glibc-locale-base
         grep
         gzip
         kernel-kvmsmall
+        kmod
+        libasan8
+        libkmod2
+        libubsan1
         openssh-clients
         openssh-server
         pam
+        patterns-base-minimal_base
         python3-pefile
         quota
         rpm-build
@@ -52,4 +56,11 @@ Packages=
 
 InitrdPackages=
         btrfs-progs
+        kmod
+        libkmod2
         tpm2.0-tools
+
+InitrdVolatilePackages=
+        systemd
+        udev
+        systemd-experimental
similarity index 50%
rename from mkosi.images/base/mkosi.conf.d/10-opensuse/mkosi.prepare
rename to mkosi.images/system/mkosi.conf.d/10-opensuse/mkosi.prepare
index a35a8f3bba2a26adaa8f076dacf118bb62137fc2..492aa0e45fdd75aa2a9fcf1e90b4c1f513d88af3 100755 (executable)
@@ -2,43 +2,43 @@
 # SPDX-License-Identifier: LGPL-2.1-or-later
 set -e
 
-if [ ! -f "pkg/$PKG_SUBDIR/systemd.spec" ]; then
-    echo "spec not found at pkg/$PKG_SUBDIR/systemd.spec, run mkosi once with -ff to make sure the spec is cloned" >&2
-    exit 1
-fi
-
-if [ "$1" = "final" ]; then
-    DEPS="--requires"
-else
-    DEPS="--buildrequires"
+if [ "$1" = "build" ]; then
+    exit 0
 fi
 
-mkosi-chroot \
-    rpmspec \
-    --with upstream \
-    --query \
-    "$DEPS" \
-    --define "_topdir /var/tmp" \
-    --define "_sourcedir pkg/$PKG_SUBDIR" \
-    "pkg/$PKG_SUBDIR/systemd.spec" |
-        grep --invert-match --regexp systemd --regexp /bin/sh --regexp "rpmlib(" --regexp udev |
-        sort --unique |
-        tee /tmp/buildrequires |
-        xargs --delimiter '\n' mkosi-install
+# shellcheck source=/dev/null
+. "$BUILDROOT/usr/lib/os-release"
+ID="${ID%-*}"
 
-if [ "$1" = "final" ]; then
-    exit 0
+if [ ! -f "pkg/$ID/systemd.spec" ]; then
+    echo "spec not found at pkg/$ID/systemd.spec, run mkosi once with -ff to make sure the spec is cloned" >&2
+    exit 1
 fi
 
+for DEPS in --requires --buildrequires; do
+    mkosi-chroot \
+        rpmspec \
+        --with upstream \
+        --query \
+        "$DEPS" \
+        --define "_topdir /var/tmp" \
+        --define "_sourcedir pkg/$ID" \
+        "pkg/$ID/systemd.spec" |
+            grep --invert-match --regexp systemd --regexp /bin/sh --regexp "rpmlib(" --regexp udev |
+            sort --unique |
+            tee /tmp/buildrequires |
+            xargs --delimiter '\n' mkosi-install
+done
+
 until mkosi-chroot \
     rpmbuild \
     -bd \
     --build-in-place \
     --with upstream \
     --define "_topdir /var/tmp" \
-    --define "_sourcedir pkg/$PKG_SUBDIR" \
+    --define "_sourcedir pkg/$ID" \
     --define "_build_name_fmt %%{NAME}-%%{VERSION}-%%{RELEASE}.%%{ARCH}.rpm" \
-    "pkg/$PKG_SUBDIR/systemd.spec"
+    "pkg/$ID/systemd.spec"
 do
     EXIT_STATUS=$?
     if [ $EXIT_STATUS -ne 11 ]; then
diff --git a/mkosi.images/system/mkosi.conf.d/10-ubuntu/mkosi.conf b/mkosi.images/system/mkosi.conf.d/10-ubuntu/mkosi.conf
new file mode 100644 (file)
index 0000000..957d00e
--- /dev/null
@@ -0,0 +1,10 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+[Match]
+Distribution=ubuntu
+
+[Content]
+Packages=
+        linux-image-virtual
+        linux-tools-common
+        linux-tools-virtual
similarity index 78%
rename from mkosi.images/base/mkosi.finalize
rename to mkosi.images/system/mkosi.finalize
index 74b810c152bcbf37ba6ac4d1192e8e85d3f52a1f..6da35e51380ae9b2407fe022ce7f3209692833ba 100755 (executable)
@@ -1,4 +1,5 @@
 #!/bin/sh
 # SPDX-License-Identifier: LGPL-2.1-or-later
 
+mkdir -p "$BUILDROOT"/usr/share/factory/mkosi
 cp --archive --recursive --no-target-directory --reflink=auto "$BUILDROOT"/etc "$BUILDROOT"/usr/share/factory/mkosi
diff --git a/mkosi.images/system/mkosi.sync b/mkosi.images/system/mkosi.sync
new file mode 100755 (executable)
index 0000000..3e42a09
--- /dev/null
@@ -0,0 +1,9 @@
+#!/bin/bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -e
+
+if [ -z "$(ls --almost-all "pkg/$DISTRIBUTION")" ] || [ -f "pkg/$DISTRIBUTION/.git" ]; then
+    PKG_SUBDIR="$(realpath "pkg/$DISTRIBUTION" --relative-to "$PWD")"
+    git submodule sync "$PKG_SUBDIR"
+    git submodule update --init "$PKG_SUBDIR"
+fi
diff --git a/network/80-namespace-ns.network b/network/80-namespace-ns.network
new file mode 100644 (file)
index 0000000..8a84de9
--- /dev/null
@@ -0,0 +1,31 @@
+# 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-namespace-ns.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
+#    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-nsresourced's network support. See systemd-nsresourced(1) for
+# details.
+
+[Match]
+Kind=veth
+Name=ns-*
+
+[Network]
+# Default to using a /28 prefix, giving up to 13 addresses per namespace
+Address=0.0.0.0/28
+LinkLocalAddressing=yes
+DHCPServer=yes
+IPMasquerade=both
+LLDP=yes
+EmitLLDP=customer-bridge
+IPv6AcceptRA=no
+IPv6SendRA=yes
index 9df0bea76096d79971edd0bcba1b5a2e47657176..a1a87272f7ed8fc6874fcefea55dd2067a462658 100644 (file)
@@ -11,6 +11,7 @@ if conf.get('ENABLE_NETWORKD') == 1
                 '80-container-ve.link',
                 '80-container-vz.network',
                 '80-container-vz.link',
+                '80-namespace-ns.network',
                 '80-vm-vt.network',
                 '80-vm-vt.link',
                 '80-wifi-adhoc.network',
index 3b86b9146b84d499789ba924a9dd4ac643d796ab..ccc32ea10164a9b6ca3098765e63f653cddc6817 160000 (submodule)
--- a/pkg/arch
+++ b/pkg/arch
@@ -1 +1 @@
-Subproject commit 3b86b9146b84d499789ba924a9dd4ac643d796ab
+Subproject commit ccc32ea10164a9b6ca3098765e63f653cddc6817
index 3cf45106c8bc5a050901851be6b2ac85b6f5c571..ad880b10ee6bbfbe266c518fc87b8c7a3df962da 160000 (submodule)
@@ -1 +1 @@
-Subproject commit 3cf45106c8bc5a050901851be6b2ac85b6f5c571
+Subproject commit ad880b10ee6bbfbe266c518fc87b8c7a3df962da
index 1932e19d92daef5928a1402073ad3b5aa6fc0767..30c77a7332b5f44cbade27155c0b8e816a75ae7f 160000 (submodule)
@@ -1 +1 @@
-Subproject commit 1932e19d92daef5928a1402073ad3b5aa6fc0767
+Subproject commit 30c77a7332b5f44cbade27155c0b8e816a75ae7f
index f1d38667ef013aa832f43ea7b5861efd29b09fee..a37923658fbe9f511c36d31f556eaada782691eb 160000 (submodule)
@@ -1 +1 @@
-Subproject commit f1d38667ef013aa832f43ea7b5861efd29b09fee
+Subproject commit a37923658fbe9f511c36d31f556eaada782691eb
diff --git a/pkg/ubuntu b/pkg/ubuntu
new file mode 120000 (symlink)
index 0000000..b2f7fd3
--- /dev/null
@@ -0,0 +1 @@
+debian
\ No newline at end of file
index b6be9aa1c27db2a73f309ee990fe3a9c50bac1d4..958ba6a6db0caf194cc76f3c45dfb3f654c75e35 100644 (file)
--- a/po/ja.po
+++ b/po/ja.po
@@ -6,7 +6,7 @@
 msgid ""
 msgstr ""
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2024-03-04 10:09+0100\n"
+"POT-Creation-Date: 2024-04-10 07:06+0900\n"
 "PO-Revision-Date: 2021-09-09 03:04+0000\n"
 "Last-Translator: Takuro Onoue <kusanaginoturugi@gmail.com>\n"
 "Language-Team: Japanese <https://translate.fedoraproject.org/projects/"
@@ -123,24 +123,12 @@ msgid ""
 msgstr "ユーザのホーム領域のパスワードを変更するには認証が必要です。"
 
 #: src/home/org.freedesktop.home1.policy:73
-msgid "Inhibit automatic lock of a home area"
-msgstr ""
-
-#: src/home/org.freedesktop.home1.policy:74
-#, fuzzy
-msgid ""
-"Authentication is required to inhibit automatic lock of a user's home area."
-msgstr "ユーザのホーム領域の更新には認証が必要です。"
-
-#: src/home/org.freedesktop.home1.policy:83
-#, fuzzy
 msgid "Activate a home area"
-msgstr "ホーム領域の作成"
+msgstr "ホーム領域の有効化"
 
-#: src/home/org.freedesktop.home1.policy:84
-#, fuzzy
+#: src/home/org.freedesktop.home1.policy:74
 msgid "Authentication is required to activate a user's home area."
-msgstr "ユーザのホーム領域を作成するには認証が必要です。"
+msgstr "ユーザのホーム領域を有効化するには認証が必要です。"
 
 #: src/home/pam_systemd_home.c:293
 #, c-format
@@ -362,44 +350,37 @@ msgid "Authentication is required to get system description."
 msgstr "システムの説明を取得するには認証が必要です。"
 
 #: src/import/org.freedesktop.import1.policy:22
-#, fuzzy
 msgid "Import a disk image"
-msgstr "仮想マシンもしくはコンテナイメージの読込"
+msgstr "ディスクイメージのの読込"
 
 #: src/import/org.freedesktop.import1.policy:23
-#, fuzzy
 msgid "Authentication is required to import an image"
-msgstr "仮想マシンもしくはコンテナイメージを読込むには認証が必要です"
+msgstr "ディスクイメージを読込むには認証が必要です"
 
 #: src/import/org.freedesktop.import1.policy:32
-#, fuzzy
 msgid "Export a disk image"
-msgstr "仮想マシンもしくはコンテナイメージの書出し"
+msgstr "ディスクイメージの書出し"
 
 #: src/import/org.freedesktop.import1.policy:33
-#, fuzzy
 msgid "Authentication is required to export disk image"
-msgstr "仮想マシンもしくはコンテナイメージを書出すには認証が必要です"
+msgstr "ディスクイメージを書出すには認証が必要です"
 
 #: src/import/org.freedesktop.import1.policy:42
-#, fuzzy
 msgid "Download a disk image"
-msgstr "仮想マシンもしくはコンテナイメージのダウンロード"
+msgstr "ディスクイメージのダウンロード"
 
 #: src/import/org.freedesktop.import1.policy:43
-#, fuzzy
 msgid "Authentication is required to download a disk image"
-msgstr "仮想マシンもしくはコンテナイメージをダウンロードするには認証が必要です"
+msgstr "ディスクイメージをダウンロードするには認証が必要です"
 
 #: src/import/org.freedesktop.import1.policy:52
 msgid "Cancel transfer of a disk image"
-msgstr ""
+msgstr "ディスクイメージの転送を中止"
 
 #: src/import/org.freedesktop.import1.policy:53
-#, fuzzy
 msgid ""
 "Authentication is required to cancel the ongoing transfer of a disk image"
-msgstr "ã\83¦ã\83¼ã\82¶ã\81®ã\83\9bã\83¼ã\83 é \98å\9f\9fã\81®ã\83\91ã\82¹ã\83¯ã\83¼ã\83\89ã\82\92å¤\89æ\9b´ã\81\99ã\82\8bã\81«ã\81¯èª\8d証ã\81\8cå¿\85è¦\81ã\81§ã\81\99ã\80\82"
+msgstr "ã\83\87ã\82£ã\82¹ã\82¯ã\82¤ã\83¡ã\83¼ã\82¸ã\81®è»¢é\80\81ã\82\92中止ã\81\99ã\82\8bã\81«ã\81¯èª\8d証ã\81\8cå¿\85è¦\81ã\81§ã\81\99"
 
 #: src/locale/org.freedesktop.locale1.policy:22
 msgid "Set system locale"
@@ -983,6 +964,16 @@ msgstr "ネットワークインターフェイスの再設定"
 msgid "Authentication is required to reconfigure network interface."
 msgstr "ネットワークインターフェイスの再設定には認証が必要です。"
 
+#: src/network/org.freedesktop.network1.policy:187
+msgid "Specify whether persistent storage for systemd-networkd is available."
+msgstr "systemd-networkdの永続的ストレージの設定"
+
+#: src/network/org.freedesktop.network1.policy:188
+msgid ""
+"Authentication is required to specify whether persistent storage for systemd-"
+"networkd is available."
+msgstr "systemd-networkdの永続的ストレージを設定するには認証が必要です。"
+
 #: src/portable/org.freedesktop.portable1.policy:13
 msgid "Inspect a portable service image"
 msgstr "ポータブルサービスイメージの読み込み"
index 5f1c5b072fabd66d374a54e1ccb0d044bc5c075d..7b7c20a02000bdff064d2f6d3d7af3e4c1ffd1cb 100644 (file)
@@ -23,9 +23,11 @@ enable systemd-homed.service
 enable systemd-homed-activate.service
 enable systemd-homed-firstboot.service
 enable systemd-journald-audit.socket
+enable systemd-mountfsd.socket
 enable systemd-network-generator.service
 enable systemd-networkd.service
 enable systemd-networkd-wait-online.service
+enable systemd-nsresourced.socket
 enable systemd-pstore.service
 enable systemd-resolved.service
 enable systemd-sysext.service
index df52415a8f24753997ace2bf24f3d410e5251c73..ad0c7e2fb5e45fd4af7c39d5ce85edb8a6dc6c06 100644 (file)
@@ -18,19 +18,18 @@ SUBSYSTEM=="ubi", TAG+="systemd"
 
 SUBSYSTEM=="block", TAG+="systemd"
 
-# We can't make any conclusions about suspended DM devices so let's just import previous SYSTEMD_READY state and skip other rules
-SUBSYSTEM=="block", ENV{DM_SUSPENDED}=="1", IMPORT{db}="SYSTEMD_READY"
-SUBSYSTEM=="block", ENV{DM_SUSPENDED}=="1", GOTO="systemd_end"
+# When a dm device is first created, it's just an empty container. Ignore it.
+# DM_NAME is not set in this case, but it's set on spurious "add" events that occur later.
+SUBSYSTEM=="block", ACTION=="add", KERNEL=="dm-*", ENV{DM_NAME}!="?*", ENV{SYSTEMD_READY}="0"
 
-SUBSYSTEM=="block", ACTION=="add", ENV{DM_UDEV_DISABLE_OTHER_RULES_FLAG}=="1", ENV{SYSTEMD_READY}="0"
+# DM_UDEV_DISABLE_OTHER_RULES_FLAG==1 means that the device shouldn't be probed.
+# Import previous SYSTEMD_READY state.
+SUBSYSTEM=="block", ENV{DM_UDEV_DISABLE_OTHER_RULES_FLAG}=="1", ENV{SYSTEMD_READY}=="", IMPORT{db}="SYSTEMD_READY"
 
 # Ignore encrypted devices with no identified superblock on it, since
 # we are probably still calling mke2fs or mkswap on it.
 SUBSYSTEM=="block", ENV{DM_UUID}=="CRYPT-*", ENV{ID_PART_TABLE_TYPE}=="", ENV{ID_FS_USAGE}=="", ENV{SYSTEMD_READY}="0"
 
-# Explicitly set SYSTEMD_READY=1 for DM devices that don't have it set yet, so that we always have something to import above
-SUBSYSTEM=="block", ENV{DM_UUID}=="?*", ENV{SYSTEMD_READY}=="", ENV{SYSTEMD_READY}="1"
-
 # add symlink to GPT root disk
 SUBSYSTEM=="block", ENV{ID_PART_GPT_AUTO_ROOT}=="1", ENV{ID_FS_TYPE}!="crypto_LUKS", SYMLINK+="gpt-auto-root"
 SUBSYSTEM=="block", ENV{ID_PART_GPT_AUTO_ROOT}=="1", ENV{ID_FS_TYPE}=="crypto_LUKS", SYMLINK+="gpt-auto-root-luks"
index 83d910cda15a6058d36a9bb25f12dfcb597522cb..f021978f5f8c62400155bfeb0223f99889fb4045 100644 (file)
@@ -69,12 +69,12 @@ _bootctl_reboot-to-firmware() {
 }
 
 _arguments \
-    {-h,--help}'[Prints a short help text and exits.]' \
-    '--version[Prints a short version string and exits.]' \
+    '(- *)'{-h,--help}'[Prints a short help text and exits.]' \
+    '(- *)--version[Prints a short version string and exits.]' \
     '--esp-path=[Path to the EFI System Partition (ESP)]:path:_directories' \
     '--boot-path=[Path to the $BOOT partition]:path:_directories' \
-    {-p,--print-esp-path}'[Print path to the EFI system partition]' \
-    {-x,--print-boot-path}'[Print path to the $BOOT partition]' \
+    '(-p --print-esp-path)'{-p,--print-esp-path}'[Print path to the EFI system partition]' \
+    '(-x --print-boot-path)'{-x,--print-boot-path}'[Print path to the $BOOT partition]' \
     '--make-machine-id-directory=[Control creation and deletion of the top-level machine ID directory.]:options:(yes no auto)' \
     '--no-variables[Do not touch EFI variables]' \
     '--no-pager[Do not pipe output into a pager]' \
index 70f9e74036961be7ab64ef21daf3dd3ae1beb74c..d8d7adfb4cc5e6ad7d49b3c4ee99dc080038e97c 100644 (file)
@@ -259,14 +259,14 @@ local -a _modes; _modes=("--user" "--system")
 local _bus_address=${${words:*_modes}[(R)(${(j.|.)_modes})]}
 local curcontext=$curcontext state line
 _arguments \
-    {-h,--help}'[Prints a short help text and exits.]' \
-    '--version[Prints a short version string and exits.]' \
+    '(- *)'{-h,--help}'[Prints a short help text and exits.]' \
+    '(- *)--version[Prints a short version string and exits.]' \
     '--no-pager[Do not pipe output into a pager]' \
     '--no-legend[Do not show the headers and footers]' \
     '--system[Connect to system manager]' \
     '--user[Connect to user service manager]' \
-    {-H+,--host=}'[Operate on remote host]:userathost:_sd_hosts_or_user_at_host' \
-    {-M+,--machine=}'[Operate on local container]:machines:_sd_machines' \
+    '(-H --host)'{-H+,--host=}'[Operate on remote host]:userathost:_sd_hosts_or_user_at_host' \
+    '(-M --machine)'{-M+,--machine=}'[Operate on local container]:machines:_sd_machines' \
     '--address=[Connect to the bus specified by address]:address' \
     '--show-machine[Show machine ID column in list]' \
     '--unique[Only show unique names]' \
@@ -274,7 +274,7 @@ _arguments \
     '--activatable[Only show activatable names]' \
     '--match=[Only show matching messages]:match:__dbus_matchspec' \
     '--list[Do not show tree, but simple object path list]' \
-    {-q,--quiet}'[Do not show method call reply]'\
+    '(-q --quiet)'{-q,--quiet}'[Do not show method call reply]'\
     '--verbose[Show result values in long format]' \
     '--xml-interface[Dump the XML description in introspect command]' \
     '--json=[Show result values in long format]:format:_busctl_get_json' \
index dad21a74ad825b5f4eb0f10bc02e40d2b1b6669f..7472626069587931c0e00b87a39950d5603cade1 100644 (file)
@@ -30,18 +30,18 @@ _coredumpctl_commands(){
 }
 
 _arguments \
-    {-o+,--output=}'[Write output to FILE]:output file:_files' \
-    {-F+,--field=}'[Show field in list output]:field' \
+    '(-o --output)'{-o+,--output=}'[Write output to FILE]:output file:_files' \
+    '(-F --field)'{-F+,--field=}'[Show field in list output]:field' \
     '-1[Show information about most recent entry only]' \
-    {-S,--since}'[Print entries since the specified date]' \
-    {-U,--until}'[Print entries until the specified date]' \
-    {-r,--reverse}'[Show the newest entries first]' \
+    '(-S --since)'{-S,--since}'[Print entries since the specified date]' \
+    '(-U --until)'{-U,--until}'[Print entries until the specified date]' \
+    '(-r --reverse)'{-r,--reverse}'[Show the newest entries first]' \
     '--no-pager[Do not pipe output into a pager]' \
     '--no-legend[Do not print the column headers]' \
-    {-h,--help}'[Show this help]' \
-    '--version[Show package version]' \
+    '(- *)'{-h,--help}'[Show this help]' \
+    '(- *)--version[Show package version]' \
     '--debugger=[Use the given debugger]:debugger: _command_names -e' \
-    {-D,--directory=}'[Use the journal files in the specified dir]:directory: _directories' \
-    {-q,--quiet}'[Do not show info messages and privilege warning]' \
+    '(-D --directory)'{-D,--directory=}'[Use the journal files in the specified dir]:directory: _directories' \
+    '(-q --quiet)'{-q,--quiet}'[Do not show info messages and privilege warning]' \
     '--all[Look at all journal files instead of local ones]' \
     '*::coredumpctl commands:_coredumpctl_commands'
index bdd26b10f1ebb56db5847d5e5bd99160f0523b0f..76473937a0040e9ee74d810131880bf8f180829f 100644 (file)
@@ -83,13 +83,13 @@ _hostnamectl_commands() {
 }
 
 _arguments -s \
-    {-h,--help}'[Show this help]' \
-    '--version[Show package version]' \
+    '(- *)'{-h,--help}'[Show this help]' \
+    '(- *)--version[Show package version]' \
     '--transient[Only set transient hostname]' \
     '--static[Only set static hostname]' \
     '--pretty[Only set pretty hostname]' \
     '--no-ask-password[Do not prompt for password]' \
-    {-H+,--host=}'[Operate on remote host]:userathost:_sd_hosts_or_user_at_host' \
-    {-M+,--machine=}'[Operate on local container]:machines:_sd_machines' \
-    '--json[Shows output formatted as JSON]:format:_hostnamectl_get_json' \
+    '(-H --host)'{-H+,--host=}'[Operate on remote host]:userathost:_sd_hosts_or_user_at_host' \
+    '(-M --machine)'{-M+,--machine=}'[Operate on local container]:machines:_sd_machines' \
+    '--json=[Shows output formatted as JSON]:format:_hostnamectl_get_json' \
     '*::hostnamectl commands:_hostnamectl_commands'
index 7138615cdde2d49edf2444b0dc2cdd0d871b638f..1cfa049b0ff54aaeed8e338b60a713af02855e73 100644 (file)
@@ -96,37 +96,37 @@ while (( i++ < n )); do
     fi
 done
 _arguments -s \
-    {-h,--help}'[Show this help]' \
-    '--version[Show package version]' \
+    '(- *)'{-h,--help}'[Show this help]' \
+    '(- *)--version[Show package version]' \
     '--no-pager[Do not pipe output into a pager]' \
     --no-hostname"[Don't show the hostname of local log messages]" \
-    {-l,--full}'[Show long fields in full]' \
-    {-a,--all}'[Show all fields, including long and unprintable]' \
-    {-f,--follow}'[Follow journal]' \
-    {-e,--pager-end}'[Jump to the end of the journal in the pager]' \
-    {-n+,--lines=}'[Number of journal entries to show]:integer' \
+    '(-l --full)'{-l,--full}'[Show long fields in full]' \
+    '(-a --all)'{-a,--all}'[Show all fields, including long and unprintable]' \
+    '(-f --follow)'{-f,--follow}'[Follow journal]' \
+    '(-e --pager-end)'{-e,--pager-end}'[Jump to the end of the journal in the pager]' \
+    '(-n --lines)'{-n+,--lines=}'[Number of journal entries to show]:integer' \
     '--no-tail[Show all lines, even in follow mode]' \
-    {-r,--reverse}'[Reverse output]' \
-    {-o+,--output=}'[Change journal output mode]:output modes:_sd_outputmodes' \
-    {-x,--catalog}'[Show explanatory texts with each log line]' \
-    {-q,--quiet}"[Don't show privilege warning]" \
-    {-m,--merge}'[Show entries from all available journals]' \
-    {-b+,--boot=}'[Show data only from the specified boot or offset]::boot id or offset:_journalctl_boots' \
+    '(-r --reverse)'{-r,--reverse}'[Reverse output]' \
+    '(-o --output)'{-o+,--output=}'[Change journal output mode]:output modes:_sd_outputmodes' \
+    '(-x --catalog)'{-x,--catalog}'[Show explanatory texts with each log line]' \
+    '(-q --quiet)'{-q,--quiet}"[Don't show privilege warning]" \
+    '(-m --merge)'{-m,--merge}'[Show entries from all available journals]' \
+    '(-b --boot)'{-b+,--boot=}'[Show data only from the specified boot or offset]::boot id or offset:_journalctl_boots' \
     '--list-boots[List boots ordered by time]' \
-    {-k,--dmesg}'[Show only kernel messages from the current boot]' \
-    {-u+,--unit=}'[Show data only from the specified unit]:units:_journalctl_field_values _SYSTEMD_UNIT' \
+    '(-k --dmesg)'{-k,--dmesg}'[Show only kernel messages from the current boot]' \
+    '(-u --unit)'{-u+,--unit=}'[Show data only from the specified unit]:units:_journalctl_field_values _SYSTEMD_UNIT' \
     '--user-unit=[Show data only from the specified user session unit]:units:_journalctl_field_values USER_UNIT' \
-    {-p+,--priority=}'[Show only messages within the specified priority range]:priority:_journalctl_field_values PRIORITY' \
+    '(-p --priority)'{-p+,--priority=}'[Show only messages within the specified priority range]:priority:_journalctl_field_values PRIORITY' \
     '--facility=[Filter messages by facility]:facility:_journalctl_facilities' \
-    {-t+,--identifier=}'[Filter messages by syslog identifier]:identifier:_journalctl_field_values SYSLOG_IDENTIFIER' \
-    {-c+,--cursor=}'[Start showing entries from the specified cursor]:cursors:_journalctl_field_values __CURSORS' \
+    '(-t --identifier)'{-t+,--identifier=}'[Filter messages by syslog identifier]:identifier:_journalctl_field_values SYSLOG_IDENTIFIER' \
+    '(-c --cursor)'{-c+,--cursor=}'[Start showing entries from the specified cursor]:cursors:_journalctl_field_values __CURSORS' \
     '--cursor-file=[Show entries using cursor stored in file]:file:_files' \
     '--after-cursor=[Start showing entries from after the specified cursor]:cursors:_journalctl_field_values __CURSORS' \
     '--since=[Start showing entries on or newer than the specified date]:YYYY-MM-DD HH\:MM\:SS' \
     '--until=[Stop showing entries on or older than the specified date]:YYYY-MM-DD HH\:MM\:SS' \
-    {-g+,--grep=}'[Show entries with MESSAGE field matching PCRE pattern]' \
+    '(-g --grep)'{-g+,--grep=}'[Show entries with MESSAGE field matching PCRE pattern]' \
     '--case-sensitive=[Force case sensitive or insensitive matching]:boolean:(true false)' \
-    {-F,--field=}'[List all values a certain field takes]:Fields:_journalctl_fields' \
+    '(-F --field)'{-F,--field=}'[List all values a certain field takes]:Fields:_journalctl_fields' \
     '--system[Show system and kernel messages]' \
     '--user[Show messages from user services]' \
     '(--directory -D -M --machine --root --file)'{-M+,--machine=}'[Operate on local container]:machines:_sd_machines' \
index cb2565db7a3865698c250e777c96fc988c84586d..0e786e36f095d05dcd511cefe60e420dd6b09dae 100644 (file)
@@ -22,6 +22,8 @@ _kernel-install_kernels(){
 }
 
 _arguments \
+    '(- :)'{-h,--help}'[Show help]' \
+    '(- :)--version[Show package version]' \
     '1::add or remove:(add remove)' \
     '2::kernel versions:_kernel-install_kernels' \
     '3::kernel images:_kernel-install_images'
index 65babe2c083bdc7227d9da08b40a3e8309b6eb3f..8c098413394b1da5434f9ecd9f47740cf058a884 100644 (file)
@@ -88,11 +88,11 @@ _localectl_commands() {
 }
 
 _arguments \
-    {-h,--help}'[Show this help]' \
+    '(- *)'{-h,--help}'[Show this help]' \
     '--version[Show package version]' \
     "--no-convert[Don't convert keyboard mappings]" \
     '--no-pager[Do not pipe output into a pager]' \
     '--no-ask-password[Do not prompt for password]' \
-    {-H+,--host=}'[Operate on remote host]:userathost:_sd_hosts_or_user_at_host' \
-    {-M+,--machine=}'[Operate on local container]:machine' \
+    '(-H --host)'{-H+,--host=}'[Operate on remote host]:userathost:_sd_hosts_or_user_at_host' \
+    '(-M --machine)'{-M+,--machine=}'[Operate on local container]:machine' \
     '*::localectl commands:_localectl_commands'
index 5473020e3d6d524e83ab777e22d5238a1c39c628..5e86ff6d0123df3a920a877379d11eb0c5cdd7e5 100644 (file)
@@ -165,18 +165,18 @@ _loginctl_commands() {
 
 
 _arguments -s \
-    {-h,--help}'[Show help]' \
-    '--version[Show package version]' \
-    \*{-p+,--property=}'[Show only properties by this name]:unit property' \
-    {-a,--all}'[Show all properties, including empty ones]' \
+    '(- *)'{-h,--help}'[Show help]' \
+    '(- *)--version[Show package version]' \
+    '*'{-p+,--property=}'[Show only properties by this name]:unit property' \
+    '(-a --all)'{-a,--all}'[Show all properties, including empty ones]' \
     '--kill-whom=[Whom to send signal to]:killwhom:(main control all)' \
-    {-s+,--signal=}'[Which signal to send]:signal:_signals' \
-    {-H+,--host=}'[Operate on remote host]:userathost:_sd_hosts_or_user_at_host' \
-    {-M+,--machine=}'[Operate on local container]:machine:_sd_machines' \
-    {-l,--full}'[Do not ellipsize output]' \
+    '(-s --signal)'{-s+,--signal=}'[Which signal to send]:signal:_signals' \
+    '(-H --host)'{-H+,--host=}'[Operate on remote host]:userathost:_sd_hosts_or_user_at_host' \
+    '(-M --machine)'{-M+,--machine=}'[Operate on local container]:machine:_sd_machines' \
+    '(-l --full)'{-l,--full}'[Do not ellipsize output]' \
     '--no-pager[Do not pipe output into a pager]' \
     '--no-legend[Do not show the headers and footers]' \
     '--no-ask-password[Do not ask for system passwords]' \
-    {-n+,--lines=}'[Number of journal entries to show]' \
-    {-o+,--output=}'[Change journal output mode]:output modes:_sd_outputmodes' \
+    '(-n --lines)'{-n+,--lines=}'[Number of journal entries to show]' \
+    '(-o --output)'{-o+,--output=}'[Change journal output mode]:output modes:_sd_outputmodes' \
     '*::loginctl command:_loginctl_commands'
index cb43721961e02783096208bd5e627d60850b6be4..99cc103755eb00bd2d42c80bb0871209fbb57012 100644 (file)
 }
 
 _arguments \
-    {-h,--help}'[Prints a short help text and exits.]' \
-    '--version[Prints a short version string and exits.]' \
+    '(- *)'{-h,--help}'[Prints a short help text and exits.]' \
+    '(- *)--version[Prints a short version string and exits.]' \
     '--no-pager[Do not pipe output into a pager.]' \
     '--no-legend[Do not show the headers and footers.]' \
     '--no-ask-password[Do not ask for system passwords.]' \
-    {-H+,--host=}'[Operate on remote host.]:userathost:_sd_hosts_or_user_at_host' \
-    {-M+,--machine=}'[Operate on local container.]:machine:_sd_machines' \
-    {-p+,--property=}'[Limit output to specified property.]:property:(Name Id Timestamp TimestampMonotonic Service Scope Leader Class State RootDirectory)' \
-    {-a,--all}'[Show all properties.]' \
-    {-q,--quiet}'[Suppress output.]' \
-    {-l,--full}'[Do not ellipsize cgroup members.]' \
+    '(-H --host)'{-H+,--host=}'[Operate on remote host.]:userathost:_sd_hosts_or_user_at_host' \
+    '(-M --machine)'{-M+,--machine=}'[Operate on local container.]:machine:_sd_machines' \
+    '(-p --property)'{-p+,--property=}'[Limit output to specified property.]:property:(Name Id Timestamp TimestampMonotonic Service Scope Leader Class State RootDirectory)' \
+    '(-a --all)'{-a,--all}'[Show all properties.]' \
+    '(-q --quiet)'{-q,--quiet}'[Suppress output.]' \
+    '(-l --full)'{-l,--full}'[Do not ellipsize cgroup members.]' \
     '--kill-whom=[Whom to send signal to.]:killwhom:(leader all)' \
-    {-s+,--signal=}'[Which signal to send.]:signal:_signals' \
+    '(-s --signal)'{-s+,--signal=}'[Which signal to send.]:signal:_signals' \
     '--read-only[Create read-only bind mount.]' \
     '--mkdir[Create directory before bind mounting, if missing.]' \
-    {-n+,--lines=}'[Number of journal entries to show.]:integer' \
-    {-o+,--output=}'[Change journal output mode.]:output modes:_sd_outputmodes' \
+    '(-n --lines)'{-n+,--lines=}'[Number of journal entries to show.]:integer' \
+    '(-o --output)'{-o+,--output=}'[Change journal output mode.]:output modes:_sd_outputmodes' \
     '--verify=[Verification mode for downloaded images.]:verify:(no checksum signature)' \
     '--force[Download image even if already exists.]' \
     '*::machinectl command:_machinectl_commands'
index c7d1e3a3657924660ea8777005710218b92c191f..6969797e12448028d81036328ba80fdd54ee507c 100644 (file)
 }
 
 _arguments \
-    {-a,--all}'[Show all links with status]' \
+    '(-a --all)'{-a,--all}'[Show all links with status]' \
     '--no-pager[Do not pipe output into a pager]' \
     '--no-legend[Do not print the column headers]' \
-    {-h,--help}'[Show this help]' \
-    '--version[Show package version]' \
-    '--drop-in=[Use the given drop-in file name]' \
+    '(- *)'{-h,--help}'[Show this help]' \
+    '(- *)--version[Show package version]' \
+    '--drop-in=[Use the given drop-in file name]:NAME' \
     '--no-reload[Do not reload the network manager state when editing]' \
-    '--json[Shows output formatted as JSON]:format:_networkctl_get_json' \
+    '--json=[Shows output formatted as JSON]:format:_networkctl_get_json' \
     '*::networkctl commands:_networkctl_commands'
index 18e1938ab99e51f0ba3f50cc50022a3ab71181a7..ac7ec64c09eb1c5b27bdbec1ca6cacc8ed94a7e6 100644 (file)
@@ -22,7 +22,7 @@
 }
 
 _arguments \
-    {-h,--help}'[Prints a short help text and exits.]' \
-    '--version[Prints a short version string and exits.]' \
+    '(- *)'{-h,--help}'[Prints a short help text and exits.]' \
+    '(- *)--version[Prints a short version string and exits.]' \
     '--no-pager[Do not pipe output into a pager]' \
     '*::oomctl command:_oomctl_commands'
index 25a518edef75fa32b0e27911c8f708b055fdb0eb..94ff3950af963b5f9d0277e35174e903f3b4c57e 100644 (file)
 }
 
 _arguments \
-    {-h,--help}'[Print a short help text and exit]' \
-    '--version[Print a short version string and exit]' \
-    '--legend=no[Do not show headers and footers]' \
+    '(- *)'{-h,--help}'[Print a short help text and exit]' \
+    '(- *)--version[Print a short version string and exit]' \
+    '--legend=[Do not show headers and footers]:BOOL:(yes no)' \
     '-4[Resolve IPv4 addresses]' \
     '-6[Resolve IPv6 addresses]' \
-    {-i+,--interface=}'[Look on interface]:interface:_net_interfaces' \
-    {-p+,--protocol=}'[Look via protocol]:protocol:_resolvectl_protocols' \
-    {-t+,--type=}'[Query RR with DNS type]:type:_resolvectl_types' \
-    {-c+,--class=}'[Query RR with DNS class]:class:_resolvectl_classes' \
+    '(-i --interface)'{-i+,--interface=}'[Look on interface]:interface:_net_interfaces' \
+    '(-p --protocol)'{-p+,--protocol=}'[Look via protocol]:protocol:_resolvectl_protocols' \
+    '(-t --type)'{-t+,--type=}'[Query RR with DNS type]:type:_resolvectl_types' \
+    '(-c --class)'{-c+,--class=}'[Query RR with DNS class]:class:_resolvectl_classes' \
     '--service[Resolve services]' \
-    '--service-address=no[Do not resolve address for services]' \
-    '--service-txt=no[Do not resolve TXT records for services]' \
-    '--cname=no[Do not follow CNAME redirects]' \
-    '--search=no[Do not use search domains]' \
+    '--service-address=[Do not resolve address for services]:BOOL:(yes no)' \
+    '--service-txt=[Do not resolve TXT records for services]:BOOL:(yes no)' \
+    '--cname=[Do not follow CNAME redirects]:BOOL:(yes no)' \
+    '--search=[Do not use search domains]:BOOL:(yes no)' \
     '*::default: _resolvectl_commands'
index 54e34a1781e169f58fe2d6c48aead7c794edd1d3..df9045f229bca71f9120a89df5ed7a2cfe6a7910 100644 (file)
@@ -502,20 +502,20 @@ done
 # Use the last mode, or --system (they are exclusive and the last one is used).
 local _sys_service_mgr=${words[(R)(--user|--system)]:---system}
 _arguments -s \
-    {-h,--help}'[Show help]' \
-    '--version[Show package version]' \
-    {-t+,--type=}'[List only units of a particular type]:unit type:_systemctl_unit_types' \
+    '(- *)'{-h,--help}'[Show help]' \
+    '(- *)--version[Show package version]' \
+    '(-t --type)'{-t+,--type=}'[List only units of a particular type]:unit type:_systemctl_unit_types' \
     '--state=[Display units in the specified state]:unit state:_systemctl_unit_states' \
     '--job-mode=[Specify how to deal with other jobs]:mode:_systemctl_job_modes' \
-    {-p+,--property=}'[Show only properties by specific name]:unit property:_systemctl_unit_properties' \
-    {-a,--all}'[Show all units/properties, including dead/empty ones]' \
+    '(-p --property)'{-p+,--property=}'[Show only properties by specific name]:unit property:_systemctl_unit_properties' \
+    '(-a --all)'{-a,--all}'[Show all units/properties, including dead/empty ones]' \
     '--reverse[Show reverse dependencies]' \
     '--after[Show units ordered after]' \
     '--before[Show units ordered before]' \
-    {-l,--full}"[Don't ellipsize unit names on output]" \
+    '(-l --full)'{-l,--full}"[Don't ellipsize unit names on output]" \
     '--show-types[When showing sockets, show socket type]' \
     '--check-inhibitors[Specify if inhibitors should be checked]:mode:_systemctl_check_inhibitors' \
-    {-q,--quiet}'[Suppress output]' \
+    '(-q --quiet)'{-q,--quiet}'[Suppress output]' \
     '--no-warn[Suppress several warnings shown by default]' \
     '--no-block[Do not wait until operation finished]' \
     '--legend=no[Do not print a legend, i.e. the column headers and the footer with hints]' \
@@ -527,14 +527,14 @@ _arguments -s \
     "--no-reload[When enabling/disabling unit files, don't reload daemon configuration]" \
     '--no-ask-password[Do not ask for system passwords]' \
     '--kill-whom=[Whom to send signal to]:killwhom:(main control all)' \
-    {-s+,--signal=}'[Which signal to send]:signal:_signals' \
-    {-f,--force}'[When enabling unit files, override existing symlinks. When shutting down, execute action immediately]' \
+    '(-s --signal)'{-s+,--signal=}'[Which signal to send]:signal:_signals' \
+    '(-f --force)'{-f,--force}'[When enabling unit files, override existing symlinks. When shutting down, execute action immediately]' \
     '--root=[Enable/disable/mask unit files in the specified root directory]:directory:_directories' \
     '--runtime[Enable/disable/mask unit files only temporarily until next reboot]' \
-    {-H+,--host=}'[Operate on remote host]:userathost:_sd_hosts_or_user_at_host' \
-    {-P,--privileged}'[Acquire privileges before execution]' \
-    {-n+,--lines=}'[Journal entries to show]:number of entries' \
-    {-o+,--output=}'[Change journal output mode]:modes:_sd_outputmodes' \
+    '(-H --host)'{-H+,--host=}'[Operate on remote host]:userathost:_sd_hosts_or_user_at_host' \
+    '(-P --privileged)'{-P,--privileged}'[Acquire privileges before execution]' \
+    '(-n --lines)'{-n+,--lines=}'[Journal entries to show]:number of entries' \
+    '(-o --output)'{-o+,--output=}'[Change journal output mode]:modes:_sd_outputmodes' \
     '--firmware-setup[Tell the firmware to show the setup menu on next boot]' \
     '--plain[When used with list-dependencies, print output as a list]' \
     '--failed[Show failed units]' \
index 44476144c118493c677b96d7f98307324b5332ff..5547b0cbcb4ec80f04231d10e3b645e09018bfe3 100644 (file)
@@ -5,7 +5,7 @@ local curcontext="$curcontext" state lstate line
 case "$service" in
     systemd-ask-password)
         _arguments \
-            {-h,--help}'[Show this help]' \
+            '(- *)'{-h,--help}'[Show this help]' \
             '--icon=[Icon name]:icon name:' \
             '--timeout=[Timeout in sec]:timeout (seconds):' \
             '--no-tty[Ask question via agent even on TTY]' \
@@ -14,54 +14,54 @@ case "$service" in
         ;;
     systemd-cat)
         _arguments \
-            {-h,--help}'[Show this help]' \
-            '--version[Show package version.]' \
-            {-t+,--identifier=}'[Set syslog identifier.]:syslog identifier:' \
-            {-p+,--priority=}'[Set priority value.]:value:({0..7})' \
+            '(- *)'{-h,--help}'[Show this help]' \
+            '(- *)--version[Show package version.]' \
+            '(-t --identifier)'{-t+,--identifier=}'[Set syslog identifier.]:syslog identifier:' \
+            '(-p --priority)'{-p+,--priority=}'[Set priority value.]:value:({0..7})' \
             '--level-prefix=[Control whether level prefix shall be parsed.]:boolean:(1 0)' \
             '--namespace=[Connect to specified journal namespace.]:journal namespace:' \
             ':Message'
         ;;
     systemd-cgls)
         _arguments \
-            {-h,--help}'[Show this help]' \
-            '--version[Show package version]' \
+            '(- *)'{-h,--help}'[Show this help]' \
+            '(- *)--version[Show package version]' \
             '--no-pager[Do not pipe output into a pager]' \
-            {-a,--all}'[Show all groups, including empty]' \
+            '(-a --all)'{-a,--all}'[Show all groups, including empty]' \
             '-k[Include kernel threads in output]' \
             ':cgroups:(cpuset cpu cpuacct memory devices freezer blkio)'
         ;;
     systemd-cgtop)
         _arguments \
-            {-h,--help}'[Show this help]' \
-            '--version[Print version and exit]' \
+            '(-)'{-h,--help}'[Show this help]' \
+           '(-)--version[Print version and exit]' \
             '(-c -m -i -t)-p[Order by path]' \
             '(-c -p -m -i)-t[Order by number of tasks]' \
             '(-m -p -i -t)-c[Order by CPU load]' \
             '(-c -p -i -t)-m[Order by memory load]' \
             '(-c -m -p -t)-i[Order by IO load]' \
-            {-d+,--delay=}'[Specify delay]:delay:' \
-            {-n+,--iterations=}'[Run for N iterations before exiting]:number of iterations:' \
-            {-b,--batch}'[Run in batch mode, accepting no input]' \
+            '(-d --delay)'{-d+,--delay=}'[Specify delay]:delay:' \
+            '(-n --iterations)'{-n+,--iterations=}'[Run for N iterations before exiting]:number of iterations:' \
+            '(-b --batch)'{-b,--batch}'[Run in batch mode, accepting no input]' \
             '--depth=[Maximum traversal depth]:maximum depth:'
         ;;
     systemd-detect-virt)
         _arguments \
-            {-h,--help}'[Show this help]' \
-            '--version[Show package version]' \
-            {-c,--container}'[Only detect whether we are run in a container]' \
-            {-v,--vm}'[Only detect whether we are run in a VM]' \
-            {-q,--quiet}"[Don't output anything, just set return value]"
+            '(-)'{-h,--help}'[Show this help]' \
+           '(-)--version[Show package version]' \
+            '(-c --container)'{-c,--container}'[Only detect whether we are run in a container]' \
+            '(-v --vm)'{-v,--vm}'[Only detect whether we are run in a VM]' \
+            '(-q --quiet)'{-q,--quiet}"[Don't output anything, just set return value]"
         ;;
     systemd-machine-id-setup)
         _arguments \
-            {-h,--help}'[Show this help]' \
-            '--version[Show package version]'
+            '(-)'{-h,--help}'[Show this help]' \
+           '(-)--version[Show package version]'
         ;;
     systemd-notify)
         _arguments \
-            {-h,--help}'[Show this help]' \
-            '--version[Show package version]' \
+            '(-)'{-h,--help}'[Show this help]' \
+           '(-)--version[Show package version]' \
             '--ready[Inform the init system about service start-up completion.]' \
             '--pid=[Inform the init system about the main PID of the daemon]:daemon main PID:_pids' \
             '--status=[Send a free-form status string for the daemon to the init systemd]:status string:' \
@@ -69,9 +69,9 @@ case "$service" in
         ;;
     systemd-tty-ask-password-agent)
         _arguments \
-            {-h,--help}'[Prints a short help text and exits.]' \
-            '--version[Prints a short version string and exits.]' \
-            '--list[Lists all currently pending system password requests.]' \
+            '(-)'{-h,--help}'[Prints a short help text and exits.]' \
+           '(-)--version[Prints a short version string and exits.]' \
+           '--list[Lists all currently pending system password requests.]' \
             '--query[Process all currently pending system password requests by querying the user on the calling TTY.]' \
             '--watch[Continuously process password requests.]' \
             '--wall[Forward password requests to wall(1).]' \
index 2e046ea1119d71df72bcbb3c86f027381302f4e1..20b89646b17245bef03ba0cb0d3239a569d567bd 100644 (file)
@@ -94,8 +94,8 @@ JSON or table format'
     }
 
 _arguments \
-    {-h,--help}'[Show help text]' \
-    '--version[Show package version]' \
+    '(- *)'{-h,--help}'[Show help text]' \
+    '(- *)--version[Show package version]' \
     '--system[Operate on system systemd instance]' \
     '--user[Operate on user systemd instance]' \
     '--global[Show global user instance config]' \
@@ -117,7 +117,7 @@ _arguments \
     '--fuzz=[When printing the tree of the critical chain, print also services, which finished TIMESPAN earlier, than the latest in the branch]:TIMESPAN' \
     '--from-pattern=[When generating a dependency graph, filter only origins]:GLOB' \
     '--to-pattern=[When generating a dependency graph, filter only destinations]:GLOB' \
-    {-H+,--host=}'[Operate on remote host]:userathost:_sd_hosts_or_user_at_host' \
-    {-M+,--machine=}'[Operate on local container]:machine:_sd_machines' \
+    '(-H --host)'{-H+,--host=}'[Operate on remote host]:userathost:_sd_hosts_or_user_at_host' \
+    '(-M --machine)'{-M+,--machine=}'[Operate on local container]:machine:_sd_machines' \
     '--quiet[Do not show hints]' \
     '*::systemd-analyze commands:_systemd-analyze_commands'
index 7d7456b879d9596f493e2f334adbe67e2f9bcc71..efcec8b4d491711a2bfec60e7620e5649db223a5 100644 (file)
@@ -9,8 +9,8 @@ _systemd-delta_types() {
 }
 
 _arguments \
-    {-h,--help}'[Show this help]' \
-    '--version[Show package version]' \
+    '(- :)'{-h,--help}'[Show this help]' \
+    '(- :)--version[Show package version]' \
     '--no-pager[Do not pipe output into a pager]' \
     '--diff=[Show a diff when overridden files differ]:boolean:(1 0)' \
     {-t+,--type=}'[Only display a selected set of override types]:types:_systemd-delta_types' \
index 059ef9e6be0a31606ca5ca86dbc04fc683877024..08bcf4208d71d65c82aeeea599fafd02c5192f39 100644 (file)
@@ -26,8 +26,10 @@ _systemd-inhibit_what() {
 }
 
 _arguments \
-    {-h,--help}'[Show this help]' \
-    '--version[Show package version]' \
+    '(- *)'{-h,--help}'[Show this help]' \
+    '(- *)--version[Show package version]' \
+    '--no-pager[Do not pipe output into a pager]' \
+    '--no-legend[Do not show the headers and footers]' \
     '--what=[Operations to inhibit]:options:_systemd-inhibit_what' \
     '--who=[A descriptive string who is inhibiting]:who is inhibiting:' \
     '--why=[A descriptive string why is being inhibited]:reason for the lock:' \
index adc5d7c8a0473e9b8e8ce14278240d2f87bdf5a2..a9856b500831cab4ae9b7b9199756f7fb27986ca 100644 (file)
@@ -13,27 +13,27 @@ _systemd-nspawn_caps(){
 }
 
 _arguments \
-    {-h,--help}'[Show this help.]' \
-    '--version[Print a short version string and exit.]' \
-    {--quiet,-q}'[Turns off any status output by the tool itself.]' \
-    {--directory=,-D+}'[Directory to use as file system root for the namespace container. If omitted the current directory will be used.]:directories:_directories' \
+    '(- *)'{-h,--help}'[Show this help.]' \
+    '(- *)--version[Print a short version string and exit.]' \
+    '(--quiet -q)'{--quiet,-q}'[Turns off any status output by the tool itself.]' \
+    '(--directory -D)'{--directory=,-D+}'[Directory to use as file system root for the namespace container. If omitted the current directory will be used.]:directories:_directories' \
     '--template=[Initialize root directory from template directory, if missing.]:template:_directories' \
-    {--ephemeral,-x}'[Run container with snapshot of root directory, and remove it after exit.]' \
-    {--image=,-i+}'[Disk image to mount the root directory for the container from.]:disk image: _files' \
-    {--boot,-b}'[Automatically search for an init binary and invoke it instead of a shell or a user supplied program.]' \
-    {--user=,-u+}'[Run the command under specified user, create home directory and cd into it.]:user:_users' \
-    {--machine=,-M+}'[Sets the machine name for this container.]: : _message "container name"' \
+    '(--ephemeral -x)'{--ephemeral,-x}'[Run container with snapshot of root directory, and remove it after exit.]' \
+    '(--image -i)'{--image=,-i+}'[Disk image to mount the root directory for the container from.]:disk image: _files' \
+    '(--boot -b)'{--boot,-b}'[Automatically search for an init binary and invoke it instead of a shell or a user supplied program.]' \
+    '(--user -u)'{--user=,-u+}'[Run the command under specified user, create home directory and cd into it.]:user:_users' \
+    '(--machine -M)'{--machine=,-M+}'[Sets the machine name for this container.]: : _message "container name"' \
     '--uuid=[Set the specified uuid for the container.]: : _message "container UUID"' \
-    {--slice=,-S+}'[Make the container part of the specified slice, instead of the default machine.slice.]: : _message slice' \
+    '(--slice -S)'{--slice=,-S+}'[Make the container part of the specified slice, instead of the default machine.slice.]: : _message slice' \
     '--private-network[Disconnect networking of the container from the host.]' \
     '--network-interface=[Assign the specified network interface to the container.]: : _net_interfaces' \
     '--network-macvlan=[Create a "macvlan" interface of the specified Ethernet network interface and add it to the container.]: : _net_interfaces' \
     '--network-ipvlan=[Create an "ipvlan" network interface based on an existing network interface to the container.]: : _net_interfaces' \
-    {--network-veth,-n}'[Create a virtual Ethernet link (veth) between host and container.]' \
+    '(--network-veth -n)'{--network-veth,-n}'[Create a virtual Ethernet link (veth) between host and container.]' \
     '--network-bridge=[Adds the host side of the Ethernet link created with --network-veth to the specified bridge.]: : _net_interfaces' \
-    {--port=,-p+}'[Expose a container IP port on the host.]: : _message port' \
-    {--selinux-context=,-Z+}'[Sets the SELinux security context to be used to label processes in the container.]: : _message "SELinux context"' \
-    {--selinux-apifs-context=,-L+}'[Sets the SELinux security context to be used to label files in the virtual API file systems in the container.]: : _message "SELinux context"' \
+    '(--port -p)'{--port=,-p+}'[Expose a container IP port on the host.]: : _message port' \
+    '(--selinux-context -Z)'{--selinux-context=,-Z+}'[Sets the SELinux security context to be used to label processes in the container.]: : _message "SELinux context"' \
+    '(--selinux-apifs-context -L)'{--selinux-apifs-context=,-L+}'[Sets the SELinux security context to be used to label files in the virtual API file systems in the container.]: : _message "SELinux context"' \
     '--capability=[List one or more additional capabilities to grant the container.]:capabilities:_systemd-nspawn_caps' \
     '--drop-capability=[Specify one or more additional capabilities to drop for the containerm]:capabilities:_systemd-nspawn_caps' \
     "--link-journal=[Control whether the container's journal shall be made visible to the host system.]:options:(no host guest auto)" \
index d1fb24b2fd8b300364c908b6b6a2f4e8f0351b2f..58e46a2b67c4774a3ae0e5f32aac73072d8d092a 100644 (file)
@@ -3,7 +3,7 @@
 
 typeset -A sdpath=( ${$(systemd-path)/:/} )
 _arguments -S \
-       '(-h --help)'{-h,--help}'[Print help text and exit]' \
-       '(-v --version)'{-v,--version}'[Print a version string and exit]' \
-       '--suffix=[Append a suffix to the paths]' \
+       '(- *)'{-h,--help}'[Print help text and exit]' \
+       '(- *)'{-v,--version}'[Print a version string and exit]' \
+       '--suffix=[Append a suffix to the paths]:SUFFIX' \
        '*:pathname:compadd -k sdpath'
index 7568ed4840bff9092a34a9aea11b27f8c742065d..d3ec2e28efe5c3ddfefb95a30bb3434512a1c476 100644 (file)
@@ -23,12 +23,12 @@ __systemctl() {
     }
 
 _arguments \
-    {-G,--collect}'[Unload the transient unit after it completed]' \
+    '(-G --collect)'{-G,--collect}'[Unload the transient unit after it completed]' \
     '--description=[Description for unit]:description' \
     '--gid=[Run as system group]:group:_groups' \
-    {-h,--help}'[Show help message]' \
-    {-H+,--host=}'[Operate on remote host]:[user@]host:_sd_hosts_or_user_at_host' \
-    {-M+,--machine=}'[Operate on local container]:machines:_sd_machines' \
+    '(- *)'{-h,--help}'[Show help message]' \
+    '(-H --host)'{-H+,--host=}'[Operate on remote host]:[user@]host:_sd_hosts_or_user_at_host' \
+    '(-M --machine)'{-M+,--machine=}'[Operate on local container]:machines:_sd_machines' \
     '--nice=[Nice level]:nice level' \
     '--no-ask-password[Do not query the user for authentication]' \
     '--no-block[Do not synchronously wait for the unit start operation to finish]' \
@@ -41,8 +41,8 @@ _arguments \
     '--on-unit-active=[Run SEC seconds after the last activation]:SEC' \
     '--on-unit-inactive=[Run SEC seconds after the last deactivation]:SEC' \
     '--path-property=[Set path unit property]:NAME=VALUE' \
-    {-P,--pipe}'[Inherit standard input, output, and error]' \
-    {-p+,--property=}'[Set unit property]:NAME=VALUE:(( \
+    '(-P --pipe)'{-P,--pipe}'[Inherit standard input, output, and error]' \
+    '(-p --property)'{-p+,--property=}'[Set unit property]:NAME=VALUE:(( \
                 CPUAccounting= MemoryAccounting= BlockIOAccounting= SendSIGHUP= \
                 SendSIGKILL= MemoryLimit= CPUShares= BlockIOWeight= User= Group= \
                 DevicePolicy= KillMode= ExitType= DeviceAllow= BlockIOReadBandwidth= \
@@ -57,22 +57,22 @@ _arguments \
                 ReadOnlyPaths= InaccessiblePaths= EnvironmentFile= \
                 ProtectSystem= ProtectHome= RuntimeDirectory= PassEnvironment= \
                 ))' \
-    {-t,--pty}'[The service connects to the terminal]' \
-    {-q,--quiet}'[Suppresses additional informational output]' \
-    {-r,--remain-after-exit}'[Leave service around until explicitly stopped]' \
-    {-d,--same-dir}'[Run on the current working directory]' \
+    '(-t --pty)'{-t,--pty}'[The service connects to the terminal]' \
+    '(-q --quiet)'{-q,--quiet}'[Suppresses additional informational output]' \
+    '(-r --remain-after-exit)'{-r,--remain-after-exit}'[Leave service around until explicitly stopped]' \
+    '(-d --same-dir)'{-d,--same-dir}'[Run on the current working directory]' \
     '--scope[Run this as scope rather than service]' \
     '--send-sighup[Send SIGHUP when terminating]' \
     '--service-type=[Service type]:type:(simple forking oneshot dbus notify idle)' \
-    {-E+,--setenv=}'[Set environment]:NAME=VALUE' \
-    {-S,--shell}'[requests an interactive shell in the current working directory]' \
+    '(-E --setenv)'{-E+,--setenv=}'[Set environment]:NAME=VALUE' \
+    '(-S --shell)'{-S,--shell}'[requests an interactive shell in the current working directory]' \
     '--slice=[Run in the specified slice]:slices:__systemd-run_slices' \
     '--slice-inherit[Run in the inherited slice]' \
     '--socket-property=[Set socket unit property]:NAME=VALUE' \
     '--system[Run as system unit]' \
     '--timer-property=[Set timer unit property]:NAME=VALUE' \
     '--uid=[Run as system user]:user:_users' \
-    {-u+,--unit=}'[Run under the specified unit name]:unit name' \
+    '(-u --unit)'{-u+,--unit=}'[Run under the specified unit name]:unit name' \
     '--user[Run as user unit]' \
     '--version[Show package version]' \
     '--wait=[Wait until service stopped again]' \
index 6c9094afda3b9a737b41de51858fd010b149a81d..316014882da3da7c608f163dba3282905fed3ac5 100644 (file)
@@ -2,13 +2,22 @@
 # SPDX-License-Identifier: LGPL-2.1-or-later
 
 _arguments \
-    {-h,--help}'[Show help]' \
-    '--version[Show package version]' \
+    '(- *)'{-h,--help}'[Show help]' \
+    '--user[Execute user configuration]' \
+    '(- *)--version[Show package version]' \
+    '--cat-config[Show configuration files]' \
+    '--tldr[Show non-comment parts of configuration]' \
     '--create[Create, set ownership/permissions based on the config files.]' \
     '--clean[Clean up all files and directories with an age parameter configured.]' \
     '--remove[All files and directories marked with r, R in the configuration files are removed.]' \
     '--boot[Execute actions only safe at boot]' \
-    '--prefix=[Only apply rules that apply to paths with the specified prefix.]' \
-    '--exclude-prefix=[Ignore rules that apply to paths with the specified prefix.]' \
+    '--graceful[Quietly ignore unknown users or groups]' \
+    '--prefix=[Only apply rules that apply to paths with the specified prefix.]:PATH' \
+    '--exclude-prefix=[Ignore rules that apply to paths with the specified prefix.]:PATH' \
+    '-E[Ignore rules prefixed with /dev, /proc, /run, /sys]' \
     '--root=[Operate on an alternate filesystem root]:directory:_directories' \
+    '--image=[Operate on disk image as filesystem root]:image' \
+    '--image-policy=[Specify disk image dissection policy]:policy' \
+    '--replace=[Treat arguments as replacement for PATH]:PATH' \
+    '--no-pager[Do not pipe output into a pager]' \
     '*::files:_files'
index 2467b80bcfd2c7d4ff12503daa85e08d6fafd084..938fa876ae2dc4db42c7221843d82afaa292df70 100644 (file)
@@ -59,11 +59,11 @@ _timedatectl_command(){
 }
 
 _arguments -s \
-    {-h,--help}'[Show this help]' \
-    '--version[Show package version]' \
+    '(- *)'{-h,--help}'[Show this help]' \
+    '(- *)--version[Show package version]' \
     '--adjust-system-clock[Adjust system clock when changing local RTC mode]' \
     '--no-pager[Do not pipe output into a pager]' \
     '--no-ask-password[Do not prompt for password]' \
-    {-H+,--host=}'[Operate on remote host]:userathost:_sd_hosts_or_user_at_host' \
-    {-M+,--machine=}'[Operate on local container]:machines:_sd_machines' \
+    '(-H --host)'{-H+,--host=}'[Operate on remote host]:userathost:_sd_hosts_or_user_at_host' \
+    '(-M --machine)'{-M+,--machine=}'[Operate on local container]:machines:_sd_machines' \
     '*::timedatectl commands:_timedatectl_command'
index 6d313986bb961977901f22323275b4088a086445..9ff87d83123bb5729543c9fe8d68bbaae45cfff2 100644 (file)
@@ -4,6 +4,8 @@
 (( $+functions[_udevadm_info] )) ||
 _udevadm_info(){
     _arguments \
+        '(-)'{-h,--help}'[Print help]' \
+        '(-)'{-V,--version}'[Print version of the program]' \
         '--query=[Query the database for specified type of device data. It needs the --path or --name to identify the specified device.]:type:(name symlink path property all)' \
         '--path=[The devpath of the device to query.]:sys files:_files -P /sys/ -W /sys' \
         '--name=[The name of the device node or a symlink to query]:device files:_files -P /dev/ -W /dev' \
@@ -15,73 +17,76 @@ _udevadm_info(){
         '--export-db[Export the content of the udev database.]' \
         '--cleanup-db[Cleanup the udev database.]' \
         '--value[When showing properties, print only their values.]' \
-        '--property=[Show only properties by this name.]'
+        '--property=[Show only properties by this name.]:NAME'
 }
 
 (( $+functions[_udevadm_trigger] )) ||
 _udevadm_trigger(){
     _arguments \
+        '(-)'{-h,--help}'[Show help]' \
+        '(-)'{-V,--version}'[Show package version]' \
         '--verbose[Print the list of devices which will be triggered.]' \
         '--dry-run[Do not actually trigger the event.]' \
         '--quiet[Suppress error logging in triggering events.]' \
         '--type=[Trigger a specific type of devices.]:types:(all devices subsystems failed)' \
         '--action=[Type of event to be triggered.]:actions:(add change remove move online offline bind unbind)' \
-        '--subsystem-match=[Trigger events for devices which belong to a matching subsystem.]' \
-        '--subsystem-nomatch=[Do not trigger events for devices which belong to a matching subsystem.]' \
-        '--attr-match=attribute=[Trigger events for devices with a matching sysfs attribute.]' \
-        '--attr-nomatch=attribute=[Do not trigger events for devices with a matching sysfs attribute.]' \
-        '--property-match=[Trigger events for devices with a matching property value.]' \
-        '--tag-match=property[Trigger events for devices with a matching tag.]' \
-        '--sysname-match=[Trigger events for devices with a matching sys device name.]' \
-        '--parent-match=[Trigger events for all children of a given device.]' \
+        '--subsystem-match=[Trigger events for devices which belong to a matching subsystem.]:SUBSYSTEM' \
+        '--subsystem-nomatch=[Do not trigger events for devices which belong to a matching subsystem.]:SUBSYSTEM' \
+        '--attr-match=attribute=[Trigger events for devices with a matching sysfs attribute.]:FILE' \
+        '--attr-nomatch=attribute=[Do not trigger events for devices with a matching sysfs attribute.]:FILE' \
+        '--property-match=[Trigger events for devices with a matching property value.]:KEY=VALUE' \
+        '--tag-match=[Trigger events for devices with a matching tag.]:TAG' \
+        '--sysname-match=[Trigger events for devices with a matching sys device name.]:NAME' \
+        '--parent-match=[Trigger events for all children of a given device.]:NAME' \
         '--initialized-match[Trigger events for devices that are already initialized.]' \
         '--initialized-nomatch[Trigger events for devices that are not initialized yet.]' \
         '--uuid[Print synthetic uevent UUID.]' \
-        '--prioritized-subsystem=[Trigger events for devices which belong to a matching subsystem earlier.]'
+        '--prioritized-subsystem=[Trigger events for devices which belong to a matching subsystem earlier.]:SUBSYSTEM'
 }
 
 (( $+functions[_udevadm_settle] )) ||
 _udevadm_settle(){
     _arguments \
-       '--timeout=[Maximum number of seconds to wait for the event queue to become empty.]' \
-       '--seq-start=[Wait only for events after the given sequence number.]' \
-       '--seq-end=[Wait only for events before the given sequence number.]' \
-       '--exit-if-exists=[Stop waiting if file exists.]:files:_files' \
-       '--quiet[Do not print any output, like the remaining queue entries when reaching the timeout.]' \
-       '--help[Print help text.]'
+       '(-)'{-h,--help}'[Print help]' \
+       '(-)'{-V,--version}'[Print version of the program]' \
+       '(-t --timeout)'{-t,--timeout=}'[Maximum number of seconds to wait for the event queue to become empty.]:SEC' \
+       '(-E --exit-if-exists)'{-E,--exit-if-exists=}'[Stop waiting if file exists.]:files:_files'
 }
 
 (( $+functions[_udevadm_control] )) ||
 _udevadm_control(){
     _arguments \
-        '--exit[Signal and wait for systemd-udevd to exit.]' \
-        '--log-priority=[Set the internal log level of systemd-udevd.]:priorities:(err info debug)' \
-        '--stop-exec-queue[Signal systemd-udevd to stop executing new events. Incoming events will be queued.]' \
-        '--start-exec-queue[Signal systemd-udevd to enable the execution of events.]' \
-        '--reload[Signal systemd-udevd to reload the rules files and other databases like the kernel module index.]' \
-        '--property=[Set a global property for all events.]' \
-        '--children-max=[Set the maximum number of events.]' \
-        '--timeout=[The maximum number of seconds to wait for a reply from systemd-udevd.]' \
-        '--help[Print help text.]'
+        '(-)'{-h,--help}'[Show help]' \
+        '(-)'{-V,--version}'[Show package version]' \
+        '(-e --exit)'{-e,--exit}'[Signal and wait for systemd-udevd to exit.]' \
+        '(-l --log-level)'{-l,--log-level=}'[Set the internal log level of systemd-udevd.]:LEVEL:(err info debug)' \
+        '(-s --stop-exec-queue)'{-s,--stop-exec-queue}'[Signal systemd-udevd to stop executing new events. Incoming events will be queued.]' \
+        '(-S --start-exec-queue)'{-S,--start-exec-queue}'[Signal systemd-udevd to enable the execution of events.]' \
+        '(-R --reload)'{-R,--reload}'[Signal systemd-udevd to reload the rules files and other databases like the kernel module index.]' \
+        '(-p --property)'{-p,--property=}'[Set a global property for all events.]:KEY=VALUE' \
+        '(-m --children-max=)'{-m,--children-max=}'[Set the maximum number of events.]:N' \
+        '(-t --timeout=)'{-t,--timeout=}'[The maximum number of seconds to wait for a reply from systemd-udevd.]:SECONDS'
 }
 
 (( $+functions[_udevadm_monitor] )) ||
 _udevadm_monitor(){
     _arguments \
-        '--kernel[Print the kernel uevents.]' \
-        '--udev[Print the udev event after the rule processing.]' \
-        '--property[Also print the properties of the event.]' \
-        '--subsystem-match=[Filter events by subsystem/\[devtype\].]' \
-        '--tag-match=[Filter events by property.]' \
-        '--help[Print help text.]'
+        '(-)'{-h,--help}'[Show help]' \
+        '(-)'{-V,--version}'[Show package version]' \
+        '(-k --kernel)'{-k,--kernel}'[Print the kernel uevents.]' \
+        '(-u --udev)'{-u,--udev}'[Print the udev event after the rule processing.]' \
+        '(-p --property)'{-p,--property}'[Also print the properties of the event.]' \
+        '(-s --subsystem-match)'{-s,--subsystem-match=}'[Filter events by subsystem/\[devtype\].]:SUBSYSTEM' \
+        '(-t --tag-match)'{-t,--tag-match=}'[Filter events by property.]:TAG'
 }
 
 (( $+functions[_udevadm_test] )) ||
 _udevadm_test(){
     _arguments \
+        '(-)'{-h,--help}'[Show help]' \
+        '(-)'{-V,--version}'[Show package version]' \
         '--action=[The action string.]:actions:(add change remove move online offline bind unbind)' \
         '--subsystem=[The subsystem string.]' \
-        '--help[Print help text.]' \
         '*::devpath:_files -P /sys/ -W /sys'
 }
 
@@ -89,51 +94,55 @@ _udevadm_test(){
 _udevadm_test-builtin(){
     if (( CURRENT == 2 )); then
     _arguments \
+        '(- *)'{-h,--help}'[Print help]' \
+        '(- *)'{-V,--version}'[Print version of the program]' \
         '--action=[The action string.]:actions:(add change remove move online offline bind unbind)' \
-        '--help[Print help text]' \
         '*::builtins:(blkid btrfs hwdb input_id net_id net_setup_link kmod path_id usb_id uaccess)'
     elif  (( CURRENT == 3 )); then
         _arguments \
             '--action=[The action string.]:actions:(add change remove move online offline bind unbind)' \
-            '--help[Print help text]' \
+            '(- *)--help[Print help text]' \
             '*::syspath:_files -P /sys -W /sys'
     else
         _arguments \
             '--action=[The action string.]:actions:(add change remove move online offline bind unbind)' \
-            '--help[Print help text]'
+            '(- *)--help[Print help text]'
     fi
 }
 
 (( $+functions[_udevadm_verify] )) ||
 _udevadm_verify(){
     _arguments \
-        {-N+,--resolve-names=}'[When to resolve names.]:resolve:(early never)' \
+        '(- *)'{-h,--help}'[Show help]' \
+        '(- *)'{-V,--version}'[Show package version]' \
+        '(-N --resolve-names)'{-N+,--resolve-names=}'[When to resolve names.]:resolve:(early never)' \
         '--root=[Operate on catalog hierarchy under specified directory]:directories:_directories' \
-        {--no-summary}'[Do not show summary.]' \
-        {--no-style}'[Ignore style issues.]' \
-        {-h,--help}'[Print help text.]' \
+        --no-summary'[Do not show summary.]' \
+        --no-style'[Ignore style issues.]' \
         '*::files:_files'
 }
 
 (( $+functions[_udevadm_wait] )) ||
 _udevadm_wait(){
     _arguments \
-        '--timeout=[Maximum number of seconds to wait for the devices being created.]' \
+        '(- *)'{-h,--help}'[Print help]' \
+        '(- *)'{-V,--version}'[Print version of the program]' \
+        '--timeout=[Maximum number of seconds to wait for the devices being created.]:SEC' \
         '--initialized=[Wait for devices being initialized by systemd-udevd.]:boolean:(yes no)' \
         '--removed[Wait for devices being removed.]' \
         '--settle[Also wait for udev queue being empty.]' \
-        '--help[Print help text.]' \
         '*::devpath:_files -P /dev/ -W /dev'
 }
 
 (( $+functions[_udevadm_lock] )) ||
 _udevadm_lock(){
     _arguments \
-        '--timeout=[Maximum number of seconds to wait for the devices being locked.]' \
-        '--device=[Block device to lock.]' \
-        '--backing=[File whose backing block device to lock.]' \
-        '--print[Only show which block device the lock would be taken on.]' \
-        '--help[Print help text.]'
+        '(- *)'{-h,--help}'[Print help]' \
+        '(- *)'{-V,--version}'[Print version of the program]' \
+        '(-t --timeout)'{-t,--timeout=}'[Maximum number of seconds to wait for the devices being locked.]:SECS' \
+        '(-d --device)'{-d,--device=}'[Block device to lock.]:DEVICE' \
+        '(-b --backing)'{-b,--backing=}'[File whose backing block device to lock.]:FILE' \
+        '(-p --print)'{-p,--print}'[Only show which block device the lock would be taken on.]'
 }
 
 (( $+functions[_udevadm_mounts] )) ||
@@ -189,6 +198,6 @@ _udevadm_commands(){
 
 _arguments \
     '--debug[Print debug messages to stderr]' \
-    '--version[Print version number]' \
-    '--help[Print help text]' \
+    '(- *)--version[Print version number]' \
+    '(- *)--help[Print help text]' \
     '*::udevadm commands:_udevadm_commands'
index 6201b4facc4175976a2b1f767ad86ac0ad6300c5..797f23ae85a40fca876c053a8edf3ae2329b5345 100644 (file)
@@ -316,7 +316,10 @@ static int produce_plot_as_svg(
                     strempty(host->virtualization));
 
         svg("<g transform=\"translate(%.3f,100)\">\n", 20.0 + (SCALE_X * boot->firmware_time));
-        svg_graph_box(m, -(double) boot->firmware_time, boot->finish_time);
+        if (timestamp_is_set(boot->softreboot_start_time))
+                svg_graph_box(m, 0, boot->finish_time);
+        else
+                svg_graph_box(m, -(double) boot->firmware_time, boot->finish_time);
 
         if (timestamp_is_set(boot->firmware_time)) {
                 svg_bar("firmware", -(double) boot->firmware_time, -(double) boot->loader_time, y);
@@ -344,6 +347,11 @@ static int produce_plot_as_svg(
                 svg_text(true, boot->initrd_time, y, "initrd");
                 y++;
         }
+        if (timestamp_is_set(boot->softreboot_start_time)) {
+                svg_bar("soft-reboot", 0, boot->userspace_time, y);
+                svg_text(true, 0, y, "soft-reboot");
+                y++;
+        }
 
         for (u = times; u->has_data; u++) {
                 if (u->activating >= boot->userspace_time)
index 741cab33b67113d25e2b532ddb9a3b359750daf1..94f6031893d6141e00ca5b998c50330818580cc8 100644 (file)
@@ -38,6 +38,7 @@ int acquire_boot_times(sd_bus *bus, bool require_finished, BootTimes **ret) {
                 { "FinishTimestampMonotonic",                 "t", NULL, offsetof(BootTimes, finish_time)                   },
                 { "SecurityStartTimestampMonotonic",          "t", NULL, offsetof(BootTimes, security_start_time)           },
                 { "SecurityFinishTimestampMonotonic",         "t", NULL, offsetof(BootTimes, security_finish_time)          },
+                { "SoftRebootStartTimestampMonotonic",        "t", NULL, offsetof(BootTimes, softreboot_start_time)         },
                 { "GeneratorsStartTimestampMonotonic",        "t", NULL, offsetof(BootTimes, generators_start_time)         },
                 { "GeneratorsFinishTimestampMonotonic",       "t", NULL, offsetof(BootTimes, generators_finish_time)        },
                 { "UnitsLoadStartTimestampMonotonic",         "t", NULL, offsetof(BootTimes, unitsload_start_time)          },
@@ -48,6 +49,7 @@ int acquire_boot_times(sd_bus *bus, bool require_finished, BootTimes **ret) {
                 { "InitRDGeneratorsFinishTimestampMonotonic", "t", NULL, offsetof(BootTimes, initrd_generators_finish_time) },
                 { "InitRDUnitsLoadStartTimestampMonotonic",   "t", NULL, offsetof(BootTimes, initrd_unitsload_start_time)   },
                 { "InitRDUnitsLoadFinishTimestampMonotonic",  "t", NULL, offsetof(BootTimes, initrd_unitsload_finish_time)  },
+                { "SoftRebootsCount",                         "t", NULL, offsetof(BootTimes, soft_reboots_count)            },
                 {},
         };
         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
@@ -81,7 +83,25 @@ int acquire_boot_times(sd_bus *bus, bool require_finished, BootTimes **ret) {
         if (require_finished && times.finish_time <= 0)
                 return log_not_finished(times.finish_time);
 
-        if (arg_runtime_scope == RUNTIME_SCOPE_SYSTEM && times.security_start_time > 0) {
+        if (arg_runtime_scope == RUNTIME_SCOPE_SYSTEM && timestamp_is_set(times.softreboot_start_time)) {
+                /* On soft-reboot ignore kernel/firmware/initrd times as they are from the previous boot */
+                times.firmware_time = times.loader_time = times.kernel_time = times.initrd_time =
+                                times.initrd_security_start_time = times.initrd_security_finish_time =
+                                times.initrd_generators_start_time = times.initrd_generators_finish_time =
+                                times.initrd_unitsload_start_time = times.initrd_unitsload_finish_time = 0;
+                times.reverse_offset = times.softreboot_start_time;
+
+                /* Clamp all timestamps to avoid showing huge graphs */
+                if (timestamp_is_set(times.finish_time))
+                        subtract_timestamp(&times.finish_time, times.reverse_offset);
+                subtract_timestamp(&times.userspace_time, times.reverse_offset);
+
+                subtract_timestamp(&times.generators_start_time, times.reverse_offset);
+                subtract_timestamp(&times.generators_finish_time, times.reverse_offset);
+
+                subtract_timestamp(&times.unitsload_start_time, times.reverse_offset);
+                subtract_timestamp(&times.unitsload_finish_time, times.reverse_offset);
+        } else if (arg_runtime_scope == RUNTIME_SCOPE_SYSTEM && timestamp_is_set(times.security_start_time)) {
                 /* security_start_time is set when systemd is not running under container environment. */
                 if (times.initrd_time > 0)
                         times.kernel_done_time = times.initrd_time;
@@ -183,6 +203,8 @@ int pretty_boot_time(sd_bus *bus, char **ret) {
                 return log_oom();
         if (timestamp_is_set(t->initrd_time) && !strextend(&text, FORMAT_TIMESPAN(t->userspace_time - t->initrd_time, USEC_PER_MSEC), " (initrd) + "))
                 return log_oom();
+        if (timestamp_is_set(t->softreboot_start_time) && strextendf(&text, "%s (soft reboot #%" PRIu64 ") + ", FORMAT_TIMESPAN(t->userspace_time, USEC_PER_MSEC), t->soft_reboots_count) < 0)
+                return log_oom();
 
         if (!strextend(&text, FORMAT_TIMESPAN(t->finish_time - t->userspace_time, USEC_PER_MSEC), " (userspace) "))
                 return log_oom();
@@ -192,7 +214,13 @@ int pretty_boot_time(sd_bus *bus, char **ret) {
                         return log_oom();
 
         if (unit_id && timestamp_is_set(activated_time)) {
-                usec_t base = timestamp_is_set(t->userspace_time) ? t->userspace_time : t->reverse_offset;
+                usec_t base;
+
+                /* On soft-reboot times are clamped to avoid showing huge graphs */
+                if (timestamp_is_set(t->softreboot_start_time) && timestamp_is_set(t->userspace_time))
+                        base = t->userspace_time + t->reverse_offset;
+                else
+                        base = timestamp_is_set(t->userspace_time) ? t->userspace_time : t->reverse_offset;
 
                 if (!strextend(&text, "\n", unit_id, " reached after ", FORMAT_TIMESPAN(activated_time - base, USEC_PER_MSEC), " in userspace."))
                         return log_oom();
@@ -299,10 +327,28 @@ int acquire_time_data(sd_bus *bus, bool require_finished, UnitTimes **out) {
                         return log_error_errno(r, "Failed to get timestamp properties of unit %s: %s",
                                                u.id, bus_error_message(&error, r));
 
+                /* Activated in the previous soft-reboot iteration? Ignore it, we want new activations */
+                if ((t->activated > 0 && t->activated < boot_times->softreboot_start_time) ||
+                    (t->activating > 0 && t->activating < boot_times->softreboot_start_time))
+                        continue;
+
                 subtract_timestamp(&t->activating, boot_times->reverse_offset);
                 subtract_timestamp(&t->activated, boot_times->reverse_offset);
-                subtract_timestamp(&t->deactivating, boot_times->reverse_offset);
-                subtract_timestamp(&t->deactivated, boot_times->reverse_offset);
+
+                /* If the last deactivation was in the previous soft-reboot, ignore it */
+                if (timestamp_is_set(boot_times->softreboot_start_time)) {
+                        if (t->deactivating < boot_times->reverse_offset)
+                                t->deactivating = 0;
+                        else
+                                subtract_timestamp(&t->deactivating, boot_times->reverse_offset);
+                        if (t->deactivated < boot_times->reverse_offset)
+                                t->deactivated = 0;
+                        else
+                                subtract_timestamp(&t->deactivated, boot_times->reverse_offset);
+                } else {
+                        subtract_timestamp(&t->deactivating, boot_times->reverse_offset);
+                        subtract_timestamp(&t->deactivated, boot_times->reverse_offset);
+                }
 
                 if (t->activated >= t->activating)
                         t->time = t->activated - t->activating;
index 9049d876b258defecc5a69be84f2370280c8f98c..1932d59c3d4c6bafd50a4e379a194628c2e121c2 100644 (file)
@@ -14,6 +14,7 @@ typedef struct BootTimes {
         usec_t initrd_time;
         usec_t userspace_time;
         usec_t finish_time;
+        usec_t softreboot_start_time;
         usec_t security_start_time;
         usec_t security_finish_time;
         usec_t generators_start_time;
@@ -26,6 +27,8 @@ typedef struct BootTimes {
         usec_t initrd_generators_finish_time;
         usec_t initrd_unitsload_start_time;
         usec_t initrd_unitsload_finish_time;
+        /* Not strictly a timestamp, but we are going to show it next to the other timestamps */
+        uint64_t soft_reboots_count;
 
         /*
          * If we're analyzing the user instance, all timestamps will be offset by its own start-up timestamp,
index 371d31633288520b8662680766886d9b66221b0c..20285a7227dbff73ebc09f6643bb4bc91ce54d58 100644 (file)
@@ -234,10 +234,7 @@ static int run(int argc, char *argv[]) {
         if (r <= 0)
                 return r;
 
-        if (arg_timeout > 0)
-                timeout = usec_add(now(CLOCK_MONOTONIC), arg_timeout);
-        else
-                timeout = 0;
+        timeout = arg_timeout > 0 ? usec_add(now(CLOCK_MONOTONIC), arg_timeout) : 0;
 
         AskPasswordRequest req = {
                 .message = arg_message,
index 61264059941b769a0e9d4ab748e368e3d57dc8d4..2de6c2008af0d6e01f9c74c94f7250b4fbcc7ff9 100644 (file)
@@ -55,6 +55,10 @@ static int has_multiple_graphics_cards(void) {
         if (r < 0)
                 return r;
 
+        r = sd_device_enumerator_allow_uninitialized(e);
+        if (r < 0)
+                return r;
+
         r = sd_device_enumerator_add_match_subsystem(e, "pci", /* match = */ true);
         if (r < 0)
                 return r;
index 6991460f86e65a0b7b9a30293f6c71bba8025da6..8303b376843e755fa0994a59d4fd61b6e0c2eba4 100644 (file)
@@ -22,6 +22,7 @@
 #include "log.h"
 #include "login-util.h"
 #include "macro.h"
+#include "missing_fs.h"
 #include "missing_magic.h"
 #include "missing_threads.h"
 #include "mkdir.h"
 #include "user-util.h"
 #include "xattr-util.h"
 
+int cg_path_open(const char *controller, const char *path) {
+        _cleanup_free_ char *fs = NULL;
+        int r;
+
+        r = cg_get_path(controller, path, /* item=*/ NULL, &fs);
+        if (r < 0)
+                return r;
+
+        return RET_NERRNO(open(fs, O_DIRECTORY|O_CLOEXEC));
+}
+
+int cg_cgroupid_open(int cgroupfs_fd, uint64_t id) {
+        _cleanup_close_ int fsfd = -EBADF;
+
+        if (cgroupfs_fd < 0) {
+                fsfd = open("/sys/fs/cgroup", O_CLOEXEC|O_DIRECTORY);
+                if (fsfd < 0)
+                        return -errno;
+
+                cgroupfs_fd = fsfd;
+        }
+
+        cg_file_handle fh = CG_FILE_HANDLE_INIT;
+        CG_FILE_HANDLE_CGROUPID(fh) = id;
+
+        int fd = open_by_handle_at(cgroupfs_fd, &fh.file_handle, O_DIRECTORY|O_CLOEXEC);
+        if (fd < 0)
+                return -errno;
+
+        return fd;
+}
+
 static int cg_enumerate_items(const char *controller, const char *path, FILE **ret, const char *item) {
         _cleanup_free_ char *fs = NULL;
         FILE *f;
@@ -1404,7 +1437,7 @@ int cg_pid_get_machine_name(pid_t pid, char **ret_machine) {
 
 int cg_path_get_cgroupid(const char *path, uint64_t *ret) {
         cg_file_handle fh = CG_FILE_HANDLE_INIT;
-        int mnt_id = -1;
+        int mnt_id;
 
         assert(path);
         assert(ret);
@@ -1418,6 +1451,20 @@ int cg_path_get_cgroupid(const char *path, uint64_t *ret) {
         return 0;
 }
 
+int cg_fd_get_cgroupid(int fd, uint64_t *ret) {
+        cg_file_handle fh = CG_FILE_HANDLE_INIT;
+        int mnt_id = -1;
+
+        assert(fd >= 0);
+        assert(ret);
+
+        if (name_to_handle_at(fd, "", &fh.file_handle, &mnt_id, AT_EMPTY_PATH) < 0)
+                return -errno;
+
+        *ret = CG_FILE_HANDLE_CGROUPID(fh);
+        return 0;
+}
+
 int cg_path_get_session(const char *path, char **ret_session) {
         _cleanup_free_ char *unit = NULL;
         char *start, *end;
index 244f3b657bf2e55fe20949978c6351796ddd5178..9ad6dd8eb6ce52e232ef670fbffea4760ddacb16 100644 (file)
@@ -180,6 +180,9 @@ typedef enum CGroupUnified {
  * generate paths with multiple adjacent / removed.
  */
 
+int cg_path_open(const char *controller, const char *path);
+int cg_cgroupid_open(int fsfd, uint64_t id);
+
 int cg_enumerate_processes(const char *controller, const char *path, FILE **ret);
 int cg_read_pid(FILE *f, pid_t *ret);
 int cg_read_pidref(FILE *f, PidRef *ret);
@@ -267,6 +270,7 @@ int cg_is_empty_recursive(const char *controller, const char *path);
 int cg_get_root_path(char **path);
 
 int cg_path_get_cgroupid(const char *path, uint64_t *ret);
+int cg_fd_get_cgroupid(int fd, uint64_t *ret);
 int cg_path_get_session(const char *path, char **ret_session);
 int cg_path_get_owner_uid(const char *path, uid_t *ret_uid);
 int cg_path_get_unit(const char *path, char **ret_unit);
@@ -352,5 +356,10 @@ typedef union {
         uint8_t space[offsetof(struct file_handle, f_handle) + sizeof(uint64_t)];
 } cg_file_handle;
 
-#define CG_FILE_HANDLE_INIT { .file_handle.handle_bytes = sizeof(uint64_t) }
+#define CG_FILE_HANDLE_INIT                                     \
+        (cg_file_handle) {                                      \
+                .file_handle.handle_bytes = sizeof(uint64_t),   \
+                .file_handle.handle_type = FILEID_KERNFS,       \
+        }
+
 #define CG_FILE_HANDLE_CGROUPID(fh) (*(uint64_t*) (fh).file_handle.f_handle)
index a321df3c672d5c9b9e41de65382d05c087a7d528..8022f552943f47d3d451636d8e943abc21b697e3 100644 (file)
@@ -49,6 +49,8 @@ int dlopen_many_sym_or_warn_sentinel(void **dlp, const char *filename, int log_l
                 return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
                                        "%s is not installed: %s", filename, dlerror());
 
+        log_debug("Loaded '%s' via dlopen()", filename);
+
         va_list ap;
         va_start(ap, log_level);
         r = dlsym_many_or_warnv(dl, log_level, ap);
index fdc6c439bb85cb563080b0c7c2e947b77fb5a3a7..43f498de0ff7e34108f82e55d703eafa1a309f19 100644 (file)
@@ -177,12 +177,13 @@ static int efi_verify_variable(const char *variable, uint32_t attr, const void *
 }
 
 int efi_set_variable(const char *variable, const void *value, size_t size) {
+        static const uint32_t attr = EFI_VARIABLE_NON_VOLATILE|EFI_VARIABLE_BOOTSERVICE_ACCESS|EFI_VARIABLE_RUNTIME_ACCESS;
+
         struct var {
                 uint32_t attr;
                 char buf[];
         } _packed_ * _cleanup_free_ buf = NULL;
         _cleanup_close_ int fd = -EBADF;
-        uint32_t attr = EFI_VARIABLE_NON_VOLATILE|EFI_VARIABLE_BOOTSERVICE_ACCESS|EFI_VARIABLE_RUNTIME_ACCESS;
         bool saved_flags_valid = false;
         unsigned saved_flags;
         int r;
@@ -190,14 +191,14 @@ int efi_set_variable(const char *variable, const void *value, size_t size) {
         assert(variable);
         assert(value || size == 0);
 
-        const char *p = strjoina("/sys/firmware/efi/efivars/", variable);
-
         /* size 0 means removal, empty variable would not be enough for that */
         if (size > 0 && efi_verify_variable(variable, attr, value, size) > 0) {
                 log_debug("Variable '%s' is already in wanted state, skipping write.", variable);
                 return 0;
         }
 
+        const char *p = strjoina("/sys/firmware/efi/efivars/", variable);
+
         /* Newer efivarfs protects variables that are not in an allow list with FS_IMMUTABLE_FL by default,
          * to protect them for accidental removal and modification. We are not changing these variables
          * accidentally however, hence let's unset the bit first. */
@@ -238,10 +239,10 @@ int efi_set_variable(const char *variable, const void *value, size_t size) {
 
         /* For some reason efivarfs doesn't update mtime automatically. Let's do it manually then. This is
          * useful for processes that cache EFI variables to detect when changes occurred. */
-        if (futimens(fd, (struct timespec[2]) {
+        if (futimens(fd, (const struct timespec[2]) {
                                 { .tv_nsec = UTIME_NOW },
                                 { .tv_nsec = UTIME_NOW }
-                        }) < 0)
+                         }) < 0)
                 log_debug_errno(errno, "Failed to update mtime/atime on %s, ignoring: %m", p);
 
         r = 0;
index 41c9362be18e007d67dc450f739f8de9f17de23c..081866537c40eb4e7c8c02745e0cc5845b787055 100644 (file)
 #include "gcrypt-util.h"
 #include "hexdecoct.h"
 
-void initialize_libgcrypt(bool secmem) {
-        if (gcry_control(GCRYCTL_INITIALIZATION_FINISHED_P))
-                return;
+static void *gcrypt_dl = NULL;
 
-        gcry_control(GCRYCTL_SET_PREFERRED_RNG_TYPE, GCRY_RNG_TYPE_SYSTEM);
-        assert_se(gcry_check_version("1.4.5"));
+static DLSYM_FUNCTION(gcry_control);
+static DLSYM_FUNCTION(gcry_check_version);
+DLSYM_FUNCTION(gcry_md_close);
+DLSYM_FUNCTION(gcry_md_copy);
+DLSYM_FUNCTION(gcry_md_ctl);
+DLSYM_FUNCTION(gcry_md_get_algo_dlen);
+DLSYM_FUNCTION(gcry_md_open);
+DLSYM_FUNCTION(gcry_md_read);
+DLSYM_FUNCTION(gcry_md_reset);
+DLSYM_FUNCTION(gcry_md_setkey);
+DLSYM_FUNCTION(gcry_md_write);
+DLSYM_FUNCTION(gcry_mpi_add);
+DLSYM_FUNCTION(gcry_mpi_add_ui);
+DLSYM_FUNCTION(gcry_mpi_cmp);
+DLSYM_FUNCTION(gcry_mpi_cmp_ui);
+DLSYM_FUNCTION(gcry_mpi_get_nbits);
+DLSYM_FUNCTION(gcry_mpi_invm);
+DLSYM_FUNCTION(gcry_mpi_mod);
+DLSYM_FUNCTION(gcry_mpi_mul);
+DLSYM_FUNCTION(gcry_mpi_mulm);
+DLSYM_FUNCTION(gcry_mpi_new);
+DLSYM_FUNCTION(gcry_mpi_powm);
+DLSYM_FUNCTION(gcry_mpi_print);
+DLSYM_FUNCTION(gcry_mpi_release);
+DLSYM_FUNCTION(gcry_mpi_scan);
+DLSYM_FUNCTION(gcry_mpi_set_ui);
+DLSYM_FUNCTION(gcry_mpi_sub);
+DLSYM_FUNCTION(gcry_mpi_subm);
+DLSYM_FUNCTION(gcry_mpi_sub_ui);
+DLSYM_FUNCTION(gcry_prime_check);
+DLSYM_FUNCTION(gcry_randomize);
+DLSYM_FUNCTION(gcry_strerror);
+
+static int dlopen_gcrypt(void) {
+        return dlopen_many_sym_or_warn(
+                        &gcrypt_dl,
+                        "libgcrypt.so.20", LOG_DEBUG,
+                        DLSYM_ARG(gcry_control),
+                        DLSYM_ARG(gcry_check_version),
+                        DLSYM_ARG(gcry_md_close),
+                        DLSYM_ARG(gcry_md_copy),
+                        DLSYM_ARG(gcry_md_ctl),
+                        DLSYM_ARG(gcry_md_get_algo_dlen),
+                        DLSYM_ARG(gcry_md_open),
+                        DLSYM_ARG(gcry_md_read),
+                        DLSYM_ARG(gcry_md_reset),
+                        DLSYM_ARG(gcry_md_setkey),
+                        DLSYM_ARG(gcry_md_write),
+                        DLSYM_ARG(gcry_mpi_add),
+                        DLSYM_ARG(gcry_mpi_add_ui),
+                        DLSYM_ARG(gcry_mpi_cmp),
+                        DLSYM_ARG(gcry_mpi_cmp_ui),
+                        DLSYM_ARG(gcry_mpi_get_nbits),
+                        DLSYM_ARG(gcry_mpi_invm),
+                        DLSYM_ARG(gcry_mpi_mod),
+                        DLSYM_ARG(gcry_mpi_mul),
+                        DLSYM_ARG(gcry_mpi_mulm),
+                        DLSYM_ARG(gcry_mpi_new),
+                        DLSYM_ARG(gcry_mpi_powm),
+                        DLSYM_ARG(gcry_mpi_print),
+                        DLSYM_ARG(gcry_mpi_release),
+                        DLSYM_ARG(gcry_mpi_scan),
+                        DLSYM_ARG(gcry_mpi_set_ui),
+                        DLSYM_ARG(gcry_mpi_sub),
+                        DLSYM_ARG(gcry_mpi_subm),
+                        DLSYM_ARG(gcry_mpi_sub_ui),
+                        DLSYM_ARG(gcry_prime_check),
+                        DLSYM_ARG(gcry_randomize),
+                        DLSYM_ARG(gcry_strerror));
+}
+
+int initialize_libgcrypt(bool secmem) {
+        int r;
+
+        r = dlopen_gcrypt();
+        if (r < 0)
+                return r;
+
+        if (sym_gcry_control(GCRYCTL_INITIALIZATION_FINISHED_P))
+                return 0;
+
+        sym_gcry_control(GCRYCTL_SET_PREFERRED_RNG_TYPE, GCRY_RNG_TYPE_SYSTEM);
+        assert_se(sym_gcry_check_version("1.4.5"));
 
         /* Turn off "secmem". Clients which wish to make use of this
          * feature should initialize the library manually */
         if (!secmem)
-                gcry_control(GCRYCTL_DISABLE_SECMEM);
+                sym_gcry_control(GCRYCTL_DISABLE_SECMEM);
 
-        gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0);
+        sym_gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0);
+
+        return 0;
 }
 
 #  if !PREFER_OPENSSL
 int string_hashsum(const char *s, size_t len, int md_algorithm, char **out) {
-        _cleanup_(gcry_md_closep) gcry_md_hd_t md = NULL;
+        _cleanup_(sym_gcry_md_closep) gcry_md_hd_t md = NULL;
         gcry_error_t err;
         size_t hash_size;
         void *hash;
         char *enc;
+        int r;
 
-        initialize_libgcrypt(false);
+        r = initialize_libgcrypt(false);
+        if (r < 0)
+                return r;
 
-        hash_size = gcry_md_get_algo_dlen(md_algorithm);
+        hash_size = sym_gcry_md_get_algo_dlen(md_algorithm);
         assert(hash_size > 0);
 
-        err = gcry_md_open(&md, md_algorithm, 0);
+        err = sym_gcry_md_open(&md, md_algorithm, 0);
         if (gcry_err_code(err) != GPG_ERR_NO_ERROR || !md)
                 return -EIO;
 
-        gcry_md_write(md, s, len);
+        sym_gcry_md_write(md, s, len);
 
-        hash = gcry_md_read(md, 0);
+        hash = sym_gcry_md_read(md, 0);
         if (!hash)
                 return -EIO;
 
index 4c40cefbeda2d49075d8c8d9f0d251c6f76d4668..acb50e84b76f553f4e3c2ab63902a94e63b903fd 100644 (file)
@@ -9,11 +9,59 @@
 #if HAVE_GCRYPT
 #include <gcrypt.h>
 
+#include "dlfcn-util.h"
 #include "macro.h"
 
-void initialize_libgcrypt(bool secmem);
+DLSYM_PROTOTYPE(gcry_md_close);
+DLSYM_PROTOTYPE(gcry_md_copy);
+DLSYM_PROTOTYPE(gcry_md_ctl);
+DLSYM_PROTOTYPE(gcry_md_get_algo_dlen);
+DLSYM_PROTOTYPE(gcry_md_open);
+DLSYM_PROTOTYPE(gcry_md_read);
+DLSYM_PROTOTYPE(gcry_md_reset);
+DLSYM_PROTOTYPE(gcry_md_setkey);
+DLSYM_PROTOTYPE(gcry_md_write);
+DLSYM_PROTOTYPE(gcry_mpi_add);
+DLSYM_PROTOTYPE(gcry_mpi_add_ui);
+DLSYM_PROTOTYPE(gcry_mpi_cmp);
+DLSYM_PROTOTYPE(gcry_mpi_cmp_ui);
+DLSYM_PROTOTYPE(gcry_mpi_get_nbits);
+DLSYM_PROTOTYPE(gcry_mpi_invm);
+DLSYM_PROTOTYPE(gcry_mpi_mod);
+DLSYM_PROTOTYPE(gcry_mpi_mul);
+DLSYM_PROTOTYPE(gcry_mpi_mulm);
+DLSYM_PROTOTYPE(gcry_mpi_new);
+DLSYM_PROTOTYPE(gcry_mpi_powm);
+DLSYM_PROTOTYPE(gcry_mpi_print);
+DLSYM_PROTOTYPE(gcry_mpi_release);
+DLSYM_PROTOTYPE(gcry_mpi_scan);
+DLSYM_PROTOTYPE(gcry_mpi_set_ui);
+DLSYM_PROTOTYPE(gcry_mpi_sub);
+DLSYM_PROTOTYPE(gcry_mpi_subm);
+DLSYM_PROTOTYPE(gcry_mpi_sub_ui);
+DLSYM_PROTOTYPE(gcry_prime_check);
+DLSYM_PROTOTYPE(gcry_randomize);
+DLSYM_PROTOTYPE(gcry_strerror);
 
+int initialize_libgcrypt(bool secmem);
+
+static inline gcry_md_hd_t* sym_gcry_md_closep(gcry_md_hd_t *md) {
+        if (!md || !*md)
+                return NULL;
+        sym_gcry_md_close(*md);
+
+        return NULL;
+}
 DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(gcry_md_hd_t, gcry_md_close, NULL);
+
+/* Copied from gcry_md_putc from gcrypt.h due to the need to call the sym_ variant */
+#define sym_gcry_md_putc(h,c)                              \
+        do {                                               \
+                gcry_md_hd_t h__ = (h);                    \
+                if ((h__)->bufpos == (h__)->bufsize)       \
+                        sym_gcry_md_write((h__), NULL, 0); \
+                (h__)->buf[(h__)->bufpos++] = (c) & 0xff;  \
+        } while(false)
 #endif
 
 #if !PREFER_OPENSSL
index 0e05d47df309fe55b2d1debdafdbd8b6b824c293..73a295e37dc8905128abca2ca4e8985990b01db7 100644 (file)
@@ -94,6 +94,7 @@ struct btrfs_qgroup_limit {
  * struct btrfs_qgroup_inherit.flags
  */
 #define BTRFS_QGROUP_INHERIT_SET_LIMITS        (1ULL << 0)
+#define BTRFS_QGROUP_INHERIT_FLAGS_SUPP (BTRFS_QGROUP_INHERIT_SET_LIMITS)
 
 struct btrfs_qgroup_inherit {
        __u64   flags;
index ab9bcff96e4da10bdc22b8e2345d4103f1598bc6..ffa637b38c93bc4f13e68bcaa2b84bc577cb9c81 100644 (file)
@@ -1505,6 +1505,7 @@ enum {
        IFLA_BOND_AD_LACP_ACTIVE,
        IFLA_BOND_MISSED_MAX,
        IFLA_BOND_NS_IP6_TARGET,
+       IFLA_BOND_COUPLED_CONTROL,
        __IFLA_BOND_MAX,
 };
 
index c4c53a9ab9595b2a5b95e5b22cafa5bd2cd6fd3c..ff8d21f9e95b7798eaf3e00635050e1631d6697a 100644 (file)
@@ -145,7 +145,7 @@ struct in6_flowlabel_req {
 #define IPV6_TLV_PADN          1
 #define IPV6_TLV_ROUTERALERT   5
 #define IPV6_TLV_CALIPSO       7       /* RFC 5570 */
-#define IPV6_TLV_IOAM          49      /* TEMPORARY IANA allocation for IOAM */
+#define IPV6_TLV_IOAM          49      /* RFC 9486 */
 #define IPV6_TLV_JUMBO         194
 #define IPV6_TLV_HAO           201     /* home address option */
 
diff --git a/src/basic/linux/magic.h b/src/basic/linux/magic.h
new file mode 100644 (file)
index 0000000..1b40a96
--- /dev/null
@@ -0,0 +1,106 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+#ifndef __LINUX_MAGIC_H__
+#define __LINUX_MAGIC_H__
+
+#define ADFS_SUPER_MAGIC       0xadf5
+#define AFFS_SUPER_MAGIC       0xadff
+#define AFS_SUPER_MAGIC                0x5346414F
+#define AUTOFS_SUPER_MAGIC     0x0187
+#define CEPH_SUPER_MAGIC       0x00c36400
+#define CODA_SUPER_MAGIC       0x73757245
+#define CRAMFS_MAGIC           0x28cd3d45      /* some random number */
+#define CRAMFS_MAGIC_WEND      0x453dcd28      /* magic number with the wrong endianess */
+#define DEBUGFS_MAGIC          0x64626720
+#define SECURITYFS_MAGIC       0x73636673
+#define SELINUX_MAGIC          0xf97cff8c
+#define SMACK_MAGIC            0x43415d53      /* "SMAC" */
+#define RAMFS_MAGIC            0x858458f6      /* some random number */
+#define TMPFS_MAGIC            0x01021994
+#define HUGETLBFS_MAGIC        0x958458f6      /* some random number */
+#define SQUASHFS_MAGIC         0x73717368
+#define ECRYPTFS_SUPER_MAGIC   0xf15f
+#define EFS_SUPER_MAGIC                0x414A53
+#define EROFS_SUPER_MAGIC_V1   0xE0F5E1E2
+#define EXT2_SUPER_MAGIC       0xEF53
+#define EXT3_SUPER_MAGIC       0xEF53
+#define XENFS_SUPER_MAGIC      0xabba1974
+#define EXT4_SUPER_MAGIC       0xEF53
+#define BTRFS_SUPER_MAGIC      0x9123683E
+#define NILFS_SUPER_MAGIC      0x3434
+#define F2FS_SUPER_MAGIC       0xF2F52010
+#define HPFS_SUPER_MAGIC       0xf995e849
+#define ISOFS_SUPER_MAGIC      0x9660
+#define JFFS2_SUPER_MAGIC      0x72b6
+#define XFS_SUPER_MAGIC                0x58465342      /* "XFSB" */
+#define PSTOREFS_MAGIC         0x6165676C
+#define EFIVARFS_MAGIC         0xde5e81e4
+#define HOSTFS_SUPER_MAGIC     0x00c0ffee
+#define OVERLAYFS_SUPER_MAGIC  0x794c7630
+#define FUSE_SUPER_MAGIC       0x65735546
+
+#define MINIX_SUPER_MAGIC      0x137F          /* minix v1 fs, 14 char names */
+#define MINIX_SUPER_MAGIC2     0x138F          /* minix v1 fs, 30 char names */
+#define MINIX2_SUPER_MAGIC     0x2468          /* minix v2 fs, 14 char names */
+#define MINIX2_SUPER_MAGIC2    0x2478          /* minix v2 fs, 30 char names */
+#define MINIX3_SUPER_MAGIC     0x4d5a          /* minix v3 fs, 60 char names */
+
+#define MSDOS_SUPER_MAGIC      0x4d44          /* MD */
+#define EXFAT_SUPER_MAGIC      0x2011BAB0
+#define NCP_SUPER_MAGIC                0x564c          /* Guess, what 0x564c is :-) */
+#define NFS_SUPER_MAGIC                0x6969
+#define OCFS2_SUPER_MAGIC      0x7461636f
+#define OPENPROM_SUPER_MAGIC   0x9fa1
+#define QNX4_SUPER_MAGIC       0x002f          /* qnx4 fs detection */
+#define QNX6_SUPER_MAGIC       0x68191122      /* qnx6 fs detection */
+#define AFS_FS_MAGIC           0x6B414653
+
+
+#define REISERFS_SUPER_MAGIC   0x52654973      /* used by gcc */
+                                       /* used by file system utilities that
+                                          look at the superblock, etc.  */
+#define REISERFS_SUPER_MAGIC_STRING    "ReIsErFs"
+#define REISER2FS_SUPER_MAGIC_STRING   "ReIsEr2Fs"
+#define REISER2FS_JR_SUPER_MAGIC_STRING        "ReIsEr3Fs"
+
+#define SMB_SUPER_MAGIC                0x517B
+#define CIFS_SUPER_MAGIC       0xFF534D42      /* the first four bytes of SMB PDUs */
+#define SMB2_SUPER_MAGIC       0xFE534D42
+
+#define CGROUP_SUPER_MAGIC     0x27e0eb
+#define CGROUP2_SUPER_MAGIC    0x63677270
+
+#define RDTGROUP_SUPER_MAGIC   0x7655821
+
+#define STACK_END_MAGIC                0x57AC6E9D
+
+#define TRACEFS_MAGIC          0x74726163
+
+#define V9FS_MAGIC             0x01021997
+
+#define BDEVFS_MAGIC            0x62646576
+#define DAXFS_MAGIC             0x64646178
+#define BINFMTFS_MAGIC          0x42494e4d
+#define DEVPTS_SUPER_MAGIC     0x1cd1
+#define BINDERFS_SUPER_MAGIC   0x6c6f6f70
+#define FUTEXFS_SUPER_MAGIC    0xBAD1DEA
+#define PIPEFS_MAGIC            0x50495045
+#define PROC_SUPER_MAGIC       0x9fa0
+#define SOCKFS_MAGIC           0x534F434B
+#define SYSFS_MAGIC            0x62656572
+#define USBDEVICE_SUPER_MAGIC  0x9fa2
+#define MTD_INODE_FS_MAGIC      0x11307854
+#define ANON_INODE_FS_MAGIC    0x09041934
+#define BTRFS_TEST_MAGIC       0x73727279
+#define NSFS_MAGIC             0x6e736673
+#define BPF_FS_MAGIC           0xcafe4a11
+#define AAFS_MAGIC             0x5a3c69f0
+#define ZONEFS_MAGIC           0x5a4f4653
+
+/* Since UDF 2.01 is ISO 13346 based... */
+#define UDF_SUPER_MAGIC                0x15013346
+#define DMA_BUF_MAGIC          0x444d4142      /* "DMAB" */
+#define DEVMEM_MAGIC           0x454d444d      /* "DMEM" */
+#define SECRETMEM_MAGIC                0x5345434d      /* "SECM" */
+#define PID_FS_MAGIC           0x50494446      /* "PIDF" */
+
+#endif /* __LINUX_MAGIC_H__ */
index 117c6a9b845b1a6fde23a952560c0e807a5a3d90..aa4094ca2444fbb91f16d3816477c7d07043e8c5 100644 (file)
@@ -179,13 +179,17 @@ enum nft_hook_attributes {
  * enum nft_table_flags - nf_tables table flags
  *
  * @NFT_TABLE_F_DORMANT: this table is not active
+ * @NFT_TABLE_F_OWNER:   this table is owned by a process
+ * @NFT_TABLE_F_PERSIST: this table shall outlive its owner
  */
 enum nft_table_flags {
        NFT_TABLE_F_DORMANT     = 0x1,
        NFT_TABLE_F_OWNER       = 0x2,
+       NFT_TABLE_F_PERSIST     = 0x4,
 };
 #define NFT_TABLE_F_MASK       (NFT_TABLE_F_DORMANT | \
-                                NFT_TABLE_F_OWNER)
+                                NFT_TABLE_F_OWNER | \
+                                NFT_TABLE_F_PERSIST)
 
 /**
  * enum nft_table_attributes - nf_tables table netlink attributes
index d8ffa8c9ca78492ec1f18c2634f20eb8f79b6ee7..dd8787f9cf39a4a3e882b3ad4583772fbdef69a4 100644 (file)
@@ -30,6 +30,9 @@ enum {
 
 #define NEXTHOP_GRP_TYPE_MAX (__NEXTHOP_GRP_TYPE_MAX - 1)
 
+#define NHA_OP_FLAG_DUMP_STATS         BIT(0)
+#define NHA_OP_FLAG_DUMP_HW_STATS      BIT(1)
+
 enum {
        NHA_UNSPEC,
        NHA_ID,         /* u32; id for nexthop. id == 0 means auto-assign */
@@ -60,6 +63,18 @@ enum {
        /* nested; nexthop bucket attributes */
        NHA_RES_BUCKET,
 
+       /* u32; operation-specific flags */
+       NHA_OP_FLAGS,
+
+       /* nested; nexthop group stats */
+       NHA_GROUP_STATS,
+
+       /* u32; nexthop hardware stats enable */
+       NHA_HW_STATS_ENABLE,
+
+       /* u32; read-only; whether any driver collects HW stats */
+       NHA_HW_STATS_USED,
+
        __NHA_MAX,
 };
 
@@ -101,4 +116,34 @@ enum {
 
 #define NHA_RES_BUCKET_MAX     (__NHA_RES_BUCKET_MAX - 1)
 
+enum {
+       NHA_GROUP_STATS_UNSPEC,
+
+       /* nested; nexthop group entry stats */
+       NHA_GROUP_STATS_ENTRY,
+
+       __NHA_GROUP_STATS_MAX,
+};
+
+#define NHA_GROUP_STATS_MAX    (__NHA_GROUP_STATS_MAX - 1)
+
+enum {
+       NHA_GROUP_STATS_ENTRY_UNSPEC,
+
+       /* u32; nexthop id of the nexthop group entry */
+       NHA_GROUP_STATS_ENTRY_ID,
+
+       /* uint; number of packets forwarded via the nexthop group entry */
+       NHA_GROUP_STATS_ENTRY_PACKETS,
+
+       /* uint; number of packets forwarded via the nexthop group entry in
+        * hardware
+        */
+       NHA_GROUP_STATS_ENTRY_PACKETS_HW,
+
+       __NHA_GROUP_STATS_ENTRY_MAX,
+};
+
+#define NHA_GROUP_STATS_ENTRY_MAX      (__NHA_GROUP_STATS_ENTRY_MAX - 1)
+
 #endif
index 1ccdcae24372227b2f811765128fa409e58e66ee..f23ecbdd84a284b65944aa5ef82e1d18b4f0c199 100644 (file)
@@ -11,7 +11,7 @@
  * Copyright 2008 Jouni Malinen <jouni.malinen@atheros.com>
  * Copyright 2008 Colin McCabe <colin@cozybit.com>
  * Copyright 2015-2017 Intel Deutschland GmbH
- * Copyright (C) 2018-2023 Intel Corporation
+ * Copyright (C) 2018-2024 Intel Corporation
  *
  * Permission to use, copy, modify, and/or distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
  *     %NL80211_ATTR_REASON_CODE can optionally be used to specify which type
  *     of disconnection indication should be sent to the station
  *     (Deauthentication or Disassociation frame and reason code for that
- *     frame).
+ *     frame). %NL80211_ATTR_MLO_LINK_ID can be used optionally to remove
+ *     stations connected and using at least that link as one of its links.
  *
  * @NL80211_CMD_GET_MPATH: Get mesh path attributes for mesh path to
  *     destination %NL80211_ATTR_MAC on the interface identified by
@@ -2851,6 +2852,10 @@ enum nl80211_commands {
  *      mapping is as defined in section 9.4.2.314 (TID-To-Link Mapping element)
  *      in Draft P802.11be_D4.0.
  *
+ * @NL80211_ATTR_ASSOC_SPP_AMSDU: flag attribute used with
+ *     %NL80211_CMD_ASSOCIATE indicating the SPP A-MSDUs
+ *     are used on this connection
+ *
  * @NUM_NL80211_ATTR: total number of nl80211_attrs available
  * @NL80211_ATTR_MAX: highest attribute number currently defined
  * @__NL80211_ATTR_AFTER_LAST: internal use
@@ -3394,6 +3399,8 @@ enum nl80211_attrs {
        NL80211_ATTR_MLO_TTLM_DLINK,
        NL80211_ATTR_MLO_TTLM_ULINK,
 
+       NL80211_ATTR_ASSOC_SPP_AMSDU,
+
        /* add attributes here, update the policy in nl80211.c */
 
        __NL80211_ATTR_AFTER_LAST,
@@ -3534,6 +3541,7 @@ enum nl80211_iftype {
  * @NL80211_STA_FLAG_ASSOCIATED: station is associated; used with drivers
  *     that support %NL80211_FEATURE_FULL_AP_CLIENT_STATE to transition a
  *     previously added station into associated state
+ * @NL80211_STA_FLAG_SPP_AMSDU: station supports SPP A-MSDUs
  * @NL80211_STA_FLAG_MAX: highest station flag number currently defined
  * @__NL80211_STA_FLAG_AFTER_LAST: internal use
  */
@@ -3546,6 +3554,7 @@ enum nl80211_sta_flags {
        NL80211_STA_FLAG_AUTHENTICATED,
        NL80211_STA_FLAG_TDLS_PEER,
        NL80211_STA_FLAG_ASSOCIATED,
+       NL80211_STA_FLAG_SPP_AMSDU,
 
        /* keep last */
        __NL80211_STA_FLAG_AFTER_LAST,
@@ -4260,10 +4269,13 @@ enum nl80211_wmm_rule {
  *     allowed for peer-to-peer or adhoc communication under the control
  *     of a DFS master which operates on the same channel (FCC-594280 D01
  *     Section B.3). Should be used together with %NL80211_RRF_DFS only.
- * @NL80211_FREQUENCY_ATTR_NO_UHB_VLP_CLIENT: Client connection to VLP AP
+ * @NL80211_FREQUENCY_ATTR_NO_6GHZ_VLP_CLIENT: Client connection to VLP AP
  *     not allowed using this channel
- * @NL80211_FREQUENCY_ATTR_NO_UHB_AFC_CLIENT: Client connection to AFC AP
+ * @NL80211_FREQUENCY_ATTR_NO_6GHZ_AFC_CLIENT: Client connection to AFC AP
  *     not allowed using this channel
+ * @NL80211_FREQUENCY_ATTR_CAN_MONITOR: This channel can be used in monitor
+ *     mode despite other (regulatory) restrictions, even if the channel is
+ *     otherwise completely disabled.
  * @NL80211_FREQUENCY_ATTR_MAX: highest frequency attribute number
  *     currently defined
  * @__NL80211_FREQUENCY_ATTR_AFTER_LAST: internal use
@@ -4304,8 +4316,9 @@ enum nl80211_frequency_attr {
        NL80211_FREQUENCY_ATTR_NO_EHT,
        NL80211_FREQUENCY_ATTR_PSD,
        NL80211_FREQUENCY_ATTR_DFS_CONCURRENT,
-       NL80211_FREQUENCY_ATTR_NO_UHB_VLP_CLIENT,
-       NL80211_FREQUENCY_ATTR_NO_UHB_AFC_CLIENT,
+       NL80211_FREQUENCY_ATTR_NO_6GHZ_VLP_CLIENT,
+       NL80211_FREQUENCY_ATTR_NO_6GHZ_AFC_CLIENT,
+       NL80211_FREQUENCY_ATTR_CAN_MONITOR,
 
        /* keep last */
        __NL80211_FREQUENCY_ATTR_AFTER_LAST,
@@ -4318,6 +4331,10 @@ enum nl80211_frequency_attr {
 #define NL80211_FREQUENCY_ATTR_NO_IR           NL80211_FREQUENCY_ATTR_NO_IR
 #define NL80211_FREQUENCY_ATTR_GO_CONCURRENT \
                                        NL80211_FREQUENCY_ATTR_IR_CONCURRENT
+#define NL80211_FREQUENCY_ATTR_NO_UHB_VLP_CLIENT \
+       NL80211_FREQUENCY_ATTR_NO_6GHZ_VLP_CLIENT
+#define NL80211_FREQUENCY_ATTR_NO_UHB_AFC_CLIENT \
+       NL80211_FREQUENCY_ATTR_NO_6GHZ_AFC_CLIENT
 
 /**
  * enum nl80211_bitrate_attr - bitrate attributes
@@ -4455,14 +4472,7 @@ enum nl80211_reg_rule_attr {
  *     value as specified by &struct nl80211_bss_select_rssi_adjust.
  * @NL80211_SCHED_SCAN_MATCH_ATTR_BSSID: BSSID to be used for matching
  *     (this cannot be used together with SSID).
- * @NL80211_SCHED_SCAN_MATCH_PER_BAND_RSSI: Nested attribute that carries the
- *     band specific minimum rssi thresholds for the bands defined in
- *     enum nl80211_band. The minimum rssi threshold value(s32) specific to a
- *     band shall be encapsulated in attribute with type value equals to one
- *     of the NL80211_BAND_* defined in enum nl80211_band. For example, the
- *     minimum rssi threshold value for 2.4GHZ band shall be encapsulated
- *     within an attribute of type NL80211_BAND_2GHZ. And one or more of such
- *     attributes will be nested within this attribute.
+ * @NL80211_SCHED_SCAN_MATCH_PER_BAND_RSSI: Obsolete
  * @NL80211_SCHED_SCAN_MATCH_ATTR_MAX: highest scheduled scan filter
  *     attribute number currently defined
  * @__NL80211_SCHED_SCAN_MATCH_ATTR_AFTER_LAST: internal use
@@ -4475,7 +4485,7 @@ enum nl80211_sched_scan_match_attr {
        NL80211_SCHED_SCAN_MATCH_ATTR_RELATIVE_RSSI,
        NL80211_SCHED_SCAN_MATCH_ATTR_RSSI_ADJUST,
        NL80211_SCHED_SCAN_MATCH_ATTR_BSSID,
-       NL80211_SCHED_SCAN_MATCH_PER_BAND_RSSI,
+       NL80211_SCHED_SCAN_MATCH_PER_BAND_RSSI, /* obsolete */
 
        /* keep last */
        __NL80211_SCHED_SCAN_MATCH_ATTR_AFTER_LAST,
@@ -4515,8 +4525,8 @@ enum nl80211_sched_scan_match_attr {
        peer-to-peer or adhoc communication under the control of a DFS master
        which operates on the same channel (FCC-594280 D01 Section B.3).
        Should be used together with %NL80211_RRF_DFS only.
- * @NL80211_RRF_NO_UHB_VLP_CLIENT: Client connection to VLP AP not allowed
- * @NL80211_RRF_NO_UHB_AFC_CLIENT: Client connection to AFC AP not allowed
+ * @NL80211_RRF_NO_6GHZ_VLP_CLIENT: Client connection to VLP AP not allowed
+ * @NL80211_RRF_NO_6GHZ_AFC_CLIENT: Client connection to AFC AP not allowed
  */
 enum nl80211_reg_rule_flags {
        NL80211_RRF_NO_OFDM             = 1<<0,
@@ -4539,8 +4549,8 @@ enum nl80211_reg_rule_flags {
        NL80211_RRF_NO_EHT              = 1<<19,
        NL80211_RRF_PSD                 = 1<<20,
        NL80211_RRF_DFS_CONCURRENT      = 1<<21,
-       NL80211_RRF_NO_UHB_VLP_CLIENT   = 1<<22,
-       NL80211_RRF_NO_UHB_AFC_CLIENT   = 1<<23,
+       NL80211_RRF_NO_6GHZ_VLP_CLIENT  = 1<<22,
+       NL80211_RRF_NO_6GHZ_AFC_CLIENT  = 1<<23,
 };
 
 #define NL80211_RRF_PASSIVE_SCAN       NL80211_RRF_NO_IR
@@ -4549,6 +4559,8 @@ enum nl80211_reg_rule_flags {
 #define NL80211_RRF_NO_HT40            (NL80211_RRF_NO_HT40MINUS |\
                                         NL80211_RRF_NO_HT40PLUS)
 #define NL80211_RRF_GO_CONCURRENT      NL80211_RRF_IR_CONCURRENT
+#define NL80211_RRF_NO_UHB_VLP_CLIENT  NL80211_RRF_NO_6GHZ_VLP_CLIENT
+#define NL80211_RRF_NO_UHB_AFC_CLIENT  NL80211_RRF_NO_6GHZ_AFC_CLIENT
 
 /* For backport compatibility with older userspace */
 #define NL80211_RRF_NO_IR_ALL          (NL80211_RRF_NO_IR | __NL80211_RRF_NO_IBSS)
@@ -5096,14 +5108,17 @@ enum nl80211_bss_use_for {
  *     BSS isn't possible
  * @NL80211_BSS_CANNOT_USE_NSTR_NONPRIMARY: NSTR nonprimary links aren't
  *     supported by the device, and this BSS entry represents one.
- * @NL80211_BSS_CANNOT_USE_UHB_PWR_MISMATCH: STA is not supporting
+ * @NL80211_BSS_CANNOT_USE_6GHZ_PWR_MISMATCH: STA is not supporting
  *     the AP power type (SP, VLP, AP) that the AP uses.
  */
 enum nl80211_bss_cannot_use_reasons {
        NL80211_BSS_CANNOT_USE_NSTR_NONPRIMARY  = 1 << 0,
-       NL80211_BSS_CANNOT_USE_UHB_PWR_MISMATCH = 1 << 1,
+       NL80211_BSS_CANNOT_USE_6GHZ_PWR_MISMATCH        = 1 << 1,
 };
 
+#define NL80211_BSS_CANNOT_USE_UHB_PWR_MISMATCH \
+       NL80211_BSS_CANNOT_USE_6GHZ_PWR_MISMATCH
+
 /**
  * enum nl80211_bss - netlink attributes for a BSS
  *
@@ -5742,6 +5757,8 @@ struct nl80211_pattern_support {
  *     %NL80211_ATTR_SCAN_FREQUENCIES contains more than one
  *     frequency, it means that the match occurred in more than one
  *     channel.
+ * @NL80211_WOWLAN_TRIG_UNPROTECTED_DEAUTH_DISASSOC: For wakeup reporting only.
+ *     Wake up happened due to unprotected deauth or disassoc frame in MFP.
  * @NUM_NL80211_WOWLAN_TRIG: number of wake on wireless triggers
  * @MAX_NL80211_WOWLAN_TRIG: highest wowlan trigger attribute number
  *
@@ -5769,6 +5786,7 @@ enum nl80211_wowlan_triggers {
        NL80211_WOWLAN_TRIG_WAKEUP_TCP_NOMORETOKENS,
        NL80211_WOWLAN_TRIG_NET_DETECT,
        NL80211_WOWLAN_TRIG_NET_DETECT_RESULTS,
+       NL80211_WOWLAN_TRIG_UNPROTECTED_DEAUTH_DISASSOC,
 
        /* keep last */
        NUM_NL80211_WOWLAN_TRIG,
@@ -6410,8 +6428,7 @@ enum nl80211_feature_flags {
  * @NL80211_EXT_FEATURE_AP_PMKSA_CACHING: Driver/device supports PMKSA caching
  *     (set/del PMKSA operations) in AP mode.
  *
- * @NL80211_EXT_FEATURE_SCHED_SCAN_BAND_SPECIFIC_RSSI_THOLD: Driver supports
- *     filtering of sched scan results using band specific RSSI thresholds.
+ * @NL80211_EXT_FEATURE_SCHED_SCAN_BAND_SPECIFIC_RSSI_THOLD: Obsolete
  *
  * @NL80211_EXT_FEATURE_STA_TX_PWR: This driver supports controlling tx power
  *     to a station.
@@ -6520,6 +6537,11 @@ enum nl80211_feature_flags {
  *     DFS master on the same channel as described in FCC-594280 D01
  *     (Section B.3). This, for example, allows P2P GO and P2P clients to
  *     operate on DFS channels as long as there's a concurrent BSS connection.
+ *
+ * @NL80211_EXT_FEATURE_SPP_AMSDU_SUPPORT: The driver has support for SPP
+ *     (signaling and payload protected) A-MSDUs and this shall be advertised
+ *     in the RSNXE.
+ *
  * @NUM_NL80211_EXT_FEATURES: number of extended features.
  * @MAX_NL80211_EXT_FEATURES: highest extended feature index.
  */
@@ -6561,7 +6583,7 @@ enum nl80211_ext_feature_index {
        NL80211_EXT_FEATURE_ENABLE_FTM_RESPONDER,
        NL80211_EXT_FEATURE_AIRTIME_FAIRNESS,
        NL80211_EXT_FEATURE_AP_PMKSA_CACHING,
-       NL80211_EXT_FEATURE_SCHED_SCAN_BAND_SPECIFIC_RSSI_THOLD,
+       NL80211_EXT_FEATURE_SCHED_SCAN_BAND_SPECIFIC_RSSI_THOLD, /* obsolete */
        NL80211_EXT_FEATURE_EXT_KEY_ID,
        NL80211_EXT_FEATURE_STA_TX_PWR,
        NL80211_EXT_FEATURE_SAE_OFFLOAD,
@@ -6594,6 +6616,7 @@ enum nl80211_ext_feature_index {
        NL80211_EXT_FEATURE_OWE_OFFLOAD,
        NL80211_EXT_FEATURE_OWE_OFFLOAD_AP,
        NL80211_EXT_FEATURE_DFS_CONCURRENT,
+       NL80211_EXT_FEATURE_SPP_AMSDU_SUPPORT,
 
        /* add new features before the definition below */
        NUM_NL80211_EXT_FEATURES,
index 91b332f803cab4145a85cf530608fa85867a6302..8fb4757968c08e60b63f067850f803c3bd26a17e 100644 (file)
@@ -17,7 +17,7 @@ static inline int make_lock_file(const char *p, int operation, LockFile *ret) {
 int make_lock_file_for(const char *p, int operation, LockFile *ret);
 void release_lock_file(LockFile *f);
 
-#define LOCK_FILE_INIT { .dir_fd = -EBADF, .fd = -EBADF }
+#define LOCK_FILE_INIT (LockFile) { .dir_fd = -EBADF, .fd = -EBADF }
 
 /* POSIX locks with the same interface as flock(). */
 int posix_lock(int fd, int operation);
index fc80902c450e2d924d715b1a917b8537ab064cda..6df164e06f785a9db0890aedd06b18f8e7997812 100644 (file)
@@ -32,7 +32,7 @@ typedef enum LogTarget{
 } LogTarget;
 
 /* This log level disables logging completely. It can only be passed to log_set_max_level() and cannot be
- * used a regular log level. */
+ * used as a regular log level. */
 #define LOG_NULL (LOG_EMERG - 1)
 
 /* Note to readers: << and >> have lower precedence (are evaluated earlier) than & and | */
index ced027ba8318d3ec18235f48af6ef34e5eeb05e9..d71c9d8bc7921635eb343636fa5639a7cac81030 100644 (file)
@@ -35,6 +35,7 @@ basic_sources = files(
         'filesystems.c',
         'format-util.c',
         'fs-util.c',
+        'gcrypt-util.c',
         'glob-util.c',
         'glyph-util.c',
         'gunicode.c',
@@ -233,13 +234,15 @@ run_target(
 
 ############################################################
 
-filesystem_includes = ['linux/magic.h',
-                       'linux/gfs2_ondisk.h']
+filesystem_includes = files(
+        'linux/magic.h',
+        'missing_magic.h',
+)
 
 check_filesystems = find_program('check-filesystems.sh')
 r = run_command([check_filesystems, cpp, files('filesystems-gperf.gperf')] + filesystem_includes, check: false)
 if r.returncode() != 0
-        error('Unknown filesystems defined in kernel headers:\n\n' + r.stdout())
+        warning('Unknown filesystems defined in kernel headers:\n\n' + r.stdout())
 endif
 
 filesystems_gperf_h = custom_target(
@@ -277,6 +280,7 @@ libbasic = static_library(
         include_directories : basic_includes,
         dependencies : [libcap,
                         libdl,
+                        libgcrypt_cflags,
                         liblz4_cflags,
                         libm,
                         librt,
@@ -286,20 +290,3 @@ libbasic = static_library(
                         userspace],
         c_args : ['-fvisibility=default'],
         build_by_default : false)
-
-############################################################
-
-basic_gcrypt_sources = files(
-        'gcrypt-util.c',
-)
-
-# A convenience library that is separate from libbasic to avoid
-# unnecessary linking to libgcrypt.
-libbasic_gcrypt = static_library(
-        'basic-gcrypt',
-        basic_gcrypt_sources,
-        include_directories : basic_includes,
-        dependencies : [libgcrypt,
-                        userspace],
-        c_args : ['-fvisibility=default'],
-        build_by_default : false)
index ee70ae08dbcc4c02143976842267fd8dba57217c..d97b1901316b6d2e58d1240cd16d762b518d0ea7 100644 (file)
@@ -111,3 +111,8 @@ assert_cc(FS_PROJINHERIT_FL == 0x20000000);
 #else
 assert_cc(FS_KEY_DESCRIPTOR_SIZE == 8);
 #endif
+
+/* linux/exportfs.h */
+#ifndef FILEID_KERNFS
+#define FILEID_KERNFS 0xfe
+#endif
index 93a0d745772fca0a8e16852a11af69aff0117f56..fe54bbc7563edea6ff3eff7318c7f0865336f86a 100644 (file)
@@ -3,41 +3,6 @@
 
 #include <linux/magic.h>
 
-/* 62aa81d7c4c24b90fdb61da70ac0dbbc414f9939 (4.13) */
-#ifndef OCFS2_SUPER_MAGIC
-#  define OCFS2_SUPER_MAGIC 0x7461636f
-#else
-assert_cc(OCFS2_SUPER_MAGIC == 0x7461636f);
-#endif
-
-/* 67e9c74b8a873408c27ac9a8e4c1d1c8d72c93ff (4.5) */
-#ifndef CGROUP2_SUPER_MAGIC
-#  define CGROUP2_SUPER_MAGIC 0x63677270
-#else
-assert_cc(CGROUP2_SUPER_MAGIC == 0x63677270);
-#endif
-
-/* 4282d60689d4f21b40692029080440cc58e8a17d (4.1) */
-#ifndef TRACEFS_MAGIC
-#  define TRACEFS_MAGIC 0x74726163
-#else
-assert_cc(TRACEFS_MAGIC == 0x74726163);
-#endif
-
-/* e149ed2b805fefdccf7ccdfc19eca22fdd4514ac (3.19) */
-#ifndef NSFS_MAGIC
-#  define NSFS_MAGIC 0x6e736673
-#else
-assert_cc(NSFS_MAGIC == 0x6e736673);
-#endif
-
-/* b2197755b2633e164a439682fb05a9b5ea48f706 (4.4) */
-#ifndef BPF_FS_MAGIC
-#  define BPF_FS_MAGIC 0xcafe4a11
-#else
-assert_cc(BPF_FS_MAGIC == 0xcafe4a11);
-#endif
-
 /* Not exposed yet (4.20). Defined at ipc/mqueue.c */
 #ifndef MQUEUE_MAGIC
 #  define MQUEUE_MAGIC 0x19800202
@@ -45,146 +10,34 @@ assert_cc(BPF_FS_MAGIC == 0xcafe4a11);
 assert_cc(MQUEUE_MAGIC == 0x19800202);
 #endif
 
-/* Not exposed yet (as of Linux 5.4). Defined in fs/xfs/libxfs/xfs_format.h */
-#ifndef XFS_SB_MAGIC
-#  define XFS_SB_MAGIC 0x58465342
-#else
-assert_cc(XFS_SB_MAGIC == 0x58465342);
-#endif
-
-/* dea2903719283c156b53741126228c4a1b40440f (5.17) */
-#ifndef CIFS_SUPER_MAGIC
-#  define CIFS_SUPER_MAGIC 0xFF534D42
-#else
-assert_cc(CIFS_SUPER_MAGIC == 0xFF534D42);
-#endif
-
-/* dea2903719283c156b53741126228c4a1b40440f (5.17) */
-#ifndef SMB2_SUPER_MAGIC
-#  define SMB2_SUPER_MAGIC 0xFE534D42
-#else
-assert_cc(SMB2_SUPER_MAGIC == 0xFE534D42);
-#endif
-
-/* 257f871993474e2bde6c497b54022c362cf398e1 (4.5) */
-#ifndef OVERLAYFS_SUPER_MAGIC
-#  define OVERLAYFS_SUPER_MAGIC 0x794c7630
-#else
-assert_cc(OVERLAYFS_SUPER_MAGIC == 0x794c7630);
-#endif
-
-/* 2a28900be20640fcd1e548b1e3bad79e8221fcf9 (4.7) */
-#ifndef UDF_SUPER_MAGIC
-#  define UDF_SUPER_MAGIC 0x15013346
-#else
-assert_cc(UDF_SUPER_MAGIC == 0x15013346);
-#endif
-
-/* b1123ea6d3b3da25af5c8a9d843bd07ab63213f4 (4.8) */
+/* b1123ea6d3b3da25af5c8a9d843bd07ab63213f4 (4.8), dropped by 68f2736a858324c3ec852f6c2cddd9d1c777357d (v6.0) */
 #ifndef BALLOON_KVM_MAGIC
 #  define BALLOON_KVM_MAGIC 0x13661366
 #else
 assert_cc(BALLOON_KVM_MAGIC == 0x13661366);
 #endif
 
-/* 48b4800a1c6af2cdda344ea4e2c843dcc1f6afc9 (4.8) */
+/* 48b4800a1c6af2cdda344ea4e2c843dcc1f6afc9 (4.8), dropped by 68f2736a858324c3ec852f6c2cddd9d1c777357d (v6.0) */
 #ifndef ZSMALLOC_MAGIC
 #  define ZSMALLOC_MAGIC 0x58295829
 #else
 assert_cc(ZSMALLOC_MAGIC == 0x58295829);
 #endif
 
-/* 3bc52c45bac26bf7ed1dc8d287ad1aeaed1250b6 (4.9) */
-#ifndef DAXFS_MAGIC
-#  define DAXFS_MAGIC 0x64646178
-#else
-assert_cc(DAXFS_MAGIC == 0x64646178);
-#endif
-
-/* 5ff193fbde20df5d80fec367cea3e7856c057320 (4.10) */
-#ifndef RDTGROUP_SUPER_MAGIC
-#  define RDTGROUP_SUPER_MAGIC 0x7655821
-#else
-assert_cc(RDTGROUP_SUPER_MAGIC == 0x7655821);
-#endif
-
-/* a481f4d917835cad86701fc0d1e620c74bb5cd5f (4.13) */
-#ifndef AAFS_MAGIC
-#  define AAFS_MAGIC 0x5a3c69f0
-#else
-assert_cc(AAFS_MAGIC == 0x5a3c69f0);
-#endif
-
-/* f044c8847bb61eff5e1e95b6f6bb950e7f4a73a4 (4.15) */
-#ifndef AFS_FS_MAGIC
-#  define AFS_FS_MAGIC 0x6b414653
-#else
-assert_cc(AFS_FS_MAGIC == 0x6b414653);
-#endif
-
-/* dddde68b8f06dd83486124b8d245e7bfb15c185d (4.20) */
-#ifndef XFS_SUPER_MAGIC
-#  define XFS_SUPER_MAGIC 0x58465342
-#else
-assert_cc(XFS_SUPER_MAGIC == 0x58465342);
-#endif
-
-/* 3ad20fe393b31025bebfc2d76964561f65df48aa (5.0) */
-#ifndef BINDERFS_SUPER_MAGIC
-#  define BINDERFS_SUPER_MAGIC 0x6c6f6f70
-#else
-assert_cc(BINDERFS_SUPER_MAGIC == 0x6c6f6f70);
-#endif
-
-/* ed63bb1d1f8469586006a9ca63c42344401aa2ab (5.3) */
-#ifndef DMA_BUF_MAGIC
-#  define DMA_BUF_MAGIC 0x444d4142
-#else
-assert_cc(DMA_BUF_MAGIC == 0x444d4142);
-#endif
-
-/* ea8157ab2ae5e914dd427e5cfab533b6da3819cd (5.3) */
+/* ea8157ab2ae5e914dd427e5cfab533b6da3819cd (5.3), dropped by 68f2736a858324c3ec852f6c2cddd9d1c777357d (v6.0) */
 #ifndef Z3FOLD_MAGIC
 #  define Z3FOLD_MAGIC 0x33
 #else
 assert_cc(Z3FOLD_MAGIC == 0x33);
 #endif
 
-/* 47e4937a4a7ca4184fd282791dfee76c6799966a (5.4) */
-#ifndef EROFS_SUPER_MAGIC_V1
-#  define EROFS_SUPER_MAGIC_V1 0xe0f5e1e2
-#else
-assert_cc(EROFS_SUPER_MAGIC_V1 == 0xe0f5e1e2);
-#endif
-
-/* fe030c9b85e6783bc52fe86449c0a4b8aa16c753 (5.5) */
+/* fe030c9b85e6783bc52fe86449c0a4b8aa16c753 (5.5), dropped by 68f2736a858324c3ec852f6c2cddd9d1c777357d (v6.0) */
 #ifndef PPC_CMM_MAGIC
 #  define PPC_CMM_MAGIC 0xc7571590
 #else
 assert_cc(PPC_CMM_MAGIC == 0xc7571590);
 #endif
 
-/* 8dcc1a9d90c10fa4143e5c17821082e5e60e46a1 (5.6) */
-#ifndef ZONEFS_MAGIC
-#  define ZONEFS_MAGIC 0x5a4f4653
-#else
-assert_cc(ZONEFS_MAGIC == 0x5a4f4653);
-#endif
-
-/* 3234ac664a870e6ea69ae3a57d824cd7edbeacc5 (5.8) */
-#ifndef DEVMEM_MAGIC
-#  define DEVMEM_MAGIC 0x454d444d
-#else
-assert_cc(DEVMEM_MAGIC == 0x454d444d);
-#endif
-
-/* cb12fd8e0dabb9a1c8aef55a6a41e2c255fcdf4b (6.8) */
-#ifndef PID_FS_MAGIC
-#  define PID_FS_MAGIC 0x50494446
-#else
-assert_cc(PID_FS_MAGIC == 0x50494446);
-#endif
-
 /* Not in mainline but included in Ubuntu */
 #ifndef SHIFTFS_MAGIC
 #  define SHIFTFS_MAGIC 0x6a656a62
@@ -192,20 +45,6 @@ assert_cc(PID_FS_MAGIC == 0x50494446);
 assert_cc(SHIFTFS_MAGIC == 0x6a656a62);
 #endif
 
-/* 1507f51255c9ff07d75909a84e7c0d7f3c4b2f49 (5.14) */
-#ifndef SECRETMEM_MAGIC
-#  define SECRETMEM_MAGIC 0x5345434d
-#else
-assert_cc(SECRETMEM_MAGIC == 0x5345434d);
-#endif
-
-/* Not exposed yet. Defined at fs/fuse/inode.c */
-#ifndef FUSE_SUPER_MAGIC
-#  define FUSE_SUPER_MAGIC 0x65735546
-#else
-assert_cc(FUSE_SUPER_MAGIC == 0x65735546);
-#endif
-
 /* Not exposed yet. Defined at fs/fuse/control.c */
 #ifndef FUSE_CTL_SUPER_MAGIC
 #  define FUSE_CTL_SUPER_MAGIC 0x65735543
@@ -213,13 +52,6 @@ assert_cc(FUSE_SUPER_MAGIC == 0x65735546);
 assert_cc(FUSE_CTL_SUPER_MAGIC == 0x65735543);
 #endif
 
-/* Not exposed yet. Defined at fs/ceph/super.h */
-#ifndef CEPH_SUPER_MAGIC
-#  define CEPH_SUPER_MAGIC 0x00c36400
-#else
-assert_cc(CEPH_SUPER_MAGIC == 0x00c36400);
-#endif
-
 /* Not exposed yet. Defined at fs/orangefs/orangefs-kernel.h */
 #ifndef ORANGEFS_DEVREQ_MAGIC
 #  define ORANGEFS_DEVREQ_MAGIC 0x20030529
@@ -248,13 +80,6 @@ assert_cc(CONFIGFS_MAGIC == 0x62656570);
 assert_cc(VBOXSF_SUPER_MAGIC == 0x786f4256);
 #endif
 
-/* Not exposed yet. Defined at fs/exfat/exfat_fs.h */
-#ifndef EXFAT_SUPER_MAGIC
-#  define EXFAT_SUPER_MAGIC 0x2011BAB0UL
-#else
-assert_cc(EXFAT_SUPER_MAGIC == 0x2011BAB0UL);
-#endif
-
 /* Not exposed yet, internally actually called RPCAUTH_GSSMAGIC. Defined in net/sunrpc/rpc_pipe.c */
 #ifndef RPC_PIPEFS_SUPER_MAGIC
 #  define RPC_PIPEFS_SUPER_MAGIC 0x67596969
index c537c0f9b7bbd46a1b71e63e441aa58498b2b419..5b4e43f9210ef90f488950a61edd71611fb34419 100644 (file)
@@ -34,6 +34,14 @@ const struct namespace_info namespace_info[] = {
 
 #define pid_namespace_path(pid, type) procfs_file_alloca(pid, namespace_info[type].proc_path)
 
+static NamespaceType clone_flag_to_namespace_type(unsigned long clone_flag) {
+        for (NamespaceType t = 0; t < _NAMESPACE_TYPE_MAX; t++)
+                if (((namespace_info[t].clone_flag ^ clone_flag) & (CLONE_NEWCGROUP|CLONE_NEWIPC|CLONE_NEWNET|CLONE_NEWNS|CLONE_NEWPID|CLONE_NEWUSER|CLONE_NEWUTS|CLONE_NEWTIME)) == 0)
+                        return t;
+
+        return _NAMESPACE_TYPE_INVALID;
+}
+
 int namespace_open(
                 pid_t pid,
                 int *ret_pidns_fd,
@@ -214,6 +222,88 @@ int detach_mount_namespace(void) {
         return 0;
 }
 
+int detach_mount_namespace_harder(uid_t target_uid, gid_t target_gid) {
+        int r;
+
+        /* Tried detach_mount_namespace() first. If that doesn't work due to permissions, opens up an
+         * unprivileged user namespace with a mapping of the originating UID/GID to the specified target
+         * UID/GID. Then, tries detach_mount_namespace() again.
+         *
+         * Or in other words: tries much harder to get a mount namespace, making use of unprivileged user
+         * namespaces if need be.
+         *
+         * Note that after this function completed:
+         *
+         *    → if we had privs, afterwards uids/gids on files and processes are as before
+         *
+         *    → if we had no privs, our own id and all our files will show up owned by target_uid/target_gid,
+         *    and everything else owned by nobody.
+         *
+         * Yes, that's quite a difference. */
+
+        if (!uid_is_valid(target_uid))
+                return -EINVAL;
+        if (!gid_is_valid(target_gid))
+                return -EINVAL;
+
+        r = detach_mount_namespace();
+        if (r != -EPERM)
+                return r;
+
+        if (unshare(CLONE_NEWUSER) < 0)
+                return log_debug_errno(errno, "Failed to acquire user namespace: %m");
+
+        r = write_string_filef("/proc/self/uid_map", 0,
+                               UID_FMT " " UID_FMT " 1\n", target_uid, getuid());
+        if (r < 0)
+                return log_debug_errno(r, "Failed to write uid map: %m");
+
+        r = write_string_file("/proc/self/setgroups", "deny", 0);
+        if (r < 0)
+                return log_debug_errno(r, "Failed to write setgroups file: %m");
+
+        r = write_string_filef("/proc/self/gid_map", 0,
+                               GID_FMT " " GID_FMT " 1\n", target_gid, getgid());
+        if (r < 0)
+                return log_debug_errno(r, "Failed to write gid map: %m");
+
+        return detach_mount_namespace();
+}
+
+int detach_mount_namespace_userns(int userns_fd) {
+        int r;
+
+        assert(userns_fd >= 0);
+
+        if (setns(userns_fd, CLONE_NEWUSER) < 0)
+                return log_debug_errno(errno, "Failed to join user namespace: %m");
+
+        r = reset_uid_gid();
+        if (r < 0)
+                return log_debug_errno(r, "Failed to become root in user namespace: %m");
+
+        return detach_mount_namespace();
+}
+
+int userns_acquire_empty(void) {
+        _cleanup_(sigkill_waitp) pid_t pid = 0;
+        _cleanup_close_ int userns_fd = -EBADF;
+        int r;
+
+        r = safe_fork("(sd-mkuserns)", FORK_CLOSE_ALL_FDS|FORK_DEATHSIG_SIGKILL|FORK_NEW_USERNS, &pid);
+        if (r < 0)
+                return r;
+        if (r == 0)
+                /* Child. We do nothing here, just freeze until somebody kills us. */
+                freeze();
+
+        r = namespace_open(pid, NULL, NULL, NULL, &userns_fd, NULL);
+        if (r < 0)
+                return log_error_errno(r, "Failed to open userns fd: %m");
+
+        return TAKE_FD(userns_fd);
+}
+
 int userns_acquire(const char *uid_map, const char *gid_map) {
         char path[STRLEN("/proc//uid_map") + DECIMAL_STR_MAX(pid_t) + 1];
         _cleanup_(sigkill_waitp) pid_t pid = 0;
@@ -344,3 +434,50 @@ int parse_userns_uid_range(const char *s, uid_t *ret_uid_shift, uid_t *ret_uid_r
 
         return 0;
 }
+
+int namespace_open_by_type(NamespaceType type) {
+        const char *p;
+        int fd;
+
+        assert(type >= 0);
+        assert(type < _NAMESPACE_TYPE_MAX);
+
+        p = pid_namespace_path(0, type);
+
+        fd = RET_NERRNO(open(p, O_RDONLY|O_NOCTTY|O_CLOEXEC));
+        if (fd == -ENOENT && proc_mounted() == 0)
+                return -ENOSYS;
+
+        return fd;
+}
+
+int is_our_namespace(int fd, NamespaceType request_type) {
+        int clone_flag;
+
+        assert(fd >= 0);
+
+        clone_flag = ioctl(fd, NS_GET_NSTYPE);
+        if (clone_flag < 0)
+                return -errno;
+
+        NamespaceType found_type = clone_flag_to_namespace_type(clone_flag);
+        if (found_type < 0)
+                return -EBADF; /* Uh? Unknown namespace type? */
+
+        if (request_type >= 0 && request_type != found_type) /* It's a namespace, but not of the right type? */
+                return -EUCLEAN;
+
+        struct stat st_fd, st_ours;
+        if (fstat(fd, &st_fd) < 0)
+                return -errno;
+
+        const char *p = pid_namespace_path(0, found_type);
+        if (stat(p, &st_ours) < 0) {
+                if (errno == ENOENT)
+                        return proc_mounted() == 0 ? -ENOSYS : -ENOENT;
+
+                return -errno;
+        }
+
+        return stat_inode_same(&st_ours, &st_fd);
+}
index 34cbec3f6e39b82896d84aa02e52ebc831b7b0e9..545952a5d1093f6067846a1a058bd6c800076cb7 100644 (file)
@@ -34,6 +34,8 @@ int namespace_enter(int pidns_fd, int mntns_fd, int netns_fd, int userns_fd, int
 int fd_is_ns(int fd, unsigned long nsflag);
 
 int detach_mount_namespace(void);
+int detach_mount_namespace_harder(uid_t target_uid, gid_t target_gid);
+int detach_mount_namespace_userns(int userns_fd);
 
 static inline bool userns_shift_range_valid(uid_t shift, uid_t range) {
         /* Checks that the specified userns range makes sense, i.e. contains at least one UID, and the end
@@ -50,8 +52,15 @@ static inline bool userns_shift_range_valid(uid_t shift, uid_t range) {
         return true;
 }
 
+int userns_acquire_empty(void);
 int userns_acquire(const char *uid_map, const char *gid_map);
+
 int netns_acquire(void);
+
 int in_same_namespace(pid_t pid1, pid_t pid2, NamespaceType type);
 
 int parse_userns_uid_range(const char *s, uid_t *ret_uid_shift, uid_t *ret_uid_range);
+
+int namespace_open_by_type(NamespaceType type);
+
+int is_our_namespace(int fd, NamespaceType type);
index dbd067fd4467808186b2504a18a4b9397a4a4447..0d26d189d16d807d2572760c91bca1c52172ab6f 100644 (file)
@@ -61,6 +61,34 @@ bool image_name_is_valid(const char *s) {
         return true;
 }
 
+int path_extract_image_name(const char *path, char **ret) {
+        _cleanup_free_ char *fn = NULL;
+        int r;
+
+        assert(path);
+        assert(ret);
+
+        /* Extract last component from path, without any "/" suffixes. */
+        r = path_extract_filename(path, &fn);
+        if (r < 0)
+                return r;
+        if (r != O_DIRECTORY) {
+                /* Chop off any image suffixes we recognize (unless we already know this must refer to some dir) */
+                char *m = ENDSWITH_SET(fn, ".sysext.raw", ".confext.raw", ".raw");
+                if (m)
+                        *m = 0;
+        }
+
+        /* Truncate the version/counting suffixes */
+        fn[strcspn(fn, "_+")] = 0;
+
+        if (!image_name_is_valid(fn))
+                return -EINVAL;
+
+        *ret = TAKE_PTR(fn);
+        return 0;
+}
+
 int path_is_extension_tree(ImageClass image_class, const char *path, const char *extension, bool relax_extension_release_check) {
         int r;
 
@@ -230,9 +258,25 @@ int open_extension_release_at(
                         continue;
                 }
 
-                if (!relax_extension_release_check &&
-                    extension_release_strict_xattr_value(fd, dir_path, de->d_name) != 0)
-                        continue;
+                if (!relax_extension_release_check) {
+                        _cleanup_free_ char *base_image_name = NULL, *base_extension = NULL;
+
+                        r = path_extract_image_name(image_name, &base_image_name);
+                        if (r < 0) {
+                                log_debug_errno(r, "Failed to extract image name from %s/%s, ignoring: %m", dir_path, de->d_name);
+                                continue;
+                        }
+
+                        r = path_extract_image_name(extension, &base_extension);
+                        if (r < 0) {
+                                log_debug_errno(r, "Failed to extract image name from %s, ignoring: %m", extension);
+                                continue;
+                        }
+
+                        if (!streq(base_image_name, base_extension) &&
+                            extension_release_strict_xattr_value(fd, dir_path, image_name) != 0)
+                                continue;
+                }
 
                 /* We already found what we were looking for, but there's another candidate? We treat this as
                  * an error, as we want to enforce that there are no ambiguities in case we are in the
index 7cee3dd119f8bc7ce06639cd5c3111bb2ad63c53..f6a12a3fb1c6d7e402009a110985ee78cfd97d32 100644 (file)
@@ -25,6 +25,7 @@ ImageClass image_class_from_string(const char *s) _pure_;
  * in accordance with the OS extension specification, rather than for /usr/lib/ or /etc/os-release. */
 
 bool image_name_is_valid(const char *s) _pure_;
+int path_extract_image_name(const char *path, char **ret);
 
 int path_is_extension_tree(ImageClass image_class, const char *path, const char *extension, bool relax_extension_release_check);
 static inline int path_is_os_tree(const char *path) {
index 5971173915a30506cd671cc83a8f887b925d5c04..35d13eb38f6fc915843e8a4ca7fa4b7e60ed8fec 100644 (file)
@@ -633,7 +633,7 @@ int parse_fractional_part_u(const char **p, size_t digits, unsigned *res) {
         s = *p;
 
         /* accept any number of digits, strtoull is limited to 19 */
-        for (size_t i = 0; i < digits; i++,s++) {
+        for (size_t i = 0; i < digits; i++, s++) {
                 if (!ascii_isdigit(*s)) {
                         if (i == 0)
                                 return -EINVAL;
index 5f1042fae815210d78ffd8f56ac90b43f96192c3..875fa11bea878020838920424865da8cc0fb89c4 100644 (file)
@@ -237,7 +237,7 @@ int pidref_dup(const PidRef *pidref, PidRef **ret) {
 }
 
 int pidref_new_from_pid(pid_t pid, PidRef **ret) {
-        _cleanup_(pidref_freep) PidRef *n = 0;
+        _cleanup_(pidref_freep) PidRef *n = NULL;
         int r;
 
         assert(ret);
index 351b5e4095924fd4b63505c358940f91e8e839ca..c9d968dee0a7c4fc25a9ad4ec260646b63220a02 100644 (file)
@@ -25,6 +25,7 @@
 #include "alloc-util.h"
 #include "architecture.h"
 #include "argv-util.h"
+#include "cgroup-util.h"
 #include "dirent-util.h"
 #include "env-file.h"
 #include "env-util.h"
@@ -2108,7 +2109,13 @@ int posix_spawn_wrapper(
 
                 return FLAGS_SET(flags, POSIX_SPAWN_SETCGROUP);
         }
-        if (!(ERRNO_IS_NOT_SUPPORTED(r) || ERRNO_IS_PRIVILEGE(r)))
+        if (ERRNO_IS_NOT_SUPPORTED(r)) {
+                /* clone3() could also return EOPNOTSUPP if the target cgroup is in threaded mode. */
+                if (cgroup && cg_is_threaded(cgroup) > 0)
+                        return -EUCLEAN;
+
+                /* clone3() not available? */
+        } else if (!ERRNO_IS_PRIVILEGE(r))
                 return -r;
 
         /* Compiled on a newer host, or seccomp&friends blocking clone3()? Fallback, but need to change the
index 661ad00b110cf08d2723b03f8c99d78caf5b694f..27d094b7bd9fdcaed7eafcda8f2f068dc62b13bc 100644 (file)
@@ -268,7 +268,7 @@ int pop_pending_signal_internal(int sig, ...) {
         if (r < 0)
                 return r;
 
-        r = sigtimedwait(&ss, NULL, &(struct timespec) { 0, 0 });
+        r = sigtimedwait(&ss, NULL, &(const struct timespec) {});
         if (r < 0) {
                 if (errno == EAGAIN)
                         return 0;
index 5eb8e4c98855ba4585f22073064e0896e7b5ae86..e66bc426a4daf9f445e3e19b52f99cdecde75340 100644 (file)
@@ -1,9 +1,10 @@
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 
+/* Make sure the net/if.h header is included before any linux/ one */
+#include <net/if.h>
 #include <arpa/inet.h>
 #include <errno.h>
 #include <limits.h>
-#include <net/if.h>
 #include <netdb.h>
 #include <netinet/ip.h>
 #include <poll.h>
@@ -634,7 +635,7 @@ int socknameinfo_pretty(const struct sockaddr *sa, socklen_t salen, char **ret)
         int r;
 
         assert(sa);
-        assert(salen > sizeof(sa_family_t));
+        assert(salen >= sizeof(sa_family_t));
         assert(ret);
 
         r = getnameinfo(sa, salen, host, sizeof(host), /* service= */ NULL, /* service_len= */ 0, IDN_FLAGS);
index 7f6c87b4613fde9c2d04aeffe4a15a1a6f20905d..d0d33a407a673bfe2fe6ec3027d74569e0fb9547 100644 (file)
@@ -11,6 +11,7 @@
 #include "extract-word.h"
 #include "fd-util.h"
 #include "fileio.h"
+#include "glyph-util.h"
 #include "gunicode.h"
 #include "locale-util.h"
 #include "macro.h"
@@ -282,16 +283,9 @@ bool string_has_cc(const char *p, const char *ok) {
 }
 
 static int write_ellipsis(char *buf, bool unicode) {
-        if (unicode || is_locale_utf8()) {
-                buf[0] = 0xe2; /* tri-dot ellipsis: … */
-                buf[1] = 0x80;
-                buf[2] = 0xa6;
-        } else {
-                buf[0] = '.';
-                buf[1] = '.';
-                buf[2] = '.';
-        }
-
+        const char *s = special_glyph_full(SPECIAL_GLYPH_ELLIPSIS, unicode);
+        assert(strlen(s) == 3);
+        memcpy(buf, s, 3);
         return 3;
 }
 
@@ -398,8 +392,7 @@ static char *ascii_ellipsize_mem(const char *s, size_t old_length, size_t new_le
         x = ((new_length - need_space) * percent + 50) / 100;
         assert(x <= new_length - need_space);
 
-        memcpy(t, s, x);
-        write_ellipsis(t + x, false);
+        write_ellipsis(mempcpy(t, s, x), /* unicode = */ false);
         suffix_len = new_length - x - need_space;
         memcpy(t + x + 3, s + old_length - suffix_len, suffix_len);
         *(t + x + 3 + suffix_len) = '\0';
@@ -520,13 +513,8 @@ char *ellipsize_mem(const char *s, size_t old_length, size_t new_length, unsigne
         if (!e)
                 return NULL;
 
-        /*
-        printf("old_length=%zu new_length=%zu x=%zu len=%zu len2=%zu k=%zu\n",
-               old_length, new_length, x, len, len2, k);
-        */
-
         memcpy_safe(e, s, len);
-        write_ellipsis(e + len, true);
+        write_ellipsis(e + len, /* unicode = */ true);
 
         char *dst = e + len + 3;
 
@@ -562,7 +550,9 @@ char *cellescape(char *buf, size_t len, const char *s) {
 
         size_t i = 0, last_char_width[4] = {}, k = 0;
 
+        assert(buf);
         assert(len > 0); /* at least a terminating NUL */
+        assert(s);
 
         for (;;) {
                 char four[4];
@@ -603,7 +593,7 @@ char *cellescape(char *buf, size_t len, const char *s) {
         }
 
         if (i + 4 <= len) /* yay, enough space */
-                i += write_ellipsis(buf + i, false);
+                i += write_ellipsis(buf + i, /* unicode = */ false);
         else if (i + 3 <= len) { /* only space for ".." */
                 buf[i++] = '.';
                 buf[i++] = '.';
@@ -612,7 +602,7 @@ char *cellescape(char *buf, size_t len, const char *s) {
         else
                 assert(i + 1 <= len);
 
- done:
+done:
         buf[i] = '\0';
         return buf;
 }
@@ -996,7 +986,7 @@ int strextendf_with_separator(char **x, const char *separator, const char *forma
         return 0;
 
 oom:
-        /* truncate the bytes added after the first vsnprintf() attempt again */
+        /* truncate the bytes added after memcpy_safe() again */
         (*x)[m] = 0;
         return -ENOMEM;
 }
@@ -1268,18 +1258,16 @@ int string_extract_line(const char *s, size_t i, char **ret) {
         }
 }
 
-int string_contains_word_strv(const char *string, const char *separators, char **words, const char **ret_word) {
-        /* In the default mode with no separators specified, we split on whitespace and
-         * don't coalesce separators. */
+int string_contains_word_strv(const char *string, const char *separators, char * const *words, const char **ret_word) {
+        /* In the default mode with no separators specified, we split on whitespace and coalesce separators. */
         const ExtractFlags flags = separators ? EXTRACT_DONT_COALESCE_SEPARATORS : 0;
-
         const char *found = NULL;
+        int r;
 
-        for (const char *p = string;;) {
+        for (;;) {
                 _cleanup_free_ char *w = NULL;
-                int r;
 
-                r = extract_first_word(&p, &w, separators, flags);
+                r = extract_first_word(&string, &w, separators, flags);
                 if (r < 0)
                         return r;
                 if (r == 0)
index a68f3c94abc4180601c885a8a28c43fc31f4217a..055ab66668b84d2009c238f4082d95d70d6d7139 100644 (file)
@@ -271,7 +271,7 @@ char* string_erase(char *x);
 int string_truncate_lines(const char *s, size_t n_lines, char **ret);
 int string_extract_line(const char *s, size_t i, char **ret);
 
-int string_contains_word_strv(const char *string, const char *separators, char **words, const char **ret_word);
+int string_contains_word_strv(const char *string, const char *separators, char * const *words, const char **ret_word);
 static inline int string_contains_word(const char *string, const char *separators, const char *word) {
         return string_contains_word_strv(string, separators, STRV_MAKE(word), NULL);
 }
index a3c219ecddd8cea37c14732920f2dc5891491651..ebcdc41118eacd4735e6b231e1be6239672cbe09 100644 (file)
@@ -71,8 +71,8 @@ typedef enum TimestampStyle {
 
 #define TIME_T_MAX (time_t)((UINTMAX_C(1) << ((sizeof(time_t) << 3) - 1)) - 1)
 
-#define DUAL_TIMESTAMP_NULL ((struct dual_timestamp) {})
-#define TRIPLE_TIMESTAMP_NULL ((struct triple_timestamp) {})
+#define DUAL_TIMESTAMP_NULL ((dual_timestamp) {})
+#define TRIPLE_TIMESTAMP_NULL ((triple_timestamp) {})
 
 usec_t now(clockid_t clock);
 nsec_t now_nsec(clockid_t clock);
index 90eb0cd9112591dbf662c5ff67d354e5cc739594..a7658811ece4d1b8bd8696b04b0b895730ef9f27 100644 (file)
@@ -10,6 +10,7 @@
 #include "format-util.h"
 #include "macro.h"
 #include "path-util.h"
+#include "process-util.h"
 #include "sort-util.h"
 #include "stat-util.h"
 #include "uid-range.h"
@@ -172,9 +173,9 @@ bool uid_range_covers(const UIDRange *range, uid_t start, uid_t nr) {
         if (!range)
                 return false;
 
-        for (size_t i = 0; i < range->n_entries; i++)
-                if (start >= range->entries[i].start &&
-                    start + nr <= range->entries[i].start + range->entries[i].nr)
+        FOREACH_ARRAY(i, range->entries, range->n_entries)
+                if (start >= i->start &&
+                    start + nr <= i->start + i->nr)
                         return true;
 
         return false;
@@ -204,7 +205,7 @@ 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) {
+int uid_range_load_userns(const char *path, UIDRangeUsernsMode mode, UIDRange **ret) {
         _cleanup_(uid_range_freep) UIDRange *range = NULL;
         _cleanup_fclose_ FILE *f = NULL;
         int r;
@@ -215,10 +216,12 @@ int uid_range_load_userns(UIDRange **ret, const char *path) {
          *
          * To simplify things this will modify the passed array in case of later failure. */
 
+        assert(mode >= 0);
+        assert(mode < _UID_RANGE_USERNS_MODE_MAX);
         assert(ret);
 
         if (!path)
-                path = "/proc/self/uid_map";
+                path = IN_SET(mode, UID_RANGE_USERNS_INSIDE, UID_RANGE_USERNS_OUTSIDE) ? "/proc/self/uid_map" : "/proc/self/gid_map";
 
         f = fopen(path, "re");
         if (!f) {
@@ -243,7 +246,11 @@ int uid_range_load_userns(UIDRange **ret, const char *path) {
                 if (r < 0)
                         return r;
 
-                r = uid_range_add_internal(&range, uid_base, uid_range, /* coalesce = */ false);
+                r = uid_range_add_internal(
+                                &range,
+                                IN_SET(mode, UID_RANGE_USERNS_INSIDE, GID_RANGE_USERNS_INSIDE) ? uid_base : uid_shift,
+                                uid_range,
+                                /* coalesce = */ false);
                 if (r < 0)
                         return r;
         }
@@ -253,3 +260,103 @@ int uid_range_load_userns(UIDRange **ret, const char *path) {
         *ret = TAKE_PTR(range);
         return 0;
 }
+
+int uid_range_load_userns_by_fd(int userns_fd, UIDRangeUsernsMode mode, UIDRange **ret) {
+        _cleanup_(close_pairp) int pfd[2] = EBADF_PAIR;
+        _cleanup_(sigkill_waitp) pid_t pid = 0;
+        ssize_t n;
+        char x;
+        int r;
+
+        assert(userns_fd >= 0);
+        assert(mode >= 0);
+        assert(mode < _UID_RANGE_USERNS_MODE_MAX);
+        assert(ret);
+
+        if (pipe2(pfd, O_CLOEXEC) < 0)
+                return -errno;
+
+        r = safe_fork_full(
+                        "(sd-mkuserns)",
+                        /* stdio_fds= */ NULL,
+                        (int[]) { pfd[1], userns_fd }, 2,
+                        FORK_CLOSE_ALL_FDS|FORK_DEATHSIG_SIGKILL,
+                        &pid);
+        if (r < 0)
+                return r;
+        if (r == 0) {
+                /* Child. */
+
+                if (setns(userns_fd, CLONE_NEWUSER) < 0) {
+                        log_debug_errno(errno, "Failed to join userns: %m");
+                        _exit(EXIT_FAILURE);
+                }
+
+                userns_fd = safe_close(userns_fd);
+
+                n = write(pfd[1], &(const char) { 'x' }, 1);
+                if (n < 0) {
+                        log_debug_errno(errno, "Failed to write to fifo: %m");
+                        _exit(EXIT_FAILURE);
+                }
+                assert(n == 1);
+
+                freeze();
+        }
+
+        pfd[1] = safe_close(pfd[1]);
+
+        n = read(pfd[0], &x, 1);
+        if (n < 0)
+                return -errno;
+        if (n == 0)
+                return -EPROTO;
+        assert(n == 1);
+        assert(x == 'x');
+
+        const char *p = procfs_file_alloca(
+                        pid,
+                        IN_SET(mode, UID_RANGE_USERNS_INSIDE, UID_RANGE_USERNS_OUTSIDE) ? "uid_map" : "gid_map");
+
+        return uid_range_load_userns(p, mode, ret);
+}
+
+bool uid_range_overlaps(const UIDRange *range, uid_t start, uid_t nr) {
+
+        if (!range)
+                return false;
+
+        /* Avoid overflow */
+        if (start > UINT32_MAX - nr)
+                nr = UINT32_MAX - start;
+
+        if (nr == 0)
+                return false;
+
+        FOREACH_ARRAY(entry, range->entries, range->n_entries)
+                if (start < entry->start + entry->nr &&
+                    start + nr >= entry->start)
+                        return true;
+
+        return false;
+}
+
+bool uid_range_equal(const UIDRange *a, const UIDRange *b) {
+        if (a == b)
+                return true;
+
+        if (!a || !b)
+                return false;
+
+        if (a->n_entries != b->n_entries)
+                return false;
+
+        for (size_t i = 0; i < a->n_entries; i++) {
+                if (a->entries[i].start != b->entries[i].start)
+                        return false;
+                if (a->entries[i].nr != b->entries[i].nr)
+                        return false;
+        }
+
+        return true;
+}
index 5c4aac3726089b8869f6a59dd785c29e96c9a75a..1f687b2713e1d6cefd4ecc064be83d2eef2ce793 100644 (file)
@@ -33,4 +33,46 @@ static inline bool uid_range_contains(const UIDRange *range, uid_t uid) {
 
 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);
+static inline size_t uid_range_entries(const UIDRange *range) {
+        return range ? range->n_entries : 0;
+}
+
+static inline unsigned uid_range_size(const UIDRange *range) {
+        if (!range)
+                return 0;
+
+        unsigned n = 0;
+
+        FOREACH_ARRAY(e, range->entries, range->n_entries)
+                n += e->nr;
+
+        return n;
+}
+
+static inline bool uid_range_is_empty(const UIDRange *range) {
+
+        if (!range)
+                return true;
+
+        FOREACH_ARRAY(e, range->entries, range->n_entries)
+                if (e->nr > 0)
+                        return false;
+
+        return true;
+}
+
+bool uid_range_equal(const UIDRange *a, const UIDRange *b);
+
+typedef enum UIDRangeUsernsMode {
+        UID_RANGE_USERNS_INSIDE,
+        UID_RANGE_USERNS_OUTSIDE,
+        GID_RANGE_USERNS_INSIDE,
+        GID_RANGE_USERNS_OUTSIDE,
+        _UID_RANGE_USERNS_MODE_MAX,
+        _UID_RANGE_USERNS_MODE_INVALID = -EINVAL,
+} UIDRangeUsernsMode;
+
+int uid_range_load_userns(const char *path, UIDRangeUsernsMode mode, UIDRange **ret);
+int uid_range_load_userns_by_fd(int userns_fd, UIDRangeUsernsMode mode, UIDRange **ret);
+
+bool uid_range_overlaps(const UIDRange *range, uid_t start, uid_t nr);
index 9bc4920998e3c82a98e5e18d7da26c25673da041..d03363bd379b1997b273059a5805212fab39f474 100644 (file)
@@ -99,7 +99,7 @@ static const char* const unit_load_state_table[_UNIT_LOAD_STATE_MAX] = {
         [UNIT_BAD_SETTING] = "bad-setting",
         [UNIT_ERROR]       = "error",
         [UNIT_MERGED]      = "merged",
-        [UNIT_MASKED]      = "masked"
+        [UNIT_MASKED]      = "masked",
 };
 
 DEFINE_STRING_TABLE_LOOKUP(unit_load_state, UnitLoadState);
@@ -155,7 +155,7 @@ static const char* const automount_state_table[_AUTOMOUNT_STATE_MAX] = {
         [AUTOMOUNT_DEAD]    = "dead",
         [AUTOMOUNT_WAITING] = "waiting",
         [AUTOMOUNT_RUNNING] = "running",
-        [AUTOMOUNT_FAILED]  = "failed"
+        [AUTOMOUNT_FAILED]  = "failed",
 };
 
 DEFINE_STRING_TABLE_LOOKUP(automount_state, AutomountState);
@@ -189,7 +189,7 @@ static const char* const path_state_table[_PATH_STATE_MAX] = {
         [PATH_DEAD]    = "dead",
         [PATH_WAITING] = "waiting",
         [PATH_RUNNING] = "running",
-        [PATH_FAILED]  = "failed"
+        [PATH_FAILED]  = "failed",
 };
 
 DEFINE_STRING_TABLE_LOOKUP(path_state, PathState);
@@ -238,7 +238,7 @@ DEFINE_STRING_TABLE_LOOKUP(service_state, ServiceState);
 
 static const char* const slice_state_table[_SLICE_STATE_MAX] = {
         [SLICE_DEAD]   = "dead",
-        [SLICE_ACTIVE] = "active"
+        [SLICE_ACTIVE] = "active",
 };
 
 DEFINE_STRING_TABLE_LOOKUP(slice_state, SliceState);
@@ -278,7 +278,7 @@ DEFINE_STRING_TABLE_LOOKUP(swap_state, SwapState);
 
 static const char* const target_state_table[_TARGET_STATE_MAX] = {
         [TARGET_DEAD]   = "dead",
-        [TARGET_ACTIVE] = "active"
+        [TARGET_ACTIVE] = "active",
 };
 
 DEFINE_STRING_TABLE_LOOKUP(target_state, TargetState);
@@ -288,7 +288,7 @@ static const char* const timer_state_table[_TIMER_STATE_MAX] = {
         [TIMER_WAITING] = "waiting",
         [TIMER_RUNNING] = "running",
         [TIMER_ELAPSED] = "elapsed",
-        [TIMER_FAILED]  = "failed"
+        [TIMER_FAILED]  = "failed",
 };
 
 DEFINE_STRING_TABLE_LOOKUP(timer_state, TimerState);
@@ -333,7 +333,7 @@ static const char* const notify_access_table[_NOTIFY_ACCESS_MAX] = {
         [NOTIFY_NONE] = "none",
         [NOTIFY_MAIN] = "main",
         [NOTIFY_EXEC] = "exec",
-        [NOTIFY_ALL]  = "all"
+        [NOTIFY_ALL]  = "all",
 };
 
 DEFINE_STRING_TABLE_LOOKUP(notify_access, NotifyAccess);
index 36e1e0f1558cd8300acc42eea98240c70b7eab98..36f0dc9cfd33e2861a0498c577ca30f931b8cccd 100644 (file)
@@ -1,26 +1,10 @@
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/* SPDX-License-Identifier: LGPL-2.0-or-later */
 
-/* Parts of this file are based on the GLIB utf8 validation functions. The
- * original license text follows. */
-
-/* gutf8.c - Operations on UTF-8 strings.
+/* Parts of this file are based on the GLIB utf8 validation functions. The original copyright follows.
  *
+ * gutf8.c - Operations on UTF-8 strings.
  * Copyright (C) 1999 Tom Tromey
  * Copyright (C) 2000 Red Hat, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
  */
 
 #include <errno.h>
index 38b2c3ad7cd87ec3375b37270c29c78b393a0581..cf645e2416cecaa7226dcd4a064147dcd88be614 100644 (file)
@@ -7,7 +7,6 @@
 #include "generator.h"
 #include "initrd-util.h"
 #include "log.h"
-#include "mkdir.h"
 #include "special.h"
 #include "string-util.h"
 #include "virt.h"
@@ -17,6 +16,7 @@
  * boot as "good" if we manage to boot up far enough. */
 
 static int run(const char *dest, const char *dest_early, const char *dest_late) {
+        assert(dest_early);
 
         if (in_initrd()) {
                 log_debug("Skipping generator, running in the initrd.");
@@ -34,7 +34,6 @@ static int run(const char *dest, const char *dest_early, const char *dest_late)
         }
 
         if (access(EFIVAR_PATH(EFI_LOADER_VARIABLE(LoaderBootCountPath)), F_OK) < 0) {
-
                 if (errno == ENOENT) {
                         log_debug_errno(errno, "Skipping generator, not booted with boot counting in effect.");
                         return 0;
@@ -45,12 +44,7 @@ static int run(const char *dest, const char *dest_early, const char *dest_late)
 
         /* We pull this in from basic.target so that it ends up in all "regular" boot ups, but not in
          * rescue.target or even emergency.target. */
-        const char *p = strjoina(dest_early, "/" SPECIAL_BASIC_TARGET ".wants/systemd-bless-boot.service");
-        (void) mkdir_parents(p, 0755);
-        if (symlink(SYSTEM_DATA_UNIT_DIR "/systemd-bless-boot.service", p) < 0)
-                return log_error_errno(errno, "Failed to create symlink '%s': %m", p);
-
-        return 0;
+        return generator_add_symlink(dest_early, SPECIAL_BASIC_TARGET, "wants", "systemd-bless-boot.service");
 }
 
 DEFINE_MAIN_GENERATOR_FUNCTION(run);
index 718c44b2a6d3124d3670abfaf8c73f2e09feb252..1a7ae637741daa2b5c34fc6ad7441c1412bd629c 100644 (file)
@@ -128,10 +128,10 @@ EFI_STATUS secure_boot_enroll_at(EFI_FILE *root_dir, const char16_t *path, bool
                 char *buffer;
                 size_t size;
         } sb_vars[] = {
-                { u"db",  u"db.auth",  EFI_IMAGE_SECURITY_DATABASE_GUID, true, NULL, 0 },
-                { u"dbx", u"dbx.auth", EFI_IMAGE_SECURITY_DATABASE_GUID, false, NULL, 0 },
-                { u"KEK", u"KEK.auth", EFI_GLOBAL_VARIABLE, true, NULL, 0 },
-                { u"PK",  u"PK.auth",  EFI_GLOBAL_VARIABLE, true, NULL, 0 },
+                { u"db",  u"db.auth",  EFI_IMAGE_SECURITY_DATABASE_GUID, true  },
+                { u"dbx", u"dbx.auth", EFI_IMAGE_SECURITY_DATABASE_GUID, false },
+                { u"KEK", u"KEK.auth", EFI_GLOBAL_VARIABLE,              true  },
+                { u"PK",  u"PK.auth",  EFI_GLOBAL_VARIABLE,              true  },
         };
 
         /* Make sure all keys files exist before we start enrolling them by loading them from the disk first. */
@@ -174,8 +174,9 @@ EFI_STATUS secure_boot_enroll_at(EFI_FILE *root_dir, const char16_t *path, bool
                         EFI_VARIABLE_RUNTIME_ACCESS |
                         EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS;
 
-                if (sb_vars[i].size == 0)
+                if (!sb_vars[i].buffer)
                         continue;
+
                 err = efivar_set_raw(&sb_vars[i].vendor, sb_vars[i].name, sb_vars[i].buffer, sb_vars[i].size, sb_vars_opts);
                 if (err != EFI_SUCCESS) {
                         log_error_status(err, "Failed to write %ls secure boot variable: %m", sb_vars[i].name);
index 8c5a57588da5f0d12cc236e7255a3981ff9d9de6..6cb9d525c2ee60b83a87951477ad18aaedce56fb 100644 (file)
 #include "unit.h"
 
 static const UnitActiveState state_translation_table[_AUTOMOUNT_STATE_MAX] = {
-        [AUTOMOUNT_DEAD] = UNIT_INACTIVE,
+        [AUTOMOUNT_DEAD]    = UNIT_INACTIVE,
         [AUTOMOUNT_WAITING] = UNIT_ACTIVE,
         [AUTOMOUNT_RUNNING] = UNIT_ACTIVE,
-        [AUTOMOUNT_FAILED] = UNIT_FAILED
+        [AUTOMOUNT_FAILED]  = UNIT_FAILED,
 };
 
 static int open_dev_autofs(Manager *m);
index 3bac231c97502424246253b933c1e75f6f3e67cd..185ed7d07084957bd9a8d4fa949f2895958e0788 100644 (file)
@@ -1,12 +1,13 @@
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 
+/* Make sure the net/if.h header is included before any linux/ one */
+#include <net/if.h>
 #include <arpa/inet.h>
 #include <assert.h>
 #include <errno.h>
 #include <fcntl.h>
 #include <linux/bpf_insn.h>
 #include <net/ethernet.h>
-#include <net/if.h>
 #include <netinet/ip.h>
 #include <netinet/ip6.h>
 #include <stddef.h>
index fdca434df9bfc8779c9a6ca54e8bd91f15914d86..852a16622f1108e689e014755c62de4dd44ec6a7 100644 (file)
@@ -5128,7 +5128,7 @@ int unit_cgroup_freezer_action(Unit *u, FreezerAction action) {
 
         CGroupRuntime *crt = unit_get_cgroup_runtime(u);
         if (!crt || !crt->cgroup_realized)
-                return -EBUSY;
+                return 0; /* No cgroup = nothing running to freeze */
 
         unit_next_freezer_state(u, action, &next, &target);
 
index 6d2247a8401a6235298ac670f8d53be315a0b34a..b3cea73c4342548da5af14e05481a5fcb8879285 100644 (file)
@@ -1306,18 +1306,24 @@ int bus_set_transient_exec_command(
                 sd_bus_message *message,
                 UnitWriteFlags flags,
                 sd_bus_error *error) {
-        bool is_ex_prop = endswith(name, "Ex");
-        unsigned n = 0;
+
+        const char *ex_prop = endswith(ASSERT_PTR(name), "Ex");
+        size_t n = 0;
         int r;
 
+        assert(u);
+        assert(exec_command);
+        assert(message);
+        assert(error);
+
         /* Drop Ex from the written setting. E.g. ExecStart=, not ExecStartEx=. */
-        const char *written_name = is_ex_prop ? strndupa_safe(name, strlen(name) - 2) : name;
+        const char *written_name = ex_prop ? strndupa_safe(name, ex_prop - name) : name;
 
-        r = sd_bus_message_enter_container(message, 'a', is_ex_prop ? "(sasas)" : "(sasb)");
+        r = sd_bus_message_enter_container(message, 'a', ex_prop ? "(sasas)" : "(sasb)");
         if (r < 0)
                 return r;
 
-        while ((r = sd_bus_message_enter_container(message, 'r', is_ex_prop ? "sasas" : "sasb")) > 0) {
+        while ((r = sd_bus_message_enter_container(message, 'r', ex_prop ? "sasas" : "sasb")) > 0) {
                 _cleanup_strv_free_ char **argv = NULL, **ex_opts = NULL;
                 const char *path;
                 int b;
@@ -1339,7 +1345,7 @@ int bus_set_transient_exec_command(
                         return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
                                                  "\"%s\" argv cannot be empty", name);
 
-                r = is_ex_prop ? sd_bus_message_read_strv(message, &ex_opts) : sd_bus_message_read(message, "b", &b);
+                r = ex_prop ? sd_bus_message_read_strv(message, &ex_opts) : sd_bus_message_read(message, "b", &b);
                 if (r < 0)
                         return r;
 
@@ -1348,29 +1354,28 @@ int bus_set_transient_exec_command(
                         return r;
 
                 if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
-                        ExecCommand *c;
+                        _cleanup_(exec_command_freep) ExecCommand *c = NULL;
 
-                        c = new0(ExecCommand, 1);
+                        c = new(ExecCommand, 1);
                         if (!c)
                                 return -ENOMEM;
 
-                        c->path = strdup(path);
-                        if (!c->path) {
-                                free(c);
-                                return -ENOMEM;
-                        }
+                        *c = (ExecCommand) {
+                                .argv = TAKE_PTR(argv),
+                        };
 
-                        c->argv = TAKE_PTR(argv);
+                        r = path_simplify_alloc(path, &c->path);
+                        if (r < 0)
+                                return r;
 
-                        if (is_ex_prop) {
+                        if (ex_prop) {
                                 r = exec_command_flags_from_strv(ex_opts, &c->flags);
                                 if (r < 0)
                                         return r;
-                        } else
-                                c->flags = b ? EXEC_COMMAND_IGNORE_FAILURE : 0;
+                        } else if (b)
+                                c->flags |= EXEC_COMMAND_IGNORE_FAILURE;
 
-                        path_simplify(c->path);
-                        exec_command_append_list(exec_command, c);
+                        exec_command_append_list(exec_command, TAKE_PTR(c));
                 }
 
                 n++;
index 11f93b0c8069958dd8aaec505c132dd6e325f99b..7077d44723bbfe6a2e38547c118dff0f4748a2c5 100644 (file)
@@ -772,6 +772,7 @@ static int method_generic_unit_operation(
 
         assert(message);
         assert(m);
+        assert(handler);
 
         /* Read the first argument from the command and pass the operation to the specified per-unit
          * method. */
@@ -947,9 +948,10 @@ static int method_list_units_by_names(sd_bus_message *message, void *userdata, s
 }
 
 static int method_get_unit_processes(sd_bus_message *message, void *userdata, sd_bus_error *error) {
-        /* Don't load a unit (since it won't have any processes if it's not loaded), but don't insist on the
-         * unit being loaded (because even improperly loaded units might still have processes around */
-        return method_generic_unit_operation(message, userdata, error, bus_unit_method_get_processes, 0);
+        /* Don't load a unit actively (since it won't have any processes if it's not loaded), but don't
+         * insist on the unit being loaded either (because even improperly loaded units might still have
+         * processes around). */
+        return method_generic_unit_operation(message, userdata, error, bus_unit_method_get_processes, /* flags = */ 0);
 }
 
 static int method_attach_processes_to_unit(sd_bus_message *message, void *userdata, sd_bus_error *error) {
@@ -1599,7 +1601,7 @@ static int method_reload(sd_bus_message *message, void *userdata, sd_bus_error *
         log_caller(message, m, "Reloading");
 
         /* Check the rate limit after the authorization succeeds, to avoid denial-of-service issues. */
-        if (!ratelimit_below(&m->reload_ratelimit)) {
+        if (!ratelimit_below(&m->reload_reexec_ratelimit)) {
                 log_warning("Reloading request rejected due to rate limit.");
                 return sd_bus_error_setf(error,
                                          SD_BUS_ERROR_LIMITS_EXCEEDED,
@@ -1644,6 +1646,14 @@ static int method_reexecute(sd_bus_message *message, void *userdata, sd_bus_erro
         /* Write a log message noting the unit or process who requested the Reexecute() */
         log_caller(message, m, "Reexecuting");
 
+        /* Check the rate limit after the authorization succeeds, to avoid denial-of-service issues. */
+        if (!ratelimit_below(&m->reload_reexec_ratelimit)) {
+                log_warning("Reexecuting request rejected due to rate limit.");
+                return sd_bus_error_setf(error,
+                                         SD_BUS_ERROR_LIMITS_EXCEEDED,
+                                         "Reexecute() request rejected due to rate limit.");
+        }
+
         /* We don't send a reply back here, the client should
          * just wait for us disconnecting. */
 
@@ -3042,6 +3052,7 @@ const sd_bus_vtable bus_manager_vtable[] = {
         BUS_PROPERTY_DUAL_TIMESTAMP("InitRDTimestamp", offsetof(Manager, timestamps[MANAGER_TIMESTAMP_INITRD]), SD_BUS_VTABLE_PROPERTY_CONST),
         BUS_PROPERTY_DUAL_TIMESTAMP("UserspaceTimestamp", offsetof(Manager, timestamps[MANAGER_TIMESTAMP_USERSPACE]), SD_BUS_VTABLE_PROPERTY_CONST),
         BUS_PROPERTY_DUAL_TIMESTAMP("FinishTimestamp", offsetof(Manager, timestamps[MANAGER_TIMESTAMP_FINISH]), SD_BUS_VTABLE_PROPERTY_CONST),
+        BUS_PROPERTY_DUAL_TIMESTAMP("SoftRebootStartTimestamp", offsetof(Manager, timestamps[MANAGER_TIMESTAMP_SOFTREBOOT_START]), SD_BUS_VTABLE_PROPERTY_CONST),
         BUS_PROPERTY_DUAL_TIMESTAMP("SecurityStartTimestamp", offsetof(Manager, timestamps[MANAGER_TIMESTAMP_SECURITY_START]), SD_BUS_VTABLE_PROPERTY_CONST),
         BUS_PROPERTY_DUAL_TIMESTAMP("SecurityFinishTimestamp", offsetof(Manager, timestamps[MANAGER_TIMESTAMP_SECURITY_FINISH]), SD_BUS_VTABLE_PROPERTY_CONST),
         BUS_PROPERTY_DUAL_TIMESTAMP("GeneratorsStartTimestamp", offsetof(Manager, timestamps[MANAGER_TIMESTAMP_GENERATORS_START]), SD_BUS_VTABLE_PROPERTY_CONST),
@@ -3139,6 +3150,7 @@ const sd_bus_vtable bus_manager_vtable[] = {
         SD_BUS_PROPERTY("DefaultOOMPolicy", "s", bus_property_get_oom_policy, offsetof(Manager, defaults.oom_policy), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("DefaultOOMScoreAdjust", "i", property_get_oom_score_adjust, 0, SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("CtrlAltDelBurstAction", "s", bus_property_get_emergency_action, offsetof(Manager, cad_burst_action), SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("SoftRebootsCount", "u", bus_property_get_unsigned, offsetof(Manager, soft_reboots_count), SD_BUS_VTABLE_PROPERTY_CONST),
 
         SD_BUS_METHOD_WITH_ARGS("GetUnit",
                                 SD_BUS_ARGS("s", name),
index 2278c3ee2fa4c15f3e3da71e036f67455d1d27b2..f6a9ea97b7790a915215e0d16da3088d47620f90 100644 (file)
@@ -63,7 +63,7 @@ const sd_bus_vtable bus_mount_vtable[] = {
         SD_BUS_VTABLE_START(0),
         SD_BUS_PROPERTY("Where", "s", NULL, offsetof(Mount, where), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("What", "s", property_get_what, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
-        SD_BUS_PROPERTY("Options","s", property_get_options, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
+        SD_BUS_PROPERTY("Options", "s", property_get_options, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
         SD_BUS_PROPERTY("Type", "s", property_get_type, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
         SD_BUS_PROPERTY("TimeoutUSec", "t", bus_property_get_usec, offsetof(Mount, timeout_usec), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("ControlPID", "u", bus_property_get_pid, offsetof(Mount, control_pid.pid), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
index 93cab92b8ce0669e8c4e20301e86a1e49f3617d3..d8567676a75fa00fdd0e59a6f45336762adabf95 100644 (file)
@@ -1051,6 +1051,9 @@ static void device_enumerate(Manager *m) {
                 _cleanup_set_free_ Set *ready_units = NULL, *not_ready_units = NULL;
                 Device *d;
 
+                if (device_is_processed(dev) <= 0)
+                        continue;
+
                 if (device_setup_units(m, dev, &ready_units, &not_ready_units) < 0)
                         continue;
 
index 24ed889b61ef5fa1bb0795ee0dec50e42a0df6f8..11de2ba82b1e95f71712cbee88d39855522620e0 100644 (file)
@@ -336,8 +336,10 @@ static int dynamic_user_pop(DynamicUser *d, uid_t *ret_uid, int *ret_lock_fd) {
          * the lock on the socket taken. */
 
         k = receive_one_fd_iov(d->storage_socket[0], &iov, 1, MSG_DONTWAIT, &lock_fd);
-        if (k < 0)
+        if (k < 0) {
+                assert(errno_is_valid(-k));
                 return (int) k;
+        }
 
         *ret_uid = uid;
         *ret_lock_fd = lock_fd;
index e2cd931671714bda84f0e5312d9f1e1db2fb24c2..308332096666eb27bc4badb6076009db6aceb213 100644 (file)
 #include "virt.h"
 
 static const char* const emergency_action_table[_EMERGENCY_ACTION_MAX] = {
-        [EMERGENCY_ACTION_NONE] =               "none",
-        [EMERGENCY_ACTION_REBOOT] =             "reboot",
-        [EMERGENCY_ACTION_REBOOT_FORCE] =       "reboot-force",
-        [EMERGENCY_ACTION_REBOOT_IMMEDIATE] =   "reboot-immediate",
-        [EMERGENCY_ACTION_POWEROFF] =           "poweroff",
-        [EMERGENCY_ACTION_POWEROFF_FORCE] =     "poweroff-force",
+        [EMERGENCY_ACTION_NONE]               = "none",
+        [EMERGENCY_ACTION_REBOOT]             = "reboot",
+        [EMERGENCY_ACTION_REBOOT_FORCE]       = "reboot-force",
+        [EMERGENCY_ACTION_REBOOT_IMMEDIATE]   = "reboot-immediate",
+        [EMERGENCY_ACTION_POWEROFF]           = "poweroff",
+        [EMERGENCY_ACTION_POWEROFF_FORCE]     = "poweroff-force",
         [EMERGENCY_ACTION_POWEROFF_IMMEDIATE] = "poweroff-immediate",
-        [EMERGENCY_ACTION_EXIT] =               "exit",
-        [EMERGENCY_ACTION_EXIT_FORCE] =         "exit-force",
-        [EMERGENCY_ACTION_SOFT_REBOOT] =        "soft-reboot",
-        [EMERGENCY_ACTION_SOFT_REBOOT_FORCE]  "soft-reboot-force",
-        [EMERGENCY_ACTION_KEXEC] =              "kexec",
-        [EMERGENCY_ACTION_KEXEC_FORCE] =        "kexec-force",
-        [EMERGENCY_ACTION_HALT] =               "halt",
-        [EMERGENCY_ACTION_HALT_FORCE] =         "halt-force",
-        [EMERGENCY_ACTION_HALT_IMMEDIATE] =     "halt-immediate",
+        [EMERGENCY_ACTION_EXIT]               = "exit",
+        [EMERGENCY_ACTION_EXIT_FORCE]         = "exit-force",
+        [EMERGENCY_ACTION_SOFT_REBOOT]        = "soft-reboot",
+        [EMERGENCY_ACTION_SOFT_REBOOT_FORCE]  = "soft-reboot-force",
+        [EMERGENCY_ACTION_KEXEC]              = "kexec",
+        [EMERGENCY_ACTION_KEXEC_FORCE]        = "kexec-force",
+        [EMERGENCY_ACTION_HALT]               = "halt",
+        [EMERGENCY_ACTION_HALT_FORCE]         = "halt-force",
+        [EMERGENCY_ACTION_HALT_IMMEDIATE]     = "halt-immediate",
 };
 
 static void log_and_status(Manager *m, bool warn, const char *message, const char *reason) {
index 47339bddafc220cde91f5f79e672e2fbc66974cd..90e11d0d1e9c1fc419e53940b6c807013c677648 100644 (file)
@@ -1771,10 +1771,10 @@ static const char *exec_directory_env_name_to_string(ExecDirectoryType t);
 /* And this table also maps ExecDirectoryType, to the environment variable we pass the selected directory to
  * the service payload in. */
 static const char* const exec_directory_env_name_table[_EXEC_DIRECTORY_TYPE_MAX] = {
-        [EXEC_DIRECTORY_RUNTIME] = "RUNTIME_DIRECTORY",
-        [EXEC_DIRECTORY_STATE] = "STATE_DIRECTORY",
-        [EXEC_DIRECTORY_CACHE] = "CACHE_DIRECTORY",
-        [EXEC_DIRECTORY_LOGS] = "LOGS_DIRECTORY",
+        [EXEC_DIRECTORY_RUNTIME]       = "RUNTIME_DIRECTORY",
+        [EXEC_DIRECTORY_STATE]         = "STATE_DIRECTORY",
+        [EXEC_DIRECTORY_CACHE]         = "CACHE_DIRECTORY",
+        [EXEC_DIRECTORY_LOGS]          = "LOGS_DIRECTORY",
         [EXEC_DIRECTORY_CONFIGURATION] = "CONFIGURATION_DIRECTORY",
 };
 
@@ -2269,10 +2269,10 @@ static int setup_exec_directory(
                 int *exit_status) {
 
         static const int exit_status_table[_EXEC_DIRECTORY_TYPE_MAX] = {
-                [EXEC_DIRECTORY_RUNTIME] = EXIT_RUNTIME_DIRECTORY,
-                [EXEC_DIRECTORY_STATE] = EXIT_STATE_DIRECTORY,
-                [EXEC_DIRECTORY_CACHE] = EXIT_CACHE_DIRECTORY,
-                [EXEC_DIRECTORY_LOGS] = EXIT_LOGS_DIRECTORY,
+                [EXEC_DIRECTORY_RUNTIME]       = EXIT_RUNTIME_DIRECTORY,
+                [EXEC_DIRECTORY_STATE]         = EXIT_STATE_DIRECTORY,
+                [EXEC_DIRECTORY_CACHE]         = EXIT_CACHE_DIRECTORY,
+                [EXEC_DIRECTORY_LOGS]          = EXIT_LOGS_DIRECTORY,
                 [EXEC_DIRECTORY_CONFIGURATION] = EXIT_CONFIGURATION_DIRECTORY,
         };
         int r;
@@ -3699,11 +3699,12 @@ static int add_shifted_fd(int *fds, size_t fds_size, size_t *n_fds, int *fd) {
 }
 
 static int connect_unix_harder(const ExecContext *c, const ExecParameters *p, const OpenFile *of, int ofd) {
+        static const int socket_types[] = { SOCK_DGRAM, SOCK_STREAM, SOCK_SEQPACKET };
+
         union sockaddr_union addr = {
                 .un.sun_family = AF_UNIX,
         };
         socklen_t sa_len;
-        static const int socket_types[] = { SOCK_DGRAM, SOCK_STREAM, SOCK_SEQPACKET };
         int r;
 
         assert(c);
@@ -3713,43 +3714,35 @@ static int connect_unix_harder(const ExecContext *c, const ExecParameters *p, co
 
         r = sockaddr_un_set_path(&addr.un, FORMAT_PROC_FD_PATH(ofd));
         if (r < 0)
-                return log_exec_error_errno(c, p, r, "Failed to set sockaddr for %s: %m", of->path);
-
+                return log_exec_error_errno(c, p, r, "Failed to set sockaddr for '%s': %m", of->path);
         sa_len = r;
 
-        for (size_t i = 0; i < ELEMENTSOF(socket_types); i++) {
+        FOREACH_ARRAY(i, socket_types, ELEMENTSOF(socket_types)) {
                 _cleanup_close_ int fd = -EBADF;
 
-                fd = socket(AF_UNIX, socket_types[i] | SOCK_CLOEXEC, 0);
+                fd = socket(AF_UNIX, *i|SOCK_CLOEXEC, 0);
                 if (fd < 0)
-                        return log_exec_error_errno(c,
-                                                    p,
-                                                    errno,
-                                                    "Failed to create socket for %s: %m",
+                        return log_exec_error_errno(c, p,
+                                                    errno, "Failed to create socket for '%s': %m",
                                                     of->path);
 
                 r = RET_NERRNO(connect(fd, &addr.sa, sa_len));
-                if (r == -EPROTOTYPE)
-                        continue;
-                if (r < 0)
-                        return log_exec_error_errno(c,
-                                                    p,
-                                                    r,
-                                                    "Failed to connect socket for %s: %m",
+                if (r >= 0)
+                        return TAKE_FD(fd);
+                if (r != -EPROTOTYPE)
+                        return log_exec_error_errno(c, p,
+                                                    r, "Failed to connect to socket for '%s': %m",
                                                     of->path);
-
-                return TAKE_FD(fd);
         }
 
-        return log_exec_error_errno(c,
-                                    p,
-                                    SYNTHETIC_ERRNO(EPROTOTYPE), "Failed to connect socket for \"%s\".",
+        return log_exec_error_errno(c, p,
+                                    SYNTHETIC_ERRNO(EPROTOTYPE), "No suitable socket type to connect to socket '%s'.",
                                     of->path);
 }
 
 static int get_open_file_fd(const ExecContext *c, const ExecParameters *p, const OpenFile *of) {
-        struct stat st;
         _cleanup_close_ int fd = -EBADF, ofd = -EBADF;
+        struct stat st;
 
         assert(c);
         assert(p);
@@ -3757,10 +3750,10 @@ static int get_open_file_fd(const ExecContext *c, const ExecParameters *p, const
 
         ofd = open(of->path, O_PATH | O_CLOEXEC);
         if (ofd < 0)
-                return log_exec_error_errno(c, p, errno, "Could not open \"%s\": %m", of->path);
+                return log_exec_error_errno(c, p, errno, "Failed to open '%s' as O_PATH: %m", of->path);
 
         if (fstat(ofd, &st) < 0)
-                return log_exec_error_errno(c, p, errno, "Failed to stat %s: %m", of->path);
+                return log_exec_error_errno(c, p, errno, "Failed to stat '%s': %m", of->path);
 
         if (S_ISSOCK(st.st_mode)) {
                 fd = connect_unix_harder(c, p, of, ofd);
@@ -3768,10 +3761,11 @@ static int get_open_file_fd(const ExecContext *c, const ExecParameters *p, const
                         return fd;
 
                 if (FLAGS_SET(of->flags, OPENFILE_READ_ONLY) && shutdown(fd, SHUT_WR) < 0)
-                        return log_exec_error_errno(c, p, errno, "Failed to shutdown send for socket %s: %m",
+                        return log_exec_error_errno(c, p,
+                                                    errno, "Failed to shutdown send for socket '%s': %m",
                                                     of->path);
 
-                log_exec_debug(c, p, "socket %s opened (fd=%d)", of->path, fd);
+                log_exec_debug(c, p, "Opened socket '%s' as fd %d.", of->path, fd);
         } else {
                 int flags = FLAGS_SET(of->flags, OPENFILE_READ_ONLY) ? O_RDONLY : O_RDWR;
                 if (FLAGS_SET(of->flags, OPENFILE_APPEND))
@@ -3781,9 +3775,9 @@ static int get_open_file_fd(const ExecContext *c, const ExecParameters *p, const
 
                 fd = fd_reopen(ofd, flags | O_CLOEXEC);
                 if (fd < 0)
-                        return log_exec_error_errno(c, p, fd, "Failed to open file %s: %m", of->path);
+                        return log_exec_error_errno(c, p, fd, "Failed to reopen file '%s': %m", of->path);
 
-                log_exec_debug(c, p, "file %s opened (fd=%d)", of->path, fd);
+                log_exec_debug(c, p, "Opened file '%s' as fd %d.", of->path, fd);
         }
 
         return TAKE_FD(fd);
@@ -3802,7 +3796,9 @@ static int collect_open_file_fds(const ExecContext *c, ExecParameters *p, size_t
                 fd = get_open_file_fd(c, p, of);
                 if (fd < 0) {
                         if (FLAGS_SET(of->flags, OPENFILE_GRACEFUL)) {
-                                log_exec_debug_errno(c, p, fd, "Failed to get OpenFile= file descriptor for %s, ignoring: %m", of->path);
+                                log_exec_warning_errno(c, p, fd,
+                                                       "Failed to get OpenFile= file descriptor for '%s', ignoring: %m",
+                                                       of->path);
                                 continue;
                         }
 
@@ -3816,9 +3812,7 @@ static int collect_open_file_fds(const ExecContext *c, ExecParameters *p, size_t
                 if (r < 0)
                         return r;
 
-                p->fds[*n_fds] = TAKE_FD(fd);
-
-                (*n_fds)++;
+                p->fds[(*n_fds)++] = TAKE_FD(fd);
         }
 
         return 0;
@@ -4270,9 +4264,10 @@ int exec_invoke(
                 r = cg_attach_everywhere(params->cgroup_supported, p, 0, NULL, NULL);
                 if (r == -EUCLEAN) {
                         *exit_status = EXIT_CGROUP;
-                        return log_exec_error_errno(context, params, r, "Failed to attach process to cgroup %s "
+                        return log_exec_error_errno(context, params, r,
+                                                    "Failed to attach process to cgroup '%s', "
                                                     "because the cgroup or one of its parents or "
-                                                    "siblings is in the threaded mode: %m", p);
+                                                    "siblings is in the threaded mode.", p);
                 }
                 if (r < 0) {
                         *exit_status = EXIT_CGROUP;
@@ -4302,13 +4297,20 @@ int exec_invoke(
                 return log_exec_error_errno(context, params, r, "Failed to set up standard input: %m");
         }
 
-        r = setup_output(context, params, STDOUT_FILENO, socket_fd, named_iofds, basename(command->path), uid, gid, &journal_stream_dev, &journal_stream_ino);
+        _cleanup_free_ char *fname = NULL;
+        r = path_extract_filename(command->path, &fname);
+        if (r < 0) {
+                *exit_status = EXIT_STDOUT;
+                return log_exec_error_errno(context, params, r, "Failed to extract filename from path %s: %m", command->path);
+        }
+
+        r = setup_output(context, params, STDOUT_FILENO, socket_fd, named_iofds, fname, uid, gid, &journal_stream_dev, &journal_stream_ino);
         if (r < 0) {
                 *exit_status = EXIT_STDOUT;
                 return log_exec_error_errno(context, params, r, "Failed to set up standard output: %m");
         }
 
-        r = setup_output(context, params, STDERR_FILENO, socket_fd, named_iofds, basename(command->path), uid, gid, &journal_stream_dev, &journal_stream_ino);
+        r = setup_output(context, params, STDERR_FILENO, socket_fd, named_iofds, fname, uid, gid, &journal_stream_dev, &journal_stream_ino);
         if (r < 0) {
                 *exit_status = EXIT_STDERR;
                 return log_exec_error_errno(context, params, r, "Failed to set up standard error output: %m");
index 1ae77f39fb96b4464af1ea536043f5e3dc43ff39..9e402e5e697b2c820da781b46a1ff72c645df92c 100644 (file)
@@ -2544,14 +2544,14 @@ static int exec_context_serialize(const ExecContext *c, FILE *f) {
                 if (base64mem(sc->data, sc->size, &data) < 0)
                         return log_oom_debug();
 
-                r = serialize_item_format(f, "exec-context-set-credentials", "%s %s %s", sc->id, yes_no(sc->encrypted), data);
+                r = serialize_item_format(f, "exec-context-set-credentials", "%s %s %s", sc->id, data, yes_no(sc->encrypted));
                 if (r < 0)
                         return r;
         }
 
         ExecLoadCredential *lc;
         HASHMAP_FOREACH(lc, c->load_credentials) {
-                r = serialize_item_format(f, "exec-context-load-credentials", "%s %s %s", lc->id, yes_no(lc->encrypted), lc->path);
+                r = serialize_item_format(f, "exec-context-load-credentials", "%s %s %s", lc->id, lc->path, yes_no(lc->encrypted));
                 if (r < 0)
                         return r;
         }
@@ -3668,7 +3668,7 @@ static int exec_context_deserialize(ExecContext *c, FILE *f) {
                         _cleanup_(exec_set_credential_freep) ExecSetCredential *sc = NULL;
                         _cleanup_free_ char *id = NULL, *encrypted = NULL, *data = NULL;
 
-                        r = extract_many_words(&val, " ", 0, &id, &encrypted, &data);
+                        r = extract_many_words(&val, " ", EXTRACT_DONT_COALESCE_SEPARATORS, &id, &data, &encrypted);
                         if (r < 0)
                                 return r;
                         if (r != 3)
@@ -3700,7 +3700,7 @@ static int exec_context_deserialize(ExecContext *c, FILE *f) {
                         _cleanup_(exec_load_credential_freep) ExecLoadCredential *lc = NULL;
                         _cleanup_free_ char *id = NULL, *encrypted = NULL, *path = NULL;
 
-                        r = extract_many_words(&val, " ", 0, &id, &encrypted, &path);
+                        r = extract_many_words(&val, " ", EXTRACT_DONT_COALESCE_SEPARATORS, &id, &path, &encrypted);
                         if (r < 0)
                                 return r;
                         if (r != 3)
index 41b3b3f2b57e7347439c169a848c3d63cc547f38..5a4acd0775621e7c1bc91d59cc2d8280c7369675 100644 (file)
@@ -456,6 +456,11 @@ int exec_spawn(Unit *unit,
                         environ,
                         cg_unified() > 0 ? subcgroup_path : NULL,
                         &pidref);
+        if (r == -EUCLEAN && subcgroup_path)
+                return log_unit_error_errno(unit, r,
+                                            "Failed to spawn process into cgroup '%s', because the cgroup "
+                                            "or one of its parents or siblings is in the threaded mode.",
+                                            subcgroup_path);
         if (r < 0)
                 return log_unit_error_errno(unit, r, "Failed to spawn executor: %m");
         /* We add the new process to the cgroup both in the child (so that we can be sure that no user code is ever
@@ -672,13 +677,19 @@ void exec_command_done_array(ExecCommand *c, size_t n) {
                 exec_command_done(i);
 }
 
+ExecCommand* exec_command_free(ExecCommand *c) {
+        if (!c)
+                return NULL;
+
+        exec_command_done(c);
+        return mfree(c);
+}
+
 ExecCommand* exec_command_free_list(ExecCommand *c) {
         ExecCommand *i;
 
-        while ((i = LIST_POP(command, c))) {
-                exec_command_done(i);
-                free(i);
-        }
+        while ((i = LIST_POP(command, c)))
+                exec_command_free(i);
 
         return NULL;
 }
@@ -2659,46 +2670,46 @@ ExecCleanMask exec_clean_mask_from_string(const char *s) {
 }
 
 static const char* const exec_input_table[_EXEC_INPUT_MAX] = {
-        [EXEC_INPUT_NULL] = "null",
-        [EXEC_INPUT_TTY] = "tty",
+        [EXEC_INPUT_NULL]      = "null",
+        [EXEC_INPUT_TTY]       = "tty",
         [EXEC_INPUT_TTY_FORCE] = "tty-force",
-        [EXEC_INPUT_TTY_FAIL] = "tty-fail",
-        [EXEC_INPUT_SOCKET] = "socket",
-        [EXEC_INPUT_NAMED_FD] = "fd",
-        [EXEC_INPUT_DATA] = "data",
-        [EXEC_INPUT_FILE] = "file",
+        [EXEC_INPUT_TTY_FAIL]  = "tty-fail",
+        [EXEC_INPUT_SOCKET]    = "socket",
+        [EXEC_INPUT_NAMED_FD]  = "fd",
+        [EXEC_INPUT_DATA]      = "data",
+        [EXEC_INPUT_FILE]      = "file",
 };
 
 DEFINE_STRING_TABLE_LOOKUP(exec_input, ExecInput);
 
 static const char* const exec_output_table[_EXEC_OUTPUT_MAX] = {
-        [EXEC_OUTPUT_INHERIT] = "inherit",
-        [EXEC_OUTPUT_NULL] = "null",
-        [EXEC_OUTPUT_TTY] = "tty",
-        [EXEC_OUTPUT_KMSG] = "kmsg",
-        [EXEC_OUTPUT_KMSG_AND_CONSOLE] = "kmsg+console",
-        [EXEC_OUTPUT_JOURNAL] = "journal",
+        [EXEC_OUTPUT_INHERIT]             = "inherit",
+        [EXEC_OUTPUT_NULL]                = "null",
+        [EXEC_OUTPUT_TTY]                 = "tty",
+        [EXEC_OUTPUT_KMSG]                = "kmsg",
+        [EXEC_OUTPUT_KMSG_AND_CONSOLE]    = "kmsg+console",
+        [EXEC_OUTPUT_JOURNAL]             = "journal",
         [EXEC_OUTPUT_JOURNAL_AND_CONSOLE] = "journal+console",
-        [EXEC_OUTPUT_SOCKET] = "socket",
-        [EXEC_OUTPUT_NAMED_FD] = "fd",
-        [EXEC_OUTPUT_FILE] = "file",
-        [EXEC_OUTPUT_FILE_APPEND] = "append",
-        [EXEC_OUTPUT_FILE_TRUNCATE] = "truncate",
+        [EXEC_OUTPUT_SOCKET]              = "socket",
+        [EXEC_OUTPUT_NAMED_FD]            = "fd",
+        [EXEC_OUTPUT_FILE]                = "file",
+        [EXEC_OUTPUT_FILE_APPEND]         = "append",
+        [EXEC_OUTPUT_FILE_TRUNCATE]       = "truncate",
 };
 
 DEFINE_STRING_TABLE_LOOKUP(exec_output, ExecOutput);
 
 static const char* const exec_utmp_mode_table[_EXEC_UTMP_MODE_MAX] = {
-        [EXEC_UTMP_INIT] = "init",
+        [EXEC_UTMP_INIT]  = "init",
         [EXEC_UTMP_LOGIN] = "login",
-        [EXEC_UTMP_USER] = "user",
+        [EXEC_UTMP_USER]  = "user",
 };
 
 DEFINE_STRING_TABLE_LOOKUP(exec_utmp_mode, ExecUtmpMode);
 
 static const char* const exec_preserve_mode_table[_EXEC_PRESERVE_MODE_MAX] = {
-        [EXEC_PRESERVE_NO] = "no",
-        [EXEC_PRESERVE_YES] = "yes",
+        [EXEC_PRESERVE_NO]      = "no",
+        [EXEC_PRESERVE_YES]     = "yes",
         [EXEC_PRESERVE_RESTART] = "restart",
 };
 
@@ -2706,10 +2717,10 @@ DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(exec_preserve_mode, ExecPreserveMode, EX
 
 /* This table maps ExecDirectoryType to the setting it is configured with in the unit */
 static const char* const exec_directory_type_table[_EXEC_DIRECTORY_TYPE_MAX] = {
-        [EXEC_DIRECTORY_RUNTIME] = "RuntimeDirectory",
-        [EXEC_DIRECTORY_STATE] = "StateDirectory",
-        [EXEC_DIRECTORY_CACHE] = "CacheDirectory",
-        [EXEC_DIRECTORY_LOGS] = "LogsDirectory",
+        [EXEC_DIRECTORY_RUNTIME]       = "RuntimeDirectory",
+        [EXEC_DIRECTORY_STATE]         = "StateDirectory",
+        [EXEC_DIRECTORY_CACHE]         = "CacheDirectory",
+        [EXEC_DIRECTORY_LOGS]          = "LogsDirectory",
         [EXEC_DIRECTORY_CONFIGURATION] = "ConfigurationDirectory",
 };
 
@@ -2740,10 +2751,10 @@ DEFINE_STRING_TABLE_LOOKUP(exec_directory_type_mode, ExecDirectoryType);
  * one is supposed to be generic enough to be used for unit types that don't use ExecContext and per-unit
  * directories, specifically .timer units with their timestamp touch file. */
 static const char* const exec_resource_type_table[_EXEC_DIRECTORY_TYPE_MAX] = {
-        [EXEC_DIRECTORY_RUNTIME] = "runtime",
-        [EXEC_DIRECTORY_STATE] = "state",
-        [EXEC_DIRECTORY_CACHE] = "cache",
-        [EXEC_DIRECTORY_LOGS] = "logs",
+        [EXEC_DIRECTORY_RUNTIME]       = "runtime",
+        [EXEC_DIRECTORY_STATE]         = "state",
+        [EXEC_DIRECTORY_CACHE]         = "cache",
+        [EXEC_DIRECTORY_LOGS]          = "logs",
         [EXEC_DIRECTORY_CONFIGURATION] = "configuration",
 };
 
@@ -2752,7 +2763,7 @@ DEFINE_STRING_TABLE_LOOKUP(exec_resource_type, ExecDirectoryType);
 static const char* const exec_keyring_mode_table[_EXEC_KEYRING_MODE_MAX] = {
         [EXEC_KEYRING_INHERIT] = "inherit",
         [EXEC_KEYRING_PRIVATE] = "private",
-        [EXEC_KEYRING_SHARED] = "shared",
+        [EXEC_KEYRING_SHARED]  = "shared",
 };
 
 DEFINE_STRING_TABLE_LOOKUP(exec_keyring_mode, ExecKeyringMode);
index 21dddedffef7b8e48f4f2b37858981c947487a34..c41f9cbec2fc89a483eee9e6c3408ca05ba54b58 100644 (file)
@@ -487,10 +487,13 @@ int exec_spawn(Unit *unit,
 
 void exec_command_done(ExecCommand *c);
 void exec_command_done_array(ExecCommand *c, size_t n);
+ExecCommand* exec_command_free(ExecCommand *c);
+DEFINE_TRIVIAL_CLEANUP_FUNC(ExecCommand*, exec_command_free);
 ExecCommand* exec_command_free_list(ExecCommand *c);
 void exec_command_free_array(ExecCommand **c, size_t n);
 void exec_command_reset_status_array(ExecCommand *c, size_t n);
 void exec_command_reset_status_list_array(ExecCommand **c, size_t n);
+
 void exec_command_dump_list(ExecCommand *c, FILE *f, const char *prefix);
 void exec_command_append_list(ExecCommand **l, ExecCommand *e);
 int exec_command_set(ExecCommand *c, const char *path, ...) _sentinel_;
index f4801318dea9d4f6d9a72e78a8d390707661f4d0..3f8f7e3bca581af6b5500dc5c08cf55068a86215 100644 (file)
@@ -9,28 +9,13 @@
 #include "fileio.h"
 #include "kmod-setup.h"
 #include "macro.h"
+#include "module-util.h"
 #include "recurse-dir.h"
 #include "string-util.h"
 #include "strv.h"
 #include "virt.h"
 
 #if HAVE_KMOD
-#include "module-util.h"
-
-static void systemd_kmod_log(
-                void *data,
-                int priority,
-                const char *file, int line,
-                const char *fn,
-                const char *format,
-                va_list args) {
-
-        /* library logging is enabled at debug only */
-        DISABLE_WARNING_FORMAT_NONLITERAL;
-        log_internalv(LOG_DEBUG, 0, file, line, fn, format, args);
-        REENABLE_WARNING;
-}
-
 static int match_modalias_recurse_dir_cb(
                 RecurseDirEvent event,
                 const char *path,
@@ -166,11 +151,12 @@ int kmod_setup(void) {
 #endif
         };
 
+        int r;
+
         if (have_effective_cap(CAP_SYS_MODULE) <= 0)
                 return 0;
 
-        _cleanup_(kmod_unrefp) struct kmod_ctx *ctx = NULL;
-
+        _cleanup_(sym_kmod_unrefp) struct kmod_ctx *ctx = NULL;
         FOREACH_ARRAY(kmod, kmod_table, ELEMENTSOF(kmod_table)) {
                 if (kmod->path && access(kmod->path, F_OK) >= 0)
                         continue;
@@ -184,12 +170,9 @@ int kmod_setup(void) {
                                   "this by loading the module...", kmod->module);
 
                 if (!ctx) {
-                        ctx = kmod_new(NULL, NULL);
-                        if (!ctx)
-                                return log_oom();
-
-                        kmod_set_log_fn(ctx, systemd_kmod_log, NULL);
-                        kmod_load_resources(ctx);
+                        r = module_setup_context(&ctx);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to initialize kmod context: %m");
                 }
 
                 (void) module_load_and_warn(ctx, kmod->module, kmod->warn_if_unavailable);
index be8907f2d192c3e565e4ff333688a4e84f956eea..188e5467136cf2ac83c90984432785e5b85ea725 100644 (file)
@@ -856,9 +856,7 @@ int config_parse_exec(
                 void *userdata) {
 
         ExecCommand **e = ASSERT_PTR(data);
-        const Unit *u = userdata;
-        const char *p;
-        bool semicolon;
+        const Unit *u = ASSERT_PTR(userdata);
         int r;
 
         assert(filename);
@@ -873,15 +871,11 @@ int config_parse_exec(
                 return 0;
         }
 
-        p = rvalue;
+        const char *p = rvalue;
+        bool semicolon;
+
         do {
                 _cleanup_free_ char *path = NULL, *firstword = NULL;
-                ExecCommandFlags flags = 0;
-                bool ignore = false, separate_argv0 = false;
-                _cleanup_free_ ExecCommand *nce = NULL;
-                _cleanup_strv_free_ char **n = NULL;
-                size_t nlen = 0;
-                const char *f;
 
                 semicolon = false;
 
@@ -895,25 +889,30 @@ int config_parse_exec(
                         continue;
                 }
 
-                f = firstword;
-                for (;;) {
-                        /* We accept an absolute path as first argument.  If it's prefixed with - and the path doesn't
-                         * exist, we ignore it instead of erroring out; if it's prefixed with @, we allow overriding of
-                         * argv[0]; if it's prefixed with :, we will not do environment variable substitution;
-                         * if it's prefixed with +, it will be run with full privileges and no sandboxing; if
-                         * it's prefixed with '!' we apply sandboxing, but do not change user/group credentials; if
-                         * it's prefixed with '!!', then we apply user/group credentials if the kernel supports ambient
-                         * capabilities -- if it doesn't we don't apply the credentials themselves, but do apply most
-                         * other sandboxing, with some special exceptions for changing UID.
+                const char *f = firstword;
+                bool ignore, separate_argv0 = false;
+                ExecCommandFlags flags = 0;
+
+                for (;; f++) {
+                        /* We accept an absolute path as first argument. Valid prefixes and their effect:
+                         *
+                         * "-":  Ignore if the path doesn't exist
+                         * "@":  Allow overriding argv[0] (supplied as a separate argument)
+                         * ":":  Disable environment variable substitution
+                         * "+":  Run with full privileges and no sandboxing
+                         * "!":  Apply sandboxing except for user/group credentials
+                         * "!!": Apply user/group credentials if the kernel supports ambient capabilities -
+                         *       if it doesn't we don't apply the credentials themselves, but do apply
+                         *       most other sandboxing, with some special exceptions for changing UID.
                          *
-                         * The idea is that '!!' may be used to write services that can take benefit of systemd's
-                         * UID/GID dropping if the kernel supports ambient creds, but provide an automatic fallback to
-                         * privilege dropping within the daemon if the kernel does not offer that. */
+                         * The idea is that '!!' may be used to write services that can take benefit of
+                         * systemd's UID/GID dropping if the kernel supports ambient creds, but provide
+                         * an automatic fallback to privilege dropping within the daemon if the kernel
+                         * does not offer that. */
 
-                        if (*f == '-' && !(flags & EXEC_COMMAND_IGNORE_FAILURE)) {
+                        if (*f == '-' && !(flags & EXEC_COMMAND_IGNORE_FAILURE))
                                 flags |= EXEC_COMMAND_IGNORE_FAILURE;
-                                ignore = true;
-                        } else if (*f == '@' && !separate_argv0)
+                        else if (*f == '@' && !separate_argv0)
                                 separate_argv0 = true;
                         else if (*f == ':' && !(flags & EXEC_COMMAND_NO_ENV_EXPAND))
                                 flags |= EXEC_COMMAND_NO_ENV_EXPAND;
@@ -926,9 +925,10 @@ int config_parse_exec(
                                 flags |= EXEC_COMMAND_AMBIENT_MAGIC;
                         } else
                                 break;
-                        f++;
                 }
 
+                ignore = FLAGS_SET(flags, EXEC_COMMAND_IGNORE_FAILURE);
+
                 r = unit_path_printf(u, f, &path);
                 if (r < 0) {
                         log_syntax(unit, ignore ? LOG_WARNING : LOG_ERR, filename, line, r,
@@ -938,19 +938,18 @@ int config_parse_exec(
                 }
 
                 if (isempty(path)) {
-                        /* First word is either "-" or "@" with no command. */
                         log_syntax(unit, ignore ? LOG_WARNING : LOG_ERR, filename, line, 0,
-                                   "Empty path in command line%s: '%s'",
+                                   "Empty path in command line%s: %s",
                                    ignore ? ", ignoring" : "", rvalue);
                         return ignore ? 0 : -ENOEXEC;
                 }
                 if (!string_is_safe(path)) {
                         log_syntax(unit, ignore ? LOG_WARNING : LOG_ERR, filename, line, 0,
-                                   "Executable name contains special characters%s: %s",
+                                   "Executable path contains special characters%s: %s",
                                    ignore ? ", ignoring" : "", path);
                         return ignore ? 0 : -ENOEXEC;
                 }
-                if (endswith(path, "/")) {
+                if (path_implies_directory(path)) {
                         log_syntax(unit, ignore ? LOG_WARNING : LOG_ERR, filename, line, 0,
                                    "Executable path specifies a directory%s: %s",
                                    ignore ? ", ignoring" : "", path);
@@ -964,92 +963,71 @@ int config_parse_exec(
                         return ignore ? 0 : -ENOEXEC;
                 }
 
-                if (!separate_argv0) {
-                        char *w = NULL;
+                _cleanup_strv_free_ char **args = NULL;
 
-                        if (!GREEDY_REALLOC0(n, nlen + 2))
+                if (!separate_argv0)
+                        if (strv_extend(&args, path) < 0)
                                 return log_oom();
 
-                        w = strdup(path);
-                        if (!w)
-                                return log_oom();
-                        n[nlen++] = w;
-                        n[nlen] = NULL;
-                }
-
-                path_simplify(path);
-
                 while (!isempty(p)) {
                         _cleanup_free_ char *word = NULL, *resolved = NULL;
 
-                        /* Check explicitly for an unquoted semicolon as
-                         * command separator token.  */
+                        /* Check explicitly for an unquoted semicolon as command separator token. */
                         if (p[0] == ';' && (!p[1] || strchr(WHITESPACE, p[1]))) {
                                 p++;
-                                p += strspn(p, WHITESPACE);
+                                p = skip_leading_chars(p, /* bad = */ NULL);
                                 semicolon = true;
                                 break;
                         }
 
                         /* Check for \; explicitly, to not confuse it with \\; or "\;" or "\\;" etc.
-                         * extract_first_word() would return the same for all of those.  */
+                         * extract_first_word() would return the same for all of those. */
                         if (p[0] == '\\' && p[1] == ';' && (!p[2] || strchr(WHITESPACE, p[2]))) {
-                                char *w;
-
                                 p += 2;
-                                p += strspn(p, WHITESPACE);
+                                p = skip_leading_chars(p, /* bad = */ NULL);
 
-                                if (!GREEDY_REALLOC0(n, nlen + 2))
+                                if (strv_extend(&args, ";") < 0)
                                         return log_oom();
 
-                                w = strdup(";");
-                                if (!w)
-                                        return log_oom();
-                                n[nlen++] = w;
-                                n[nlen] = NULL;
                                 continue;
                         }
 
                         r = extract_first_word_and_warn(&p, &word, NULL, EXTRACT_UNQUOTE|EXTRACT_CUNESCAPE, unit, filename, line, rvalue);
-                        if (r == 0)
-                                break;
                         if (r < 0)
                                 return ignore ? 0 : -ENOEXEC;
+                        if (r == 0)
+                                break;
 
                         r = unit_full_printf(u, word, &resolved);
                         if (r < 0) {
                                 log_syntax(unit, ignore ? LOG_WARNING : LOG_ERR, filename, line, r,
-                                           "Failed to resolve unit specifiers in %s%s: %m",
+                                           "Failed to resolve unit specifiers in '%s'%s: %m",
                                            word, ignore ? ", ignoring" : "");
                                 return ignore ? 0 : -ENOEXEC;
                         }
 
-                        if (!GREEDY_REALLOC(n, nlen + 2))
+                        if (strv_consume(&args, TAKE_PTR(resolved)) < 0)
                                 return log_oom();
-
-                        n[nlen++] = TAKE_PTR(resolved);
-                        n[nlen] = NULL;
                 }
 
-                if (!n || !n[0]) {
+                if (strv_isempty(args)) {
                         log_syntax(unit, ignore ? LOG_WARNING : LOG_ERR, filename, line, 0,
                                    "Empty executable name or zeroeth argument%s: %s",
                                    ignore ? ", ignoring" : "", rvalue);
                         return ignore ? 0 : -ENOEXEC;
                 }
 
-                nce = new0(ExecCommand, 1);
-                if (!nce)
+                ExecCommand *nec = new(ExecCommand, 1);
+                if (!nec)
                         return log_oom();
 
-                nce->argv = TAKE_PTR(n);
-                nce->path = TAKE_PTR(path);
-                nce->flags = flags;
-
-                exec_command_append_list(e, nce);
+                *nec = (ExecCommand) {
+                        .path = path_simplify(TAKE_PTR(path)),
+                        .argv = TAKE_PTR(args),
+                        .flags = flags,
+                };
 
-                /* Do not _cleanup_free_ these. */
-                nce = NULL;
+                exec_command_append_list(e, nec);
 
                 rvalue = p;
         } while (semicolon);
index 1c57cce748365d94ded08882d6eddbdf85c16076..8c8cf8b4a70e9f286f636f0d9e1494f5c82098a5 100644 (file)
@@ -810,8 +810,8 @@ static void set_manager_settings(Manager *m) {
         m->cad_burst_action = arg_cad_burst_action;
         /* Note that we don't do structured initialization here, otherwise it will reset the rate limit
          * counter on every daemon-reload. */
-        m->reload_ratelimit.interval = arg_reload_limit_interval_sec;
-        m->reload_ratelimit.burst = arg_reload_limit_burst;
+        m->reload_reexec_ratelimit.interval = arg_reload_limit_interval_sec;
+        m->reload_reexec_ratelimit.burst = arg_reload_limit_burst;
 
         manager_set_watchdog(m, WATCHDOG_RUNTIME, arg_runtime_watchdog);
         manager_set_watchdog(m, WATCHDOG_REBOOT, arg_reboot_watchdog);
@@ -1318,7 +1318,7 @@ static int enforce_syscall_archs(Set *archs) {
 
         r = seccomp_restrict_archs(arg_syscall_archs);
         if (r < 0)
-                return log_error_errno(r, "Failed to enforce system call architecture restrication: %m");
+                return log_error_errno(r, "Failed to enforce system call architecture restriction: %m");
 #endif
         return 0;
 }
@@ -2129,6 +2129,8 @@ static int invoke_main_loop(
                         manager_send_reloading(m);
                         manager_set_switching_root(m, true);
 
+                        dual_timestamp_now(m->timestamps + MANAGER_TIMESTAMP_SOFTREBOOT_START);
+
                         r = prepare_reexecute(m, &arg_serialization, ret_fds, /* switching_root= */ true);
                         if (r < 0) {
                                 *ret_error_message = "Failed to prepare for reexecution";
@@ -3190,6 +3192,11 @@ int main(int argc, char *argv[]) {
                 goto finish;
         }
 
+        /* If we got a SoftRebootStart timestamp during deserialization, then we are in a new soft-reboot
+         * iteration, so bump the counter now before starting units, so that they can reliably read it. */
+        if (dual_timestamp_is_set(&m->timestamps[MANAGER_TIMESTAMP_SOFTREBOOT_START]))
+                m->soft_reboots_count++;
+
         /* This will close all file descriptors that were opened, but not claimed by any unit. */
         fds = fdset_free(fds);
         arg_serialization = safe_fclose(arg_serialization);
index 0d91faee6f214610880096ada7ce6db9063d9333..884332b3249e536c7f6fe6db11c4050d11df45f4 100644 (file)
@@ -23,11 +23,12 @@ int manager_open_serialization(Manager *m, FILE **ret_f) {
         return open_serialization_file("systemd-state", ret_f);
 }
 
-static bool manager_timestamp_shall_serialize(ManagerTimestamp t) {
-        if (!in_initrd())
+static bool manager_timestamp_shall_serialize(ManagerObjective o, ManagerTimestamp t) {
+        if (!in_initrd() && o != MANAGER_SOFT_REBOOT)
                 return true;
 
-        /* The following timestamps only apply to the host system, hence only serialize them there */
+        /* The following timestamps only apply to the host system (or first boot in case of soft-reboot),
+         * hence only serialize them there. */
         return !IN_SET(t,
                        MANAGER_TIMESTAMP_USERSPACE, MANAGER_TIMESTAMP_FINISH,
                        MANAGER_TIMESTAMP_SECURITY_START, MANAGER_TIMESTAMP_SECURITY_FINISH,
@@ -108,10 +109,12 @@ int manager_serialize(
         (void) serialize_usec(f, "pretimeout-watchdog-overridden", m->watchdog_overridden[WATCHDOG_PRETIMEOUT]);
         (void) serialize_item(f, "pretimeout-watchdog-governor-overridden", m->watchdog_pretimeout_governor_overridden);
 
+        (void) serialize_item_format(f, "soft-reboots-count", "%u", m->soft_reboots_count);
+
         for (ManagerTimestamp q = 0; q < _MANAGER_TIMESTAMP_MAX; q++) {
                 _cleanup_free_ char *joined = NULL;
 
-                if (!manager_timestamp_shall_serialize(q))
+                if (!manager_timestamp_shall_serialize(m->objective, q))
                         continue;
 
                 joined = strjoin(manager_timestamp_to_string(q), "-timestamp");
@@ -153,6 +156,7 @@ int manager_serialize(
         }
 
         (void) serialize_ratelimit(f, "dump-ratelimit", &m->dump_ratelimit);
+        (void) serialize_ratelimit(f, "reload-reexec-ratelimit", &m->reload_reexec_ratelimit);
 
         bus_track_serialize(m->subscribed, f, "subscribed");
 
@@ -516,7 +520,16 @@ int manager_deserialize(Manager *m, FILE *f, FDSet *fds) {
                                 (void) varlink_server_deserialize_one(m->varlink_server, val, fds);
                 } else if ((val = startswith(l, "dump-ratelimit=")))
                         deserialize_ratelimit(&m->dump_ratelimit, "dump-ratelimit", val);
-                else {
+                else if ((val = startswith(l, "reload-reexec-ratelimit=")))
+                        deserialize_ratelimit(&m->reload_reexec_ratelimit, "reload-reexec-ratelimit", val);
+                else if ((val = startswith(l, "soft-reboots-count="))) {
+                        unsigned n;
+
+                        if (safe_atou(val, &n) < 0)
+                                log_notice("Failed to parse soft reboots counter '%s', ignoring.", val);
+                        else
+                                m->soft_reboots_count = n;
+                } else {
                         ManagerTimestamp q;
 
                         for (q = 0; q < _MANAGER_TIMESTAMP_MAX; q++) {
index b2d8457790729008603f17016d86a24f7a02b103..52f5e72e117bd983babd1995a6d15ce83862eaf2 100644 (file)
@@ -710,18 +710,18 @@ static int manager_setup_prefix(Manager *m) {
         };
 
         static const struct table_entry paths_system[_EXEC_DIRECTORY_TYPE_MAX] = {
-                [EXEC_DIRECTORY_RUNTIME] =       { SD_PATH_SYSTEM_RUNTIME,       NULL },
-                [EXEC_DIRECTORY_STATE] =         { SD_PATH_SYSTEM_STATE_PRIVATE, NULL },
-                [EXEC_DIRECTORY_CACHE] =         { SD_PATH_SYSTEM_STATE_CACHE,   NULL },
-                [EXEC_DIRECTORY_LOGS] =          { SD_PATH_SYSTEM_STATE_LOGS,    NULL },
+                [EXEC_DIRECTORY_RUNTIME]       = { SD_PATH_SYSTEM_RUNTIME,       NULL },
+                [EXEC_DIRECTORY_STATE]         = { SD_PATH_SYSTEM_STATE_PRIVATE, NULL },
+                [EXEC_DIRECTORY_CACHE]         = { SD_PATH_SYSTEM_STATE_CACHE,   NULL },
+                [EXEC_DIRECTORY_LOGS]          = { SD_PATH_SYSTEM_STATE_LOGS,    NULL },
                 [EXEC_DIRECTORY_CONFIGURATION] = { SD_PATH_SYSTEM_CONFIGURATION, NULL },
         };
 
         static const struct table_entry paths_user[_EXEC_DIRECTORY_TYPE_MAX] = {
-                [EXEC_DIRECTORY_RUNTIME] =       { SD_PATH_USER_RUNTIME,       NULL  },
-                [EXEC_DIRECTORY_STATE] =         { SD_PATH_USER_STATE_PRIVATE, NULL  },
-                [EXEC_DIRECTORY_CACHE] =         { SD_PATH_USER_STATE_CACHE,   NULL  },
-                [EXEC_DIRECTORY_LOGS] =          { SD_PATH_USER_STATE_PRIVATE, "log" },
+                [EXEC_DIRECTORY_RUNTIME]       = { SD_PATH_USER_RUNTIME,       NULL  },
+                [EXEC_DIRECTORY_STATE]         = { SD_PATH_USER_STATE_PRIVATE, NULL  },
+                [EXEC_DIRECTORY_CACHE]         = { SD_PATH_USER_STATE_CACHE,   NULL  },
+                [EXEC_DIRECTORY_LOGS]          = { SD_PATH_USER_STATE_PRIVATE, "log" },
                 [EXEC_DIRECTORY_CONFIGURATION] = { SD_PATH_USER_CONFIGURATION, NULL  },
         };
 
@@ -2203,8 +2203,8 @@ static int manager_dispatch_target_deps_queue(Manager *m) {
                 if (n_targets < 0)
                         return n_targets;
 
-                for (int i = 0; i < n_targets; i++) {
-                        r = unit_add_default_target_dependency(u, targets[i]);
+                FOREACH_ARRAY(i, targets, n_targets) {
+                        r = unit_add_default_target_dependency(u, *i);
                         if (r < 0)
                                 return r;
                 }
@@ -2586,22 +2586,17 @@ static void manager_invoke_notify_message(
                 UNIT_VTABLE(u)->notify_message(u, ucred, tags, fds);
 
         else if (DEBUG_LOGGING) {
-                _cleanup_free_ char *buf = NULL, *x = NULL, *y = NULL;
+                _cleanup_free_ char *joined = strv_join(tags, ", ");
+                char buf[CELLESCAPE_DEFAULT_LENGTH];
 
-                buf = strv_join(tags, ", ");
-                if (buf)
-                        x = ellipsize(buf, 20, 90);
-                if (x)
-                        y = cescape(x);
-
-                log_unit_debug(u, "Got notification message \"%s\", ignoring.", strnull(y));
+                log_unit_debug(u, "Got notification message from unexpected unit type, ignoring: %s",
+                               joined ? cellescape(buf, sizeof(buf), joined) : "(null)");
         }
 }
 
 static int manager_dispatch_notify_fd(sd_event_source *source, int fd, uint32_t revents, void *userdata) {
-
-        _cleanup_fdset_free_ FDSet *fds = NULL;
         Manager *m = ASSERT_PTR(userdata);
+        _cleanup_fdset_free_ FDSet *fds = NULL;
         char buf[NOTIFY_BUFFER_MAX+1];
         struct iovec iovec = {
                 .iov_base = buf,
@@ -3008,9 +3003,9 @@ static int manager_dispatch_signal_fd(sd_event_source *source, int fd, uint32_t
                         const char *target;
                         JobMode mode;
                 } target_table[] = {
-                        [0] = { SPECIAL_DEFAULT_TARGET,     JOB_ISOLATE },
-                        [1] = { SPECIAL_RESCUE_TARGET,      JOB_ISOLATE },
-                        [2] = { SPECIAL_EMERGENCY_TARGET,   JOB_ISOLATE },
+                        [0] = { SPECIAL_DEFAULT_TARGET,     JOB_ISOLATE              },
+                        [1] = { SPECIAL_RESCUE_TARGET,      JOB_ISOLATE              },
+                        [2] = { SPECIAL_EMERGENCY_TARGET,   JOB_ISOLATE              },
                         [3] = { SPECIAL_HALT_TARGET,        JOB_REPLACE_IRREVERSIBLY },
                         [4] = { SPECIAL_POWEROFF_TARGET,    JOB_REPLACE_IRREVERSIBLY },
                         [5] = { SPECIAL_REBOOT_TARGET,      JOB_REPLACE_IRREVERSIBLY },
@@ -3694,7 +3689,17 @@ static void manager_notify_finished(Manager *m) {
         if (MANAGER_IS_TEST_RUN(m))
                 return;
 
-        if (MANAGER_IS_SYSTEM(m) && detect_container() <= 0) {
+        if (MANAGER_IS_SYSTEM(m) && dual_timestamp_is_set(&m->timestamps[MANAGER_TIMESTAMP_SOFTREBOOT_START])) {
+                /* The soft-reboot case, where we only report data for the last reboot */
+                firmware_usec = loader_usec = initrd_usec = kernel_usec = 0;
+                total_usec = userspace_usec = usec_sub_unsigned(m->timestamps[MANAGER_TIMESTAMP_FINISH].monotonic, m->timestamps[MANAGER_TIMESTAMP_SOFTREBOOT_START].monotonic);
+
+                log_struct(LOG_INFO,
+                           "MESSAGE_ID=" SD_MESSAGE_STARTUP_FINISHED_STR,
+                           "USERSPACE_USEC="USEC_FMT, userspace_usec,
+                           LOG_MESSAGE("Soft-reboot finished in %s.",
+                                       FORMAT_TIMESPAN(total_usec, USEC_PER_MSEC)));
+        } else if (MANAGER_IS_SYSTEM(m) && detect_container() <= 0) {
                 char buf[FORMAT_TIMESPAN_MAX + STRLEN(" (firmware) + ") + FORMAT_TIMESPAN_MAX + STRLEN(" (loader) + ")]
                         = {};
                 char *p = buf;
@@ -3905,8 +3910,8 @@ static int manager_run_environment_generators(Manager *m) {
         _cleanup_strv_free_ char **paths = NULL;
         void* args[] = {
                 [STDOUT_GENERATE] = &tmp,
-                [STDOUT_COLLECT] = &tmp,
-                [STDOUT_CONSUME] = &m->transient_environment,
+                [STDOUT_COLLECT]  = &tmp,
+                [STDOUT_CONSUME]  = &m->transient_environment,
         };
         int r;
 
@@ -4800,7 +4805,7 @@ static int short_uid_range(const char *path) {
         /* Taint systemd if we the UID range assigned to this environment doesn't at least cover 0…65534,
          * i.e. from root to nobody. */
 
-        r = uid_range_load_userns(&p, path);
+        r = uid_range_load_userns(path, UID_RANGE_USERNS_INSIDE, &p);
         if (ERRNO_IS_NEG_NOT_SUPPORTED(r))
                 return false;
         if (r < 0)
@@ -5041,6 +5046,7 @@ static const char *const manager_timestamp_table[_MANAGER_TIMESTAMP_MAX] = {
         [MANAGER_TIMESTAMP_INITRD]                   = "initrd",
         [MANAGER_TIMESTAMP_USERSPACE]                = "userspace",
         [MANAGER_TIMESTAMP_FINISH]                   = "finish",
+        [MANAGER_TIMESTAMP_SOFTREBOOT_START]         = "softreboot-start",
         [MANAGER_TIMESTAMP_SECURITY_START]           = "security-start",
         [MANAGER_TIMESTAMP_SECURITY_FINISH]          = "security-finish",
         [MANAGER_TIMESTAMP_GENERATORS_START]         = "generators-start",
index 4d82c4a6a0daa7f91418057b61eca7ec0c118cd5..9541a5e0b469dd23bc67d8f689eb8910d5df4e4a 100644 (file)
@@ -120,6 +120,9 @@ typedef enum ManagerTimestamp {
         MANAGER_TIMESTAMP_INITRD_GENERATORS_FINISH,
         MANAGER_TIMESTAMP_INITRD_UNITS_LOAD_START,
         MANAGER_TIMESTAMP_INITRD_UNITS_LOAD_FINISH,
+
+        MANAGER_TIMESTAMP_SOFTREBOOT_START,
+
         _MANAGER_TIMESTAMP_MAX,
         _MANAGER_TIMESTAMP_INVALID = -EINVAL,
 } ManagerTimestamp;
@@ -488,8 +491,8 @@ struct Manager {
         /* Reference to RestrictFileSystems= BPF program */
         struct restrict_fs_bpf *restrict_fs;
 
-        /* Allow users to configure a rate limit for Reload() operations */
-        RateLimit reload_ratelimit;
+        /* Allow users to configure a rate limit for Reload()/Reexecute() operations */
+        RateLimit reload_reexec_ratelimit;
         /* Dump*() are slow, so always rate limit them to 10 per 10 minutes */
         RateLimit dump_ratelimit;
 
@@ -501,6 +504,8 @@ struct Manager {
         /* Pin the systemd-executor binary, so that it never changes until re-exec, ensuring we don't have
          * serialization/deserialization compatibility issues during upgrades. */
         int executor_fd;
+
+        unsigned soft_reboots_count;
 };
 
 static inline usec_t manager_default_timeout_abort_usec(Manager *m) {
index bda4a3450ebe550250fb977100f83f5f0f83a3c8..06ca1449549cc7df2fc2c1a0bb348bb0e6c02384 100644 (file)
@@ -125,7 +125,7 @@ libcore = shared_library(
                         libaudit,
                         libblkid,
                         libdl,
-                        libkmod,
+                        libkmod_cflags,
                         libm,
                         libmount,
                         libpam,
index 175af5498af37fffc62ca190ed475cae6bbdc806..886ef8974cb00ee4f47b9dd1a394c5cf23ae033f 100644 (file)
 #define RETRY_UMOUNT_MAX 32
 
 static const UnitActiveState state_translation_table[_MOUNT_STATE_MAX] = {
-        [MOUNT_DEAD] = UNIT_INACTIVE,
-        [MOUNT_MOUNTING] = UNIT_ACTIVATING,
-        [MOUNT_MOUNTING_DONE] = UNIT_ACTIVATING,
-        [MOUNT_MOUNTED] = UNIT_ACTIVE,
-        [MOUNT_REMOUNTING] = UNIT_RELOADING,
-        [MOUNT_UNMOUNTING] = UNIT_DEACTIVATING,
+        [MOUNT_DEAD]               = UNIT_INACTIVE,
+        [MOUNT_MOUNTING]           = UNIT_ACTIVATING,
+        [MOUNT_MOUNTING_DONE]      = UNIT_ACTIVATING,
+        [MOUNT_MOUNTED]            = UNIT_ACTIVE,
+        [MOUNT_REMOUNTING]         = UNIT_RELOADING,
+        [MOUNT_UNMOUNTING]         = UNIT_DEACTIVATING,
         [MOUNT_REMOUNTING_SIGTERM] = UNIT_RELOADING,
         [MOUNT_REMOUNTING_SIGKILL] = UNIT_RELOADING,
         [MOUNT_UNMOUNTING_SIGTERM] = UNIT_DEACTIVATING,
         [MOUNT_UNMOUNTING_SIGKILL] = UNIT_DEACTIVATING,
-        [MOUNT_FAILED] = UNIT_FAILED,
-        [MOUNT_CLEANING] = UNIT_MAINTENANCE,
+        [MOUNT_FAILED]             = UNIT_FAILED,
+        [MOUNT_CLEANING]           = UNIT_MAINTENANCE,
 };
 
 static int mount_dispatch_timer(sd_event_source *source, usec_t usec, void *userdata);
 static int mount_dispatch_io(sd_event_source *source, int fd, uint32_t revents, void *userdata);
-static void mount_enter_dead(Mount *m, MountResult f);
+static void mount_enter_dead(Mount *m, MountResult f, bool flush_result);
 static void mount_enter_mounted(Mount *m, MountResult f);
 static void mount_cycle_clear(Mount *m);
 static int mount_process_proc_self_mountinfo(Manager *m);
@@ -800,7 +800,7 @@ static void mount_catchup(Unit *u) {
                         break;
                 case MOUNT_MOUNTED:
                         assert(!pidref_is_set(&m->control_pid));
-                        mount_enter_dead(m, MOUNT_SUCCESS);
+                        mount_enter_dead(m, MOUNT_SUCCESS, /* flush_result = */ false);
                         break;
                 default:
                         break;
@@ -899,10 +899,10 @@ static int mount_spawn(Mount *m, ExecCommand *c, PidRef *ret_pid) {
         return 0;
 }
 
-static void mount_enter_dead(Mount *m, MountResult f) {
+static void mount_enter_dead(Mount *m, MountResult f, bool flush_result) {
         assert(m);
 
-        if (m->result == MOUNT_SUCCESS)
+        if (m->result == MOUNT_SUCCESS || flush_result)
                 m->result = f;
 
         unit_log_result(UNIT(m), m->result == MOUNT_SUCCESS, mount_result_to_string(m->result));
@@ -930,17 +930,20 @@ static void mount_enter_mounted(Mount *m, MountResult f) {
         mount_set_state(m, MOUNT_MOUNTED);
 }
 
-static void mount_enter_dead_or_mounted(Mount *m, MountResult f) {
+static void mount_enter_dead_or_mounted(Mount *m, MountResult f, bool flush_result) {
         assert(m);
 
-        /* Enter DEAD or MOUNTED state, depending on what the kernel currently says about the mount point. We use this
-         * whenever we executed an operation, so that our internal state reflects what the kernel says again, after all
-         * ultimately we just mirror the kernel's internal state on this. */
+        /* Enter DEAD or MOUNTED state, depending on what the kernel currently says about the mount point.
+         * We use this whenever we executed an operation, so that our internal state reflects what
+         * the kernel says again, after all ultimately we just mirror the kernel's internal state on this.
+         *
+         * Note that flush_result only applies to mount_enter_dead(), since that's when the result gets
+         * turned into unit end state. */
 
         if (m->from_proc_self_mountinfo)
                 mount_enter_mounted(m, f);
         else
-                mount_enter_dead(m, f);
+                mount_enter_dead(m, f, flush_result);
 }
 
 static int state_to_kill_operation(MountState state) {
@@ -990,12 +993,12 @@ static void mount_enter_signal(Mount *m, MountState state, MountResult f) {
         else if (state == MOUNT_UNMOUNTING_SIGTERM && m->kill_context.send_sigkill)
                 mount_enter_signal(m, MOUNT_UNMOUNTING_SIGKILL, MOUNT_SUCCESS);
         else
-                mount_enter_dead_or_mounted(m, MOUNT_SUCCESS);
+                mount_enter_dead_or_mounted(m, MOUNT_SUCCESS, /* flush_result = */ false);
 
         return;
 
 fail:
-        mount_enter_dead_or_mounted(m, MOUNT_FAILURE_RESOURCES);
+        mount_enter_dead_or_mounted(m, MOUNT_FAILURE_RESOURCES, /* flush_result = */ false);
 }
 
 static int mount_set_umount_command(Mount *m, ExecCommand *c) {
@@ -1057,7 +1060,7 @@ static void mount_enter_unmounting(Mount *m) {
         return;
 
 fail:
-        mount_enter_dead_or_mounted(m, MOUNT_FAILURE_RESOURCES);
+        mount_enter_dead_or_mounted(m, MOUNT_FAILURE_RESOURCES, /* flush_result = */ false);
 }
 
 static int mount_set_mount_command(Mount *m, ExecCommand *c, const MountParameters *p) {
@@ -1201,7 +1204,7 @@ static void mount_enter_mounting(Mount *m) {
         return;
 
 fail:
-        mount_enter_dead_or_mounted(m, MOUNT_FAILURE_RESOURCES);
+        mount_enter_dead_or_mounted(m, MOUNT_FAILURE_RESOURCES, /* flush_result = */ false);
 }
 
 static void mount_set_reload_result(Mount *m, MountResult result) {
@@ -1267,7 +1270,7 @@ static void mount_enter_remounting(Mount *m) {
 
 fail:
         mount_set_reload_result(m, MOUNT_FAILURE_RESOURCES);
-        mount_enter_dead_or_mounted(m, MOUNT_SUCCESS);
+        mount_enter_dead_or_mounted(m, MOUNT_SUCCESS, /* flush_result = */ false);
 }
 
 static void mount_cycle_clear(Mount *m) {
@@ -1435,8 +1438,8 @@ static int mount_deserialize_item(Unit *u, const char *key, const char *value, F
 
         } else if (streq(key, "control-pid")) {
 
-                pidref_done(&m->control_pid);
-                (void) deserialize_pidref(fds, value, &m->control_pid);
+                if (!pidref_is_set(&m->control_pid))
+                        (void) deserialize_pidref(fds, value, &m->control_pid);
 
         } else if (streq(key, "control-command")) {
                 MountExecCommand id;
@@ -1547,7 +1550,7 @@ static void mount_sigchld_event(Unit *u, pid_t pid, int code, int status) {
                         log_unit_warning(UNIT(m), "Mount process finished, but there is no mount.");
                         f = MOUNT_FAILURE_PROTOCOL;
                 }
-                mount_enter_dead(m, f);
+                mount_enter_dead(m, f, /* flush_result = */ false);
                 break;
 
         case MOUNT_MOUNTING_DONE:
@@ -1557,7 +1560,7 @@ static void mount_sigchld_event(Unit *u, pid_t pid, int code, int status) {
         case MOUNT_REMOUNTING:
         case MOUNT_REMOUNTING_SIGTERM:
         case MOUNT_REMOUNTING_SIGKILL:
-                mount_enter_dead_or_mounted(m, MOUNT_SUCCESS);
+                mount_enter_dead_or_mounted(m, MOUNT_SUCCESS, /* flush_result = */ false);
                 break;
 
         case MOUNT_UNMOUNTING:
@@ -1572,28 +1575,33 @@ static void mount_sigchld_event(Unit *u, pid_t pid, int code, int status) {
                                 mount_enter_unmounting(m);
                         } else {
                                 log_unit_warning(u, "Mount still present after %u attempts to unmount, giving up.", m->n_retry_umount);
-                                mount_enter_mounted(m, f);
+                                mount_enter_mounted(m, MOUNT_FAILURE_PROTOCOL);
                         }
                 } else if (f == MOUNT_FAILURE_EXIT_CODE && !m->from_proc_self_mountinfo) {
                         /* Hmm, umount process spawned by us failed, but the mount disappeared anyway?
                          * Maybe someone else is trying to unmount at the same time. */
                         log_unit_notice(u, "Mount disappeared even though umount process failed, continuing.");
-                        mount_enter_dead(m, MOUNT_SUCCESS);
+                        mount_enter_dead(m, MOUNT_SUCCESS, /* flush_result = */ true);
                 } else
-                        mount_enter_dead_or_mounted(m, f);
+                        /* At this point, either the unmount succeeded or unexpected error occurred. We usually
+                         * remember the first error in 'result', but here let's update that forcibly, since
+                         * there could previous failed attempts yet we only care about the most recent
+                         * attempt. IOW, if we eventually managed to unmount the stuff, don't enter failed
+                         * end state. */
+                        mount_enter_dead_or_mounted(m, f, /* flush_result = */ true);
 
                 break;
 
         case MOUNT_UNMOUNTING_SIGTERM:
         case MOUNT_UNMOUNTING_SIGKILL:
-                mount_enter_dead_or_mounted(m, f);
+                mount_enter_dead_or_mounted(m, f, /* flush_result = */ false);
                 break;
 
         case MOUNT_CLEANING:
                 if (m->clean_result == MOUNT_SUCCESS)
                         m->clean_result = f;
 
-                mount_enter_dead(m, MOUNT_SUCCESS);
+                mount_enter_dead(m, MOUNT_SUCCESS, /* flush_result = */ false);
                 break;
 
         default:
@@ -1631,7 +1639,7 @@ static int mount_dispatch_timer(sd_event_source *source, usec_t usec, void *user
                         mount_enter_signal(m, MOUNT_REMOUNTING_SIGKILL, MOUNT_SUCCESS);
                 } else {
                         log_unit_warning(UNIT(m), "Remounting timed out. Skipping SIGKILL. Ignoring.");
-                        mount_enter_dead_or_mounted(m, MOUNT_SUCCESS);
+                        mount_enter_dead_or_mounted(m, MOUNT_SUCCESS, /* flush_result = */ false);
                 }
                 break;
 
@@ -1639,7 +1647,7 @@ static int mount_dispatch_timer(sd_event_source *source, usec_t usec, void *user
                 mount_set_reload_result(m, MOUNT_FAILURE_TIMEOUT);
 
                 log_unit_warning(UNIT(m), "Mount process still around after SIGKILL. Ignoring.");
-                mount_enter_dead_or_mounted(m, MOUNT_SUCCESS);
+                mount_enter_dead_or_mounted(m, MOUNT_SUCCESS, /* flush_result = */ false);
                 break;
 
         case MOUNT_UNMOUNTING:
@@ -1653,13 +1661,13 @@ static int mount_dispatch_timer(sd_event_source *source, usec_t usec, void *user
                         mount_enter_signal(m, MOUNT_UNMOUNTING_SIGKILL, MOUNT_FAILURE_TIMEOUT);
                 } else {
                         log_unit_warning(UNIT(m), "Mount process timed out. Skipping SIGKILL. Ignoring.");
-                        mount_enter_dead_or_mounted(m, MOUNT_FAILURE_TIMEOUT);
+                        mount_enter_dead_or_mounted(m, MOUNT_FAILURE_TIMEOUT, /* flush_result = */ false);
                 }
                 break;
 
         case MOUNT_UNMOUNTING_SIGKILL:
                 log_unit_warning(UNIT(m), "Mount process still around after SIGKILL. Ignoring.");
-                mount_enter_dead_or_mounted(m, MOUNT_FAILURE_TIMEOUT);
+                mount_enter_dead_or_mounted(m, MOUNT_FAILURE_TIMEOUT, /* flush_result = */ false);
                 break;
 
         case MOUNT_CLEANING:
@@ -2121,8 +2129,11 @@ static int mount_process_proc_self_mountinfo(Manager *m) {
                         switch (mount->state) {
 
                         case MOUNT_MOUNTED:
-                                /* This has just been unmounted by somebody else, follow the state change. */
-                                mount_enter_dead(mount, MOUNT_SUCCESS);
+                                /* This has just been unmounted by somebody else, follow the state change.
+                                 * Also explicitly override the result (see the comment in mount_sigchld_event()),
+                                 * but more aggressively here since the state change is extrinsic. */
+                                mount_cycle_clear(mount);
+                                mount_enter_dead(mount, MOUNT_SUCCESS, /* flush_result = */ true);
                                 break;
 
                         case MOUNT_MOUNTING_DONE:
@@ -2290,7 +2301,7 @@ static int mount_can_start(Unit *u) {
 
         r = unit_test_start_limit(u);
         if (r < 0) {
-                mount_enter_dead(m, MOUNT_FAILURE_START_LIMIT_HIT);
+                mount_enter_dead(m, MOUNT_FAILURE_START_LIMIT_HIT, /* flush_result = */ false);
                 return r;
         }
 
index 855ad0de8934480d3b51e14639434f79969984da..3ade7913c7dfd531c660fe255bad558fefee1918 100644 (file)
@@ -2114,6 +2114,7 @@ int setup_namespace(const NamespaceParameters *p, char **error_path) {
         _cleanup_(dissected_image_unrefp) DissectedImage *dissected_image = NULL;
         _cleanup_strv_free_ char **hierarchies = NULL;
         _cleanup_(mount_list_done) MountList ml = {};
+        _cleanup_close_ int userns_fd = -EBADF;
         bool require_prefix = false;
         const char *root;
         DissectImageFlags dissect_image_flags =
@@ -2150,40 +2151,57 @@ int setup_namespace(const NamespaceParameters *p, char **error_path) {
 
                 SET_FLAG(dissect_image_flags, DISSECT_IMAGE_NO_PARTITION_TABLE, p->verity && p->verity->data_path);
 
-                r = loop_device_make_by_path(
-                                p->root_image,
-                                FLAGS_SET(dissect_image_flags, DISSECT_IMAGE_DEVICE_READ_ONLY) ? O_RDONLY : -1 /* < 0 means writable if possible, read-only as fallback */,
-                                /* sector_size= */ UINT32_MAX,
-                                FLAGS_SET(dissect_image_flags, DISSECT_IMAGE_NO_PARTITION_TABLE) ? 0 : LO_FLAGS_PARTSCAN,
-                                LOCK_SH,
-                                &loop_device);
-                if (r < 0)
-                        return log_debug_errno(r, "Failed to create loop device for root image: %m");
-
-                r = dissect_loop_device(
-                                loop_device,
-                                p->verity,
-                                p->root_image_options,
-                                p->root_image_policy,
-                                dissect_image_flags,
-                                &dissected_image);
-                if (r < 0)
-                        return log_debug_errno(r, "Failed to dissect image: %m");
+                if (p->runtime_scope == RUNTIME_SCOPE_SYSTEM) {
+                        /* In system mode we mount directly */
 
-                r = dissected_image_load_verity_sig_partition(
-                                dissected_image,
-                                loop_device->fd,
-                                p->verity);
-                if (r < 0)
-                        return r;
+                        r = loop_device_make_by_path(
+                                        p->root_image,
+                                        FLAGS_SET(dissect_image_flags, DISSECT_IMAGE_DEVICE_READ_ONLY) ? O_RDONLY : -1 /* < 0 means writable if possible, read-only as fallback */,
+                                        /* sector_size= */ UINT32_MAX,
+                                        FLAGS_SET(dissect_image_flags, DISSECT_IMAGE_NO_PARTITION_TABLE) ? 0 : LO_FLAGS_PARTSCAN,
+                                        LOCK_SH,
+                                        &loop_device);
+                        if (r < 0)
+                                return log_debug_errno(r, "Failed to create loop device for root image: %m");
+
+                        r = dissect_loop_device(
+                                        loop_device,
+                                        p->verity,
+                                        p->root_image_options,
+                                        p->root_image_policy,
+                                        dissect_image_flags,
+                                        &dissected_image);
+                        if (r < 0)
+                                return log_debug_errno(r, "Failed to dissect image: %m");
 
-                r = dissected_image_decrypt(
-                                dissected_image,
-                                NULL,
-                                p->verity,
-                                dissect_image_flags);
-                if (r < 0)
-                        return log_debug_errno(r, "Failed to decrypt dissected image: %m");
+                        r = dissected_image_load_verity_sig_partition(
+                                        dissected_image,
+                                        loop_device->fd,
+                                        p->verity);
+                        if (r < 0)
+                                return r;
+
+                        r = dissected_image_decrypt(
+                                        dissected_image,
+                                        NULL,
+                                        p->verity,
+                                        dissect_image_flags);
+                        if (r < 0)
+                                return log_debug_errno(r, "Failed to decrypt dissected image: %m");
+                } else {
+                        userns_fd = namespace_open_by_type(NAMESPACE_USER);
+                        if (userns_fd < 0)
+                                return log_debug_errno(userns_fd, "Failed to open our own user namespace: %m");
+
+                        r = mountfsd_mount_image(
+                                        p->root_image,
+                                        userns_fd,
+                                        p->root_image_policy,
+                                        dissect_image_flags,
+                                        &dissected_image);
+                        if (r < 0)
+                                return r;
+                }
         }
 
         if (p->root_directory)
@@ -2547,16 +2565,18 @@ int setup_namespace(const NamespaceParameters *p, char **error_path) {
                                 root,
                                 /* uid_shift= */ UID_INVALID,
                                 /* uid_range= */ UID_INVALID,
-                                /* userns_fd= */ -EBADF,
+                                userns_fd,
                                 dissect_image_flags);
                 if (r < 0)
                         return log_debug_errno(r, "Failed to mount root image: %m");
 
                 /* Now release the block device lock, so that udevd is free to call BLKRRPART on the device
                  * if it likes. */
-                r = loop_device_flock(loop_device, LOCK_UN);
-                if (r < 0)
-                        return log_debug_errno(r, "Failed to release lock on loopback block device: %m");
+                if (loop_device) {
+                        r = loop_device_flock(loop_device, LOCK_UN);
+                        if (r < 0)
+                                return log_debug_errno(r, "Failed to release lock on loopback block device: %m");
+                }
 
                 r = dissected_image_relinquish(dissected_image);
                 if (r < 0)
index edce5901547a15b47f03849be5c64643ea34b116..cfa2aeb03f6ffd058f4a8c38c087dc45f87c8d19 100644 (file)
 #include "user-util.h"
 
 static const UnitActiveState state_translation_table[_SCOPE_STATE_MAX] = {
-        [SCOPE_DEAD] = UNIT_INACTIVE,
-        [SCOPE_START_CHOWN] = UNIT_ACTIVATING,
-        [SCOPE_RUNNING] = UNIT_ACTIVE,
-        [SCOPE_ABANDONED] = UNIT_ACTIVE,
+        [SCOPE_DEAD]         = UNIT_INACTIVE,
+        [SCOPE_START_CHOWN]  = UNIT_ACTIVATING,
+        [SCOPE_RUNNING]      = UNIT_ACTIVE,
+        [SCOPE_ABANDONED]    = UNIT_ACTIVE,
         [SCOPE_STOP_SIGTERM] = UNIT_DEACTIVATING,
         [SCOPE_STOP_SIGKILL] = UNIT_DEACTIVATING,
-        [SCOPE_FAILED] = UNIT_FAILED,
+        [SCOPE_FAILED]       = UNIT_FAILED,
 };
 
 static int scope_dispatch_timer(sd_event_source *source, usec_t usec, void *userdata);
@@ -99,7 +99,8 @@ static void scope_set_state(Scope *s, ScopeState state) {
         }
 
         if (state != old_state)
-                log_debug("%s changed %s -> %s", UNIT(s)->id, scope_state_to_string(old_state), scope_state_to_string(state));
+                log_unit_debug(UNIT(s), "Changed %s -> %s",
+                               scope_state_to_string(old_state), scope_state_to_string(state));
 
         unit_notify(UNIT(s), state_translation_table[old_state], state_translation_table[state], /* reload_success = */ true);
 }
@@ -570,6 +571,8 @@ static int scope_deserialize_item(Unit *u, const char *key, const char *value, F
         } else if (streq(key, "pids")) {
                 _cleanup_(pidref_done) PidRef pidref = PIDREF_NULL;
 
+                /* We don't check if we already received the pid before here because unit_watch_pidref()
+                 * does this check internally and discards the new pidref if we already received it before. */
                 if (deserialize_pidref(fds, value, &pidref) >= 0) {
                         r = unit_watch_pidref(u, &pidref, /* exclusive= */ false);
                         if (r < 0)
index be38ec73d8b11601f62d05020aa0fd34b47c24d2..f6800c317032f6c62ad4d2e0cfec85199a32f06e 100644 (file)
 #define service_spawn(...) service_spawn_internal(__func__, __VA_ARGS__)
 
 static const UnitActiveState state_translation_table[_SERVICE_STATE_MAX] = {
-        [SERVICE_DEAD] = UNIT_INACTIVE,
-        [SERVICE_CONDITION] = UNIT_ACTIVATING,
-        [SERVICE_START_PRE] = UNIT_ACTIVATING,
-        [SERVICE_START] = UNIT_ACTIVATING,
-        [SERVICE_START_POST] = UNIT_ACTIVATING,
-        [SERVICE_RUNNING] = UNIT_ACTIVE,
-        [SERVICE_EXITED] = UNIT_ACTIVE,
-        [SERVICE_RELOAD] = UNIT_RELOADING,
-        [SERVICE_RELOAD_SIGNAL] = UNIT_RELOADING,
-        [SERVICE_RELOAD_NOTIFY] = UNIT_RELOADING,
-        [SERVICE_STOP] = UNIT_DEACTIVATING,
-        [SERVICE_STOP_WATCHDOG] = UNIT_DEACTIVATING,
-        [SERVICE_STOP_SIGTERM] = UNIT_DEACTIVATING,
-        [SERVICE_STOP_SIGKILL] = UNIT_DEACTIVATING,
-        [SERVICE_STOP_POST] = UNIT_DEACTIVATING,
-        [SERVICE_FINAL_WATCHDOG] = UNIT_DEACTIVATING,
-        [SERVICE_FINAL_SIGTERM] = UNIT_DEACTIVATING,
-        [SERVICE_FINAL_SIGKILL] = UNIT_DEACTIVATING,
-        [SERVICE_FAILED] = UNIT_FAILED,
-        [SERVICE_DEAD_BEFORE_AUTO_RESTART] = UNIT_INACTIVE,
+        [SERVICE_DEAD]                       = UNIT_INACTIVE,
+        [SERVICE_CONDITION]                  = UNIT_ACTIVATING,
+        [SERVICE_START_PRE]                  = UNIT_ACTIVATING,
+        [SERVICE_START]                      = UNIT_ACTIVATING,
+        [SERVICE_START_POST]                 = UNIT_ACTIVATING,
+        [SERVICE_RUNNING]                    = UNIT_ACTIVE,
+        [SERVICE_EXITED]                     = UNIT_ACTIVE,
+        [SERVICE_RELOAD]                     = UNIT_RELOADING,
+        [SERVICE_RELOAD_SIGNAL]              = UNIT_RELOADING,
+        [SERVICE_RELOAD_NOTIFY]              = UNIT_RELOADING,
+        [SERVICE_STOP]                       = UNIT_DEACTIVATING,
+        [SERVICE_STOP_WATCHDOG]              = UNIT_DEACTIVATING,
+        [SERVICE_STOP_SIGTERM]               = UNIT_DEACTIVATING,
+        [SERVICE_STOP_SIGKILL]               = UNIT_DEACTIVATING,
+        [SERVICE_STOP_POST]                  = UNIT_DEACTIVATING,
+        [SERVICE_FINAL_WATCHDOG]             = UNIT_DEACTIVATING,
+        [SERVICE_FINAL_SIGTERM]              = UNIT_DEACTIVATING,
+        [SERVICE_FINAL_SIGKILL]              = UNIT_DEACTIVATING,
+        [SERVICE_FAILED]                     = UNIT_FAILED,
+        [SERVICE_DEAD_BEFORE_AUTO_RESTART]   = UNIT_INACTIVE,
         [SERVICE_FAILED_BEFORE_AUTO_RESTART] = UNIT_FAILED,
-        [SERVICE_DEAD_RESOURCES_PINNED] = UNIT_INACTIVE,
-        [SERVICE_AUTO_RESTART] = UNIT_ACTIVATING,
-        [SERVICE_AUTO_RESTART_QUEUED] = UNIT_ACTIVATING,
-        [SERVICE_CLEANING] = UNIT_MAINTENANCE,
+        [SERVICE_DEAD_RESOURCES_PINNED]      = UNIT_INACTIVE,
+        [SERVICE_AUTO_RESTART]               = UNIT_ACTIVATING,
+        [SERVICE_AUTO_RESTART_QUEUED]        = UNIT_ACTIVATING,
+        [SERVICE_CLEANING]                   = UNIT_MAINTENANCE,
 };
 
 /* For Type=idle we never want to delay any other jobs, hence we
  * consider idle jobs active as soon as we start working on them */
 static const UnitActiveState state_translation_table_idle[_SERVICE_STATE_MAX] = {
-        [SERVICE_DEAD] = UNIT_INACTIVE,
-        [SERVICE_CONDITION] = UNIT_ACTIVE,
-        [SERVICE_START_PRE] = UNIT_ACTIVE,
-        [SERVICE_START] = UNIT_ACTIVE,
-        [SERVICE_START_POST] = UNIT_ACTIVE,
-        [SERVICE_RUNNING] = UNIT_ACTIVE,
-        [SERVICE_EXITED] = UNIT_ACTIVE,
-        [SERVICE_RELOAD] = UNIT_RELOADING,
-        [SERVICE_RELOAD_SIGNAL] = UNIT_RELOADING,
-        [SERVICE_RELOAD_NOTIFY] = UNIT_RELOADING,
-        [SERVICE_STOP] = UNIT_DEACTIVATING,
-        [SERVICE_STOP_WATCHDOG] = UNIT_DEACTIVATING,
-        [SERVICE_STOP_SIGTERM] = UNIT_DEACTIVATING,
-        [SERVICE_STOP_SIGKILL] = UNIT_DEACTIVATING,
-        [SERVICE_STOP_POST] = UNIT_DEACTIVATING,
-        [SERVICE_FINAL_WATCHDOG] = UNIT_DEACTIVATING,
-        [SERVICE_FINAL_SIGTERM] = UNIT_DEACTIVATING,
-        [SERVICE_FINAL_SIGKILL] = UNIT_DEACTIVATING,
-        [SERVICE_FAILED] = UNIT_FAILED,
-        [SERVICE_DEAD_BEFORE_AUTO_RESTART] = UNIT_INACTIVE,
+        [SERVICE_DEAD]                       = UNIT_INACTIVE,
+        [SERVICE_CONDITION]                  = UNIT_ACTIVE,
+        [SERVICE_START_PRE]                  = UNIT_ACTIVE,
+        [SERVICE_START]                      = UNIT_ACTIVE,
+        [SERVICE_START_POST]                 = UNIT_ACTIVE,
+        [SERVICE_RUNNING]                    = UNIT_ACTIVE,
+        [SERVICE_EXITED]                     = UNIT_ACTIVE,
+        [SERVICE_RELOAD]                     = UNIT_RELOADING,
+        [SERVICE_RELOAD_SIGNAL]              = UNIT_RELOADING,
+        [SERVICE_RELOAD_NOTIFY]              = UNIT_RELOADING,
+        [SERVICE_STOP]                       = UNIT_DEACTIVATING,
+        [SERVICE_STOP_WATCHDOG]              = UNIT_DEACTIVATING,
+        [SERVICE_STOP_SIGTERM]               = UNIT_DEACTIVATING,
+        [SERVICE_STOP_SIGKILL]               = UNIT_DEACTIVATING,
+        [SERVICE_STOP_POST]                  = UNIT_DEACTIVATING,
+        [SERVICE_FINAL_WATCHDOG]             = UNIT_DEACTIVATING,
+        [SERVICE_FINAL_SIGTERM]              = UNIT_DEACTIVATING,
+        [SERVICE_FINAL_SIGKILL]              = UNIT_DEACTIVATING,
+        [SERVICE_FAILED]                     = UNIT_FAILED,
+        [SERVICE_DEAD_BEFORE_AUTO_RESTART]   = UNIT_INACTIVE,
         [SERVICE_FAILED_BEFORE_AUTO_RESTART] = UNIT_FAILED,
-        [SERVICE_DEAD_RESOURCES_PINNED] = UNIT_INACTIVE,
-        [SERVICE_AUTO_RESTART] = UNIT_ACTIVATING,
-        [SERVICE_AUTO_RESTART_QUEUED] = UNIT_ACTIVATING,
-        [SERVICE_CLEANING] = UNIT_MAINTENANCE,
+        [SERVICE_DEAD_RESOURCES_PINNED]      = UNIT_INACTIVE,
+        [SERVICE_AUTO_RESTART]               = UNIT_ACTIVATING,
+        [SERVICE_AUTO_RESTART_QUEUED]        = UNIT_ACTIVATING,
+        [SERVICE_CLEANING]                   = UNIT_MAINTENANCE,
 };
 
 static int service_dispatch_inotify_io(sd_event_source *source, int fd, uint32_t events, void *userdata);
@@ -191,42 +191,41 @@ static void service_unwatch_pid_file(Service *s) {
         s->pid_file_pathspec = mfree(s->pid_file_pathspec);
 }
 
-static int service_set_main_pidref(Service *s, PidRef *pidref) {
+static int service_set_main_pidref(Service *s, PidRef pidref_consume) {
+        _cleanup_(pidref_done) PidRef pidref = pidref_consume;
         int r;
 
         assert(s);
 
-        /* Takes ownership of the specified pidref on success, but not on failure. */
+        /* Takes ownership of the specified pidref on both success and failure. */
 
-        if (!pidref_is_set(pidref))
+        if (!pidref_is_set(&pidref))
                 return -ESRCH;
 
-        if (pidref->pid <= 1)
+        if (pidref.pid <= 1)
                 return -EINVAL;
 
-        if (pidref_is_self(pidref))
+        if (pidref_is_self(&pidref))
                 return -EINVAL;
 
-        if (pidref_equal(&s->main_pid, pidref) && s->main_pid_known) {
-                pidref_done(pidref);
+        if (s->main_pid_known && pidref_equal(&s->main_pid, &pidref))
                 return 0;
-        }
 
-        if (!pidref_equal(&s->main_pid, pidref)) {
+        if (!pidref_equal(&s->main_pid, &pidref)) {
                 service_unwatch_main_pid(s);
-                exec_status_start(&s->main_exec_status, pidref->pid);
+                exec_status_start(&s->main_exec_status, pidref.pid);
         }
 
-        s->main_pid = TAKE_PIDREF(*pidref);
+        s->main_pid = TAKE_PIDREF(pidref);
         s->main_pid_known = true;
 
         r = pidref_is_my_child(&s->main_pid);
         if (r < 0)
                 log_unit_warning_errno(UNIT(s), r, "Can't determine if process "PID_FMT" is our child, assuming it is not: %m", s->main_pid.pid);
-        else if (r == 0)
+        else if (r == 0) // FIXME: Supervise through pidfd here
                 log_unit_warning(UNIT(s), "Supervising process "PID_FMT" which is not our child. We'll most likely not notice when it exits.", s->main_pid.pid);
-
         s->main_pid_alien = r <= 0;
+
         return 0;
 }
 
@@ -1166,7 +1165,7 @@ static int service_load_pid_file(Service *s, bool may_warn) {
         } else
                 log_unit_debug(UNIT(s), "Main PID loaded: "PID_FMT, pidref.pid);
 
-        r = service_set_main_pidref(s, &pidref);
+        r = service_set_main_pidref(s, TAKE_PIDREF(pidref));
         if (r < 0)
                 return r;
 
@@ -1196,7 +1195,7 @@ static void service_search_main_pid(Service *s) {
                 return;
 
         log_unit_debug(UNIT(s), "Main PID guessed: "PID_FMT, pid.pid);
-        if (service_set_main_pidref(s, &pid) < 0)
+        if (service_set_main_pidref(s, TAKE_PIDREF(pid)) < 0)
                 return;
 
         r = unit_watch_pidref(UNIT(s), &s->main_pid, /* exclusive= */ false);
@@ -1406,8 +1405,8 @@ static int service_collect_fds(
 
                 UNIT_FOREACH_DEPENDENCY(u, UNIT(s), UNIT_ATOM_TRIGGERED_BY) {
                         _cleanup_free_ int *cfds = NULL;
-                        Socket *sock;
                         int cn_fds;
+                        Socket *sock;
 
                         sock = SOCKET(u);
                         if (!sock)
@@ -1423,18 +1422,8 @@ static int service_collect_fds(
                         if (!rfds) {
                                 rfds = TAKE_PTR(cfds);
                                 rn_socket_fds = cn_fds;
-                        } else {
-                                int *t;
-
-                                t = reallocarray(rfds, rn_socket_fds + cn_fds, sizeof(int));
-                                if (!t)
-                                        return -ENOMEM;
-
-                                memcpy(t + rn_socket_fds, cfds, cn_fds * sizeof(int));
-
-                                rfds = t;
-                                rn_socket_fds += cn_fds;
-                        }
+                        } else if (!GREEDY_REALLOC_APPEND(rfds, rn_socket_fds, cfds, cn_fds))
+                                return -ENOMEM;
 
                         r = strv_extend_n(&rfd_names, socket_fdname(sock), cn_fds);
                         if (r < 0)
@@ -1992,7 +1981,7 @@ static void service_enter_dead(Service *s, ServiceResult f, bool allow_restart)
         } else if (s->result == SERVICE_SKIP_CONDITION) {
                 unit_log_skip(UNIT(s), service_result_to_string(s->result));
                 end_state = service_determine_dead_state(s);
-                restart_state = SERVICE_DEAD_BEFORE_AUTO_RESTART;
+                restart_state = _SERVICE_STATE_INVALID; /* Never restart if skipped due to condition failure */
         } else {
                 unit_log_failure(UNIT(s), service_result_to_string(s->result));
                 end_state = SERVICE_FAILED;
@@ -2014,6 +2003,8 @@ static void service_enter_dead(Service *s, ServiceResult f, bool allow_restart)
         if (allow_restart) {
                 usec_t restart_usec_next;
 
+                assert(restart_state >= 0 && restart_state < _SERVICE_STATE_MAX);
+
                 /* We make two state changes here: one that maps to the high-level UNIT_INACTIVE/UNIT_FAILED
                  * state (i.e. a state indicating deactivation), and then one that that maps to the
                  * high-level UNIT_STARTING state (i.e. a state indicating activation). We do this so that
@@ -2425,7 +2416,7 @@ static void service_enter_start(Service *s) {
                 /* For simple services we immediately start
                  * the START_POST binaries. */
 
-                (void) service_set_main_pidref(s, &pidref);
+                (void) service_set_main_pidref(s, TAKE_PIDREF(pidref));
                 service_enter_start_post(s);
 
         } else  if (s->type == SERVICE_FORKING) {
@@ -2444,7 +2435,7 @@ static void service_enter_start(Service *s) {
                 /* For D-Bus services we know the main pid right away, but wait for the bus name to appear on the
                  * bus. 'notify' and 'exec' services are similar. */
 
-                (void) service_set_main_pidref(s, &pidref);
+                (void) service_set_main_pidref(s, TAKE_PIDREF(pidref));
                 service_set_state(s, SERVICE_START);
         } else
                 assert_not_reached();
@@ -2513,7 +2504,6 @@ static void service_enter_condition(Service *s) {
                                   service_exec_flags(s->control_command_id, /* cred_flag = */ 0),
                                   s->timeout_start_usec,
                                   &s->control_pid);
-
                 if (r < 0) {
                         log_unit_warning_errno(UNIT(s), r, "Failed to spawn 'exec-condition' task: %m");
                         goto fail;
@@ -2718,7 +2708,7 @@ static void service_run_next_main(Service *s) {
                 return;
         }
 
-        (void) service_set_main_pidref(s, &pidref);
+        (void) service_set_main_pidref(s, TAKE_PIDREF(pidref));
 }
 
 static int service_start(Unit *u) {
@@ -3187,15 +3177,15 @@ static int service_deserialize_item(Unit *u, const char *key, const char *value,
                         s->reload_result = f;
 
         } else if (streq(key, "control-pid")) {
-                pidref_done(&s->control_pid);
 
-                (void) deserialize_pidref(fds, value, &s->control_pid);
+                if (!pidref_is_set(&s->control_pid))
+                        (void) deserialize_pidref(fds, value, &s->control_pid);
 
         } else if (streq(key, "main-pid")) {
-                _cleanup_(pidref_done) PidRef pidref = PIDREF_NULL;
+                PidRef pidref;
 
-                if (deserialize_pidref(fds, value, &pidref) >= 0)
-                        (void) service_set_main_pidref(s, &pidref);
+                if (!pidref_is_set(&s->main_pid) && deserialize_pidref(fds, value, &pidref) >= 0)
+                        (void) service_set_main_pidref(s, pidref);
 
         } else if (streq(key, "main-pid-known")) {
                 int b;
@@ -4381,10 +4371,10 @@ static void service_notify_message(
                                         log_unit_debug(u, "New main PID "PID_FMT" does not belong to service, but we'll accept it as the request to change it came from a privileged process.", new_main_pid.pid);
                                         r = 1;
                                 } else
-                                        log_unit_debug(u, "New main PID "PID_FMT" does not belong to service, refusing.", new_main_pid.pid);
+                                        log_unit_warning(u, "New main PID "PID_FMT" does not belong to service, refusing.", new_main_pid.pid);
                         }
                         if (r > 0) {
-                                (void) service_set_main_pidref(s, &new_main_pid);
+                                (void) service_set_main_pidref(s, TAKE_PIDREF(new_main_pid));
 
                                 r = unit_watch_pidref(UNIT(s), &s->main_pid, /* exclusive= */ false);
                                 if (r < 0)
@@ -4657,7 +4647,7 @@ static int bus_name_pid_lookup_callback(sd_bus_message *reply, void *userdata, s
 
         log_unit_debug(UNIT(s), "D-Bus name %s is now owned by process " PID_FMT, s->bus_name, pidref.pid);
 
-        (void) service_set_main_pidref(s, &pidref);
+        (void) service_set_main_pidref(s, TAKE_PIDREF(pidref));
         (void) unit_watch_pidref(UNIT(s), &s->main_pid, /* exclusive= */ false);
         return 1;
 }
@@ -4715,7 +4705,7 @@ int service_set_socket_fd(
                 Service *s,
                 int fd,
                 Socket *sock,
-                SocketPeer *peer,
+                SocketPeer *peer, /* reference to object is donated to us on success */
                 bool selinux_context_net) {
 
         _cleanup_free_ char *peer_text = NULL;
@@ -5029,7 +5019,7 @@ DEFINE_STRING_TABLE_LOOKUP(service_restart, ServiceRestart);
 
 static const char* const service_restart_mode_table[_SERVICE_RESTART_MODE_MAX] = {
         [SERVICE_RESTART_MODE_NORMAL] = "normal",
-        [SERVICE_RESTART_MODE_DIRECT]  = "direct",
+        [SERVICE_RESTART_MODE_DIRECT] = "direct",
 };
 
 DEFINE_STRING_TABLE_LOOKUP(service_restart_mode, ServiceRestartMode);
index 606237ee0eaa45f51f6a0d6a4bd4f12d05b76a13..81b6b23fd7dd253d6f65a7d85905c5718a067814 100644 (file)
@@ -38,11 +38,12 @@ int parse_show_status(const char *v, ShowStatus *ret) {
 
 int status_vprintf(const char *status, ShowStatusFlags flags, const char *format, va_list ap) {
         static const char status_indent[] = "         "; /* "[" STATUS "] " */
+        static bool prev_ephemeral = false;
+
         _cleanup_free_ char *s = NULL;
         _cleanup_close_ int fd = -EBADF;
         struct iovec iovec[7] = {};
         int n = 0;
-        static bool prev_ephemeral;
 
         assert(format);
 
@@ -70,7 +71,7 @@ int status_vprintf(const char *status, ShowStatusFlags flags, const char *format
                 if (c <= 0)
                         c = 80;
 
-                sl = status ? sizeof(status_indent)-1 : 0;
+                sl = status ? strlen(status_indent) : 0;
 
                 emax = c - sl - 1;
                 if (emax < 3)
index 63ea1758b0867dd971a81bae3b4c3f4dd6d411cd..4e71976e4fc8adebf4bdc4a160d3799c470c0318 100644 (file)
@@ -16,8 +16,8 @@
 #include "unit.h"
 
 static const UnitActiveState state_translation_table[_SLICE_STATE_MAX] = {
-        [SLICE_DEAD] = UNIT_INACTIVE,
-        [SLICE_ACTIVE] = UNIT_ACTIVE
+        [SLICE_DEAD]   = UNIT_INACTIVE,
+        [SLICE_ACTIVE] = UNIT_ACTIVE,
 };
 
 static void slice_init(Unit *u) {
@@ -27,24 +27,22 @@ static void slice_init(Unit *u) {
         u->ignore_on_isolate = true;
 }
 
-static void slice_set_state(Slice *t, SliceState state) {
+static void slice_set_state(Slice *s, SliceState state) {
         SliceState old_state;
 
-        assert(t);
+        assert(s);
 
-        if (t->state != state)
-                bus_unit_send_pending_change_signal(UNIT(t), false);
+        if (s->state != state)
+                bus_unit_send_pending_change_signal(UNIT(s), false);
 
-        old_state = t->state;
-        t->state = state;
+        old_state = s->state;
+        s->state = state;
 
         if (state != old_state)
-                log_debug("%s changed %s -> %s",
-                          UNIT(t)->id,
-                          slice_state_to_string(old_state),
-                          slice_state_to_string(state));
+                log_unit_debug(UNIT(s), "Changed %s -> %s",
+                               slice_state_to_string(old_state), slice_state_to_string(state));
 
-        unit_notify(UNIT(t), state_translation_table[old_state], state_translation_table[state], /* reload_success = */ true);
+        unit_notify(UNIT(s), state_translation_table[old_state], state_translation_table[state], /* reload_success = */ true);
 }
 
 static int slice_add_parent_slice(Slice *s) {
index 5dbbd5a1d255926942e022cb39eb0b0921a120ca..36d458f35c01ef14317d321ffd5d3bf99d8006e0 100644 (file)
@@ -57,20 +57,20 @@ struct SocketPeer {
 };
 
 static const UnitActiveState state_translation_table[_SOCKET_STATE_MAX] = {
-        [SOCKET_DEAD] = UNIT_INACTIVE,
-        [SOCKET_START_PRE] = UNIT_ACTIVATING,
-        [SOCKET_START_CHOWN] = UNIT_ACTIVATING,
-        [SOCKET_START_POST] = UNIT_ACTIVATING,
-        [SOCKET_LISTENING] = UNIT_ACTIVE,
-        [SOCKET_RUNNING] = UNIT_ACTIVE,
-        [SOCKET_STOP_PRE] = UNIT_DEACTIVATING,
+        [SOCKET_DEAD]             = UNIT_INACTIVE,
+        [SOCKET_START_PRE]        = UNIT_ACTIVATING,
+        [SOCKET_START_CHOWN]      = UNIT_ACTIVATING,
+        [SOCKET_START_POST]       = UNIT_ACTIVATING,
+        [SOCKET_LISTENING]        = UNIT_ACTIVE,
+        [SOCKET_RUNNING]          = UNIT_ACTIVE,
+        [SOCKET_STOP_PRE]         = UNIT_DEACTIVATING,
         [SOCKET_STOP_PRE_SIGTERM] = UNIT_DEACTIVATING,
         [SOCKET_STOP_PRE_SIGKILL] = UNIT_DEACTIVATING,
-        [SOCKET_STOP_POST] = UNIT_DEACTIVATING,
-        [SOCKET_FINAL_SIGTERM] = UNIT_DEACTIVATING,
-        [SOCKET_FINAL_SIGKILL] = UNIT_DEACTIVATING,
-        [SOCKET_FAILED] = UNIT_FAILED,
-        [SOCKET_CLEANING] = UNIT_MAINTENANCE,
+        [SOCKET_STOP_POST]        = UNIT_DEACTIVATING,
+        [SOCKET_FINAL_SIGTERM]    = UNIT_DEACTIVATING,
+        [SOCKET_FINAL_SIGKILL]    = UNIT_DEACTIVATING,
+        [SOCKET_FAILED]           = UNIT_FAILED,
+        [SOCKET_CLEANING]         = UNIT_MAINTENANCE,
 };
 
 static int socket_dispatch_io(sd_event_source *source, int fd, uint32_t revents, void *userdata);
@@ -2631,8 +2631,9 @@ static int socket_deserialize_item(Unit *u, const char *key, const char *value,
                 else
                         s->n_refused += k;
         } else if (streq(key, "control-pid")) {
-                pidref_done(&s->control_pid);
-                (void) deserialize_pidref(fds, value, &s->control_pid);
+
+                if (!pidref_is_set(&s->control_pid))
+                        (void) deserialize_pidref(fds, value, &s->control_pid);
 
         } else if (streq(key, "control-command")) {
                 SocketExecCommand id;
@@ -3476,7 +3477,7 @@ static const char* const socket_exec_command_table[_SOCKET_EXEC_COMMAND_MAX] = {
         [SOCKET_EXEC_START_CHOWN] = "ExecStartChown",
         [SOCKET_EXEC_START_POST]  = "ExecStartPost",
         [SOCKET_EXEC_STOP_PRE]    = "ExecStopPre",
-        [SOCKET_EXEC_STOP_POST]   = "ExecStopPost"
+        [SOCKET_EXEC_STOP_POST]   = "ExecStopPost",
 };
 
 DEFINE_STRING_TABLE_LOOKUP(socket_exec_command, SocketExecCommand);
@@ -3490,7 +3491,7 @@ static const char* const socket_result_table[_SOCKET_RESULT_MAX] = {
         [SOCKET_FAILURE_CORE_DUMP]               = "core-dump",
         [SOCKET_FAILURE_START_LIMIT_HIT]         = "start-limit-hit",
         [SOCKET_FAILURE_TRIGGER_LIMIT_HIT]       = "trigger-limit-hit",
-        [SOCKET_FAILURE_SERVICE_START_LIMIT_HIT] = "service-start-limit-hit"
+        [SOCKET_FAILURE_SERVICE_START_LIMIT_HIT] = "service-start-limit-hit",
 };
 
 DEFINE_STRING_TABLE_LOOKUP(socket_result, SocketResult);
index 9e200086f37817cd42cbb75eef383d520ef2d89b..820dfdff81c9999fca9df44702f0bee44107809d 100644 (file)
 #include "virt.h"
 
 static const UnitActiveState state_translation_table[_SWAP_STATE_MAX] = {
-        [SWAP_DEAD] = UNIT_INACTIVE,
-        [SWAP_ACTIVATING] = UNIT_ACTIVATING,
-        [SWAP_ACTIVATING_DONE] = UNIT_ACTIVE,
-        [SWAP_ACTIVE] = UNIT_ACTIVE,
-        [SWAP_DEACTIVATING] = UNIT_DEACTIVATING,
+        [SWAP_DEAD]                 = UNIT_INACTIVE,
+        [SWAP_ACTIVATING]           = UNIT_ACTIVATING,
+        [SWAP_ACTIVATING_DONE]      = UNIT_ACTIVE,
+        [SWAP_ACTIVE]               = UNIT_ACTIVE,
+        [SWAP_DEACTIVATING]         = UNIT_DEACTIVATING,
         [SWAP_DEACTIVATING_SIGTERM] = UNIT_DEACTIVATING,
         [SWAP_DEACTIVATING_SIGKILL] = UNIT_DEACTIVATING,
-        [SWAP_FAILED] = UNIT_FAILED,
-        [SWAP_CLEANING] = UNIT_MAINTENANCE,
+        [SWAP_FAILED]               = UNIT_FAILED,
+        [SWAP_CLEANING]             = UNIT_MAINTENANCE,
 };
 
 static int swap_dispatch_timer(sd_event_source *source, usec_t usec, void *userdata);
@@ -955,8 +955,8 @@ static int swap_deserialize_item(Unit *u, const char *key, const char *value, FD
                         s->result = f;
         } else if (streq(key, "control-pid")) {
 
-                pidref_done(&s->control_pid);
-                (void) deserialize_pidref(fds, value, &s->control_pid);
+                if (!pidref_is_set(&s->control_pid))
+                        (void) deserialize_pidref(fds, value, &s->control_pid);
 
         } else if (streq(key, "control-command")) {
                 SwapExecCommand id;
index bbaa5d313c35cc7fff9ffafee9304658d1a1be82..15866e9b1e87b3534e03a6b519dc0bb15016fef3 100644 (file)
 #include "unit.h"
 
 static const UnitActiveState state_translation_table[_TARGET_STATE_MAX] = {
-        [TARGET_DEAD] = UNIT_INACTIVE,
-        [TARGET_ACTIVE] = UNIT_ACTIVE
+        [TARGET_DEAD]   = UNIT_INACTIVE,
+        [TARGET_ACTIVE] = UNIT_ACTIVE,
 };
 
 static void target_set_state(Target *t, TargetState state) {
         TargetState old_state;
+
         assert(t);
 
         if (t->state != state)
@@ -26,10 +27,8 @@ static void target_set_state(Target *t, TargetState state) {
         t->state = state;
 
         if (state != old_state)
-                log_debug("%s changed %s -> %s",
-                          UNIT(t)->id,
-                          target_state_to_string(old_state),
-                          target_state_to_string(state));
+                log_unit_debug(UNIT(t), "Changed %s -> %s",
+                               target_state_to_string(old_state), target_state_to_string(state));
 
         unit_notify(UNIT(t), state_translation_table[old_state], state_translation_table[state], /* reload_success = */ true);
 }
@@ -56,8 +55,8 @@ static int target_add_default_dependencies(Target *t) {
         if (n_others < 0)
                 return n_others;
 
-        for (int i = 0; i < n_others; i++) {
-                r = unit_add_default_target_dependency(others[i], UNIT(t));
+        FOREACH_ARRAY(i, others, n_others) {
+                r = unit_add_default_target_dependency(*i, UNIT(t));
                 if (r < 0)
                         return r;
         }
index 0c451c020c401583451a25ef17e02352431cf2ce..d7ce4733c01528bd56a37de6fc224dd342c385b0 100644 (file)
 #include "virt.h"
 
 static const UnitActiveState state_translation_table[_TIMER_STATE_MAX] = {
-        [TIMER_DEAD] = UNIT_INACTIVE,
+        [TIMER_DEAD]    = UNIT_INACTIVE,
         [TIMER_WAITING] = UNIT_ACTIVE,
         [TIMER_RUNNING] = UNIT_ACTIVE,
         [TIMER_ELAPSED] = UNIT_ACTIVE,
-        [TIMER_FAILED] = UNIT_FAILED
+        [TIMER_FAILED]  = UNIT_FAILED,
 };
 
 static int timer_dispatch(sd_event_source *s, uint64_t usec, void *userdata);
@@ -993,7 +993,7 @@ static const char* const timer_base_table[_TIMER_BASE_MAX] = {
         [TIMER_STARTUP]       = "OnStartupSec",
         [TIMER_UNIT_ACTIVE]   = "OnUnitActiveSec",
         [TIMER_UNIT_INACTIVE] = "OnUnitInactiveSec",
-        [TIMER_CALENDAR]      = "OnCalendar"
+        [TIMER_CALENDAR]      = "OnCalendar",
 };
 
 DEFINE_STRING_TABLE_LOOKUP(timer_base, TimerBase);
index a81c40fb062a3a14dbb46502d89eb358654bc32f..b5c6348b6fcbc7bef2e7f5a6f94c05f788759174 100644 (file)
@@ -446,10 +446,10 @@ static int transaction_verify_order_one(Transaction *tr, Job *j, Job *from, unsi
          * the graph over 'before' edges in the actual job execution order. We traverse over both unit
          * ordering dependencies and we test with job_compare() whether it is the 'before' edge in the job
          * execution ordering. */
-        for (size_t d = 0; d < ELEMENTSOF(directions); d++) {
+        FOREACH_ARRAY(d, directions, ELEMENTSOF(directions)) {
                 Unit *u;
 
-                UNIT_FOREACH_DEPENDENCY(u, j->unit, directions[d]) {
+                UNIT_FOREACH_DEPENDENCY(u, j->unit, *d) {
                         Job *o;
 
                         /* Is there a job for this unit? */
@@ -463,7 +463,7 @@ static int transaction_verify_order_one(Transaction *tr, Job *j, Job *from, unsi
                         }
 
                         /* Cut traversing if the job j is not really *before* o. */
-                        if (job_compare(j, o, directions[d]) >= 0)
+                        if (job_compare(j, o, *d) >= 0)
                                 continue;
 
                         r = transaction_verify_order_one(tr, o, j, generation, e);
index 9c0794b886737a0b5bff047d5171805c058fd666..f26c1c248eed0dcd8623481b71760948fdd386f5 100644 (file)
 #define NOTICEWORTHY_IP_BYTES     (128 * U64_MB)
 
 const UnitVTable * const unit_vtable[_UNIT_TYPE_MAX] = {
-        [UNIT_SERVICE] = &service_vtable,
-        [UNIT_SOCKET] = &socket_vtable,
-        [UNIT_TARGET] = &target_vtable,
-        [UNIT_DEVICE] = &device_vtable,
-        [UNIT_MOUNT] = &mount_vtable,
+        [UNIT_SERVICE]   = &service_vtable,
+        [UNIT_SOCKET]    = &socket_vtable,
+        [UNIT_TARGET]    = &target_vtable,
+        [UNIT_DEVICE]    = &device_vtable,
+        [UNIT_MOUNT]     = &mount_vtable,
         [UNIT_AUTOMOUNT] = &automount_vtable,
-        [UNIT_SWAP] = &swap_vtable,
-        [UNIT_TIMER] = &timer_vtable,
-        [UNIT_PATH] = &path_vtable,
-        [UNIT_SLICE] = &slice_vtable,
-        [UNIT_SCOPE] = &scope_vtable,
+        [UNIT_SWAP]      = &swap_vtable,
+        [UNIT_TIMER]     = &timer_vtable,
+        [UNIT_PATH]      = &path_vtable,
+        [UNIT_SLICE]     = &slice_vtable,
+        [UNIT_SCOPE]     = &scope_vtable,
 };
 
 Unit* unit_new(Manager *m, size_t size) {
@@ -1249,10 +1249,10 @@ int unit_add_exec_dependencies(Unit *u, ExecContext *c) {
                 if (!u->manager->prefix[dt])
                         continue;
 
-                for (size_t i = 0; i < c->directories[dt].n_items; i++) {
+                FOREACH_ARRAY(i, c->directories[dt].items, c->directories[dt].n_items) {
                         _cleanup_free_ char *p = NULL;
 
-                        p = path_join(u->manager->prefix[dt], c->directories[dt].items[i].path);
+                        p = path_join(u->manager->prefix[dt], i->path);
                         if (!p)
                                 return -ENOMEM;
 
@@ -6311,7 +6311,7 @@ Condition *unit_find_failed_condition(Unit *u) {
 }
 
 static const char* const collect_mode_table[_COLLECT_MODE_MAX] = {
-        [COLLECT_INACTIVE] = "inactive",
+        [COLLECT_INACTIVE]           = "inactive",
         [COLLECT_INACTIVE_OR_FAILED] = "inactive-or-failed",
 };
 
@@ -6461,7 +6461,7 @@ int unit_compare_priority(Unit *a, Unit *b) {
 }
 
 const ActivationDetailsVTable * const activation_details_vtable[_UNIT_TYPE_MAX] = {
-        [UNIT_PATH] = &activation_details_path_vtable,
+        [UNIT_PATH]  = &activation_details_path_vtable,
         [UNIT_TIMER] = &activation_details_timer_vtable,
 };
 
index 0cfd90b3d4c5a174a81379f92f7bae3cf617cd98..74520a33cb30fc465d94690d864e331a3d6f87be 100644 (file)
@@ -64,7 +64,7 @@ static inline bool UNIT_IS_INACTIVE_OR_FAILED(UnitActiveState t) {
 }
 
 static inline bool UNIT_IS_LOAD_COMPLETE(UnitLoadState t) {
-        return t >= 0 && t < _UNIT_LOAD_STATE_MAX && t != UNIT_STUB && t != UNIT_MERGED;
+        return t >= 0 && t < _UNIT_LOAD_STATE_MAX && !IN_SET(t, UNIT_STUB, UNIT_MERGED);
 }
 
 static inline bool UNIT_IS_LOAD_ERROR(UnitLoadState t) {
index fe74c77e4840cca8eb0fb1644c78157aebf7c745..3a3cd7dca2bac36688e6975b7a1ff4428fd9866c 100644 (file)
@@ -95,7 +95,7 @@ static int add_match(sd_journal *j, const char *match) {
         if (field)
                 r = journal_add_match_pair(j, field, match);
         else
-                r = sd_journal_add_match(j, match, 0);
+                r = sd_journal_add_match(j, match, SIZE_MAX);
         if (r < 0)
                 return log_error_errno(r, "Failed to add match \"%s%s%s\": %m",
                                        strempty(field), field ? "=" : "", match);
@@ -106,11 +106,11 @@ static int add_match(sd_journal *j, const char *match) {
 static int add_matches(sd_journal *j, char **matches) {
         int r;
 
-        r = sd_journal_add_match(j, "MESSAGE_ID=" SD_MESSAGE_COREDUMP_STR, 0);
+        r = sd_journal_add_match(j, "MESSAGE_ID=" SD_MESSAGE_COREDUMP_STR, SIZE_MAX);
         if (r < 0)
                 return log_error_errno(r, "Failed to add match \"%s\": %m", "MESSAGE_ID=" SD_MESSAGE_COREDUMP_STR);
 
-        r = sd_journal_add_match(j, "MESSAGE_ID=" SD_MESSAGE_BACKTRACE_STR, 0);
+        r = sd_journal_add_match(j, "MESSAGE_ID=" SD_MESSAGE_BACKTRACE_STR, SIZE_MAX);
         if (r < 0)
                 return log_error_errno(r, "Failed to add match \"%s\": %m", "MESSAGE_ID=" SD_MESSAGE_BACKTRACE_STR);
 
index 6a62df5e3beae8ccca75262703f36d8fc2559483..c1ab0e60d77bd0f93d549499e4ee006b04f2054a 100644 (file)
@@ -60,6 +60,7 @@ static bool arg_pretty = false;
 static bool arg_quiet = false;
 static bool arg_varlink = false;
 static uid_t arg_uid = UID_INVALID;
+static bool arg_allow_null = false;
 
 STATIC_DESTRUCTOR_REGISTER(arg_tpm2_public_key, freep);
 STATIC_DESTRUCTOR_REGISTER(arg_tpm2_signature, freep);
@@ -623,7 +624,7 @@ static int verb_decrypt(int argc, char **argv, void *userdata) {
                                 arg_tpm2_signature,
                                 arg_uid,
                                 &input,
-                                /* flags= */ 0,
+                                arg_allow_null ? CREDENTIAL_ALLOW_NULL : 0,
                                 &plaintext);
         if (r < 0)
                 return r;
@@ -725,7 +726,7 @@ static int verb_help(int argc, char **argv, void *userdata) {
                "     --timestamp=TIME     Include specified timestamp in encrypted credential\n"
                "     --not-after=TIME     Include specified invalidation time in encrypted\n"
                "                          credential\n"
-               "     --with-key=host|tpm2|host+tpm2|tpm2-absent|auto|auto-initrd\n"
+               "     --with-key=host|tpm2|host+tpm2|null|auto|auto-initrd\n"
                "                          Which keys to encrypt with\n"
                "  -H                      Shortcut for --with-key=host\n"
                "  -T                      Shortcut for --with-key=tpm2\n"
@@ -741,6 +742,7 @@ static int verb_help(int argc, char **argv, void *userdata) {
                "                          Specify signature for public key PCR policy\n"
                "     --user               Select user-scoped credential encryption\n"
                "     --uid=UID            Select user for scoped credentials\n"
+               "     --allow-null         Allow decrypting credentials with empty key\n"
                "  -q --quiet              Suppress output for 'has-tpm2' verb\n"
                "\nSee the %2$s for details.\n",
                program_invocation_short_name,
@@ -774,6 +776,7 @@ static int parse_argv(int argc, char *argv[]) {
                 ARG_NOT_AFTER,
                 ARG_USER,
                 ARG_UID,
+                ARG_ALLOW_NULL,
         };
 
         static const struct option options[] = {
@@ -798,6 +801,7 @@ static int parse_argv(int argc, char *argv[]) {
                 { "quiet",                no_argument,       NULL, 'q'                      },
                 { "user",                 no_argument,       NULL, ARG_USER                 },
                 { "uid",                  required_argument, NULL, ARG_UID                  },
+                { "allow-null",           no_argument,       NULL, ARG_ALLOW_NULL           },
                 {}
         };
 
@@ -985,6 +989,10 @@ static int parse_argv(int argc, char *argv[]) {
                         }
                         break;
 
+                case ARG_ALLOW_NULL:
+                        arg_allow_null = true;
+                        break;
+
                 case 'q':
                         arg_quiet = true;
                         break;
index 4900cd536284d7ec6f694ba70fea1d2c494ceae0..c3a01dfb3e54768f5d10b28faf2a70857201a005 100644 (file)
@@ -13,8 +13,8 @@ int acquire_luks2_key(
                 const struct iovec *pubkey,
                 uint32_t pubkey_pcr_mask,
                 const char *signature_path,
-                const char *pcrlock_path,
                 const char *pin,
+                const char *pcrlock_path,
                 uint16_t primary_alg,
                 const struct iovec *key_data,
                 const struct iovec *policy_hash,
index 7099f68f88bce3d3c4367120ee3132c4e6718e1e..34ad9ebd1455e3bdeee63c16291ea397d1563ce5 100644 (file)
@@ -530,7 +530,7 @@ static int parse_one_option(const char *option) {
                  * - text descriptions prefixed with "%:" or "%keyring:".
                  * - text description with no prefix.
                  * - numeric keyring id (ignored in current patch set). */
-                if (*val == '@' || *val == '%')
+                if (IN_SET(*val, '@', '%'))
                         keyring = strndup(val, sep - val);
                 else
                         /* add type prefix if missing (crypt_set_keyring_to_link() expects it) */
index 741b59f32024b3fd720d9eee74b090d6c9df74ee..3526b84dee8ed181ee64eb56bb6136aafba54e61 100644 (file)
@@ -3,13 +3,17 @@
 #include <unistd.h>
 
 #include "alloc-util.h"
+#include "creds-util.h"
 #include "dropin.h"
+#include "errno-util.h"
+#include "fd-util.h"
+#include "fileio-label.h"
 #include "generator.h"
 #include "initrd-util.h"
-#include "mkdir-label.h"
 #include "parse-util.h"
 #include "path-util.h"
 #include "proc-cmdline.h"
+#include "recurse-dir.h"
 #include "special.h"
 #include "string-util.h"
 #include "strv.h"
@@ -141,14 +145,14 @@ static int generate_wants_symlinks(void) {
         return r;
 }
 
-static void install_debug_shell_dropin(const char *dir) {
+static void install_debug_shell_dropin(void) {
         const char *tty = arg_debug_tty ?: arg_default_debug_tty;
         int r;
 
         if (!tty || path_equal(tty, skip_dev_prefix(DEBUGTTY)))
                 return;
 
-        r = write_drop_in_format(dir, "debug-shell.service", 50, "tty",
+        r = write_drop_in_format(arg_dest, "debug-shell.service", 50, "tty",
                         "[Unit]\n"
                         "Description=Early root shell on /dev/%s FOR DEBUGGING ONLY\n"
                         "ConditionPathExists=\n"
@@ -159,8 +163,74 @@ static void install_debug_shell_dropin(const char *dir) {
                 log_warning_errno(r, "Failed to write drop-in for debug-shell.service, ignoring: %m");
 }
 
+static int process_unit_credentials(const char *credentials_dir) {
+        int r;
+
+        assert(credentials_dir);
+
+        _cleanup_free_ DirectoryEntries *des = NULL;
+        r = readdir_all_at(AT_FDCWD, credentials_dir, RECURSE_DIR_SORT|RECURSE_DIR_IGNORE_DOT|RECURSE_DIR_ENSURE_TYPE, &des);
+        if (r < 0)
+                return log_error_errno(r, "Failed to enumerate credentials from credentials directory '%s': %m", credentials_dir);
+
+        FOREACH_ARRAY(i, des->entries, des->n_entries) {
+                _cleanup_free_ void *d = NULL;
+                struct dirent *de = *i;
+                const char *unit, *dropin;
+
+                if (de->d_type != DT_REG)
+                        continue;
+
+                unit = startswith(de->d_name, "systemd.extra-unit.");
+                dropin = startswith(de->d_name, "systemd.unit-dropin.");
+
+                if (!unit && !dropin)
+                        continue;
+
+                if (!unit_name_is_valid(unit ?: dropin, UNIT_NAME_ANY)) {
+                        log_warning("Invalid unit name '%s' in credential '%s', ignoring.",
+                                    unit ?: dropin, de->d_name);
+                        continue;
+                }
+
+                r = read_credential_with_decryption(de->d_name, &d, NULL);
+                if (r < 0)
+                        continue;
+
+                if (unit) {
+                        _cleanup_free_ char *p = NULL;
+
+                        p = path_join(arg_dest, unit);
+                        if (!p)
+                                return log_oom();
+
+                        r = write_string_file_atomic_label(p, d);
+                        if (r < 0) {
+                                log_warning_errno(r, "Failed to write unit file '%s' from credential '%s', ignoring: %m",
+                                                  unit, de->d_name);
+                                continue;
+                        }
+
+                        log_debug("Wrote unit file '%s' from credential '%s'", unit, de->d_name);
+
+                } else {
+                        r = write_drop_in(arg_dest, dropin, 50, "credential", d);
+                        if (r < 0) {
+                                log_warning_errno(r, "Failed to write drop-in for unit '%s' from credential '%s', ignoring: %m",
+                                                  dropin, de->d_name);
+                                continue;
+                        }
+
+                        log_debug("Wrote drop-in for unit '%s' from credential '%s'", dropin, de->d_name);
+                }
+        }
+
+        return 0;
+}
+
 static int run(const char *dest, const char *dest_early, const char *dest_late) {
-        int r, q;
+        const char *credentials_dir;
+        int r = 0;
 
         assert_se(arg_dest = dest_early);
 
@@ -173,13 +243,19 @@ static int run(const char *dest, const char *dest_early, const char *dest_late)
                 if (r < 0)
                         return log_oom();
 
-                install_debug_shell_dropin(arg_dest);
+                install_debug_shell_dropin();
         }
 
-        r = generate_mask_symlinks();
-        q = generate_wants_symlinks();
+        if (get_credentials_dir(&credentials_dir) >= 0)
+                RET_GATHER(r, process_unit_credentials(credentials_dir));
+
+        if (get_encrypted_credentials_dir(&credentials_dir) >= 0)
+                RET_GATHER(r, process_unit_credentials(credentials_dir));
 
-        return r < 0 ? r : q;
+        RET_GATHER(r, generate_mask_symlinks());
+        RET_GATHER(r, generate_wants_symlinks());
+
+        return r;
 }
 
 DEFINE_MAIN_GENERATOR_FUNCTION(run);
index e4cc53609219a54fa276d04e2ac5b1c63329d2a8..d68c29cbf1c5d2ae1f00e1d3841191766a602191 100644 (file)
 #include "log.h"
 #include "loop-util.h"
 #include "main-func.h"
+#include "missing_syscall.h"
 #include "mkdir.h"
 #include "mount-util.h"
 #include "mountpoint-util.h"
 #include "namespace-util.h"
+#include "nsresource.h"
 #include "parse-argument.h"
 #include "parse-util.h"
 #include "path-util.h"
@@ -91,6 +93,7 @@ static char **arg_argv = NULL;
 static char *arg_loop_ref = NULL;
 static ImagePolicy *arg_image_policy = NULL;
 static bool arg_mtree_hash = true;
+static bool arg_via_service = false;
 
 STATIC_DESTRUCTOR_REGISTER(arg_image, freep);
 STATIC_DESTRUCTOR_REGISTER(arg_root, freep);
@@ -704,6 +707,18 @@ static int parse_argv(int argc, char *argv[]) {
                 assert_not_reached();
         }
 
+        r = getenv_bool("SYSTEMD_USE_MOUNTFSD");
+        if (r < 0) {
+                if (r != -ENXIO)
+                        return log_error_errno(r, "Failed to parse $SYSTEMD_USE_MOUNTFSD: %m");
+        } else
+                arg_via_service = r;
+
+        if (!IN_SET(arg_action, ACTION_DISSECT, ACTION_LIST, ACTION_MTREE, ACTION_COPY_FROM, ACTION_COPY_TO, ACTION_DISCOVER, ACTION_VALIDATE) && geteuid() != 0)
+                return log_error_errno(SYNTHETIC_ERRNO(EPERM), "Need to be root.");
+
+        SET_FLAG(arg_flags, DISSECT_IMAGE_ALLOW_INTERACTIVE_AUTH, isatty(STDIN_FILENO));
+
         return 1;
 }
 
@@ -841,7 +856,11 @@ static int get_extension_scopes(DissectedImage *m, ImageClass class, char ***ret
         return 1;
 }
 
-static int action_dissect(DissectedImage *m, LoopDevice *d) {
+static int action_dissect(
+                DissectedImage *m,
+                LoopDevice *d,
+                int userns_fd) {
+
         _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
         _cleanup_(table_unrefp) Table *t = NULL;
         _cleanup_free_ char *bn = NULL;
@@ -849,7 +868,6 @@ static int action_dissect(DissectedImage *m, LoopDevice *d) {
         int r;
 
         assert(m);
-        assert(d);
 
         r = path_extract_filename(arg_image, &bn);
         if (r < 0)
@@ -875,7 +893,7 @@ static int action_dissect(DissectedImage *m, LoopDevice *d) {
                 fflush(stdout);
         }
 
-        r = dissected_image_acquire_metadata(m, 0);
+        r = dissected_image_acquire_metadata(m, userns_fd, /* extra_flags= */ 0);
         if (r == -ENXIO)
                 return log_error_errno(r, "No root partition discovered.");
         if (r == -EUCLEAN)
@@ -1087,7 +1105,6 @@ static int action_mount(DissectedImage *m, LoopDevice *d) {
         int r;
 
         assert(m);
-        assert(d);
         assert(arg_action == ACTION_MOUNT);
 
         r = dissected_image_mount_and_warn(
@@ -1100,9 +1117,11 @@ static int action_mount(DissectedImage *m, LoopDevice *d) {
         if (r < 0)
                 return r;
 
-        r = loop_device_flock(d, LOCK_UN);
-        if (r < 0)
-                return log_error_errno(r, "Failed to unlock loopback block device: %m");
+        if (d) {
+                r = loop_device_flock(d, LOCK_UN);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to unlock loopback block device: %m");
+        }
 
         r = dissected_image_relinquish(m);
         if (r < 0)
@@ -1390,8 +1409,8 @@ static int archive_item(
 }
 #endif
 
-static int action_list_or_mtree_or_copy_or_make_archive(DissectedImage *m, LoopDevice *d) {
-        _cleanup_(umount_and_rmdir_and_freep) char *mounted_dir = NULL;
+static int action_list_or_mtree_or_copy_or_make_archive(DissectedImage *m, LoopDevice *d, int userns_fd) {
+        _cleanup_(umount_and_freep) char *mounted_dir = NULL;
         _cleanup_free_ char *t = NULL;
         const char *root;
         int r;
@@ -1400,12 +1419,16 @@ static int action_list_or_mtree_or_copy_or_make_archive(DissectedImage *m, LoopD
 
         if (arg_image) {
                 assert(m);
-                assert(d);
 
-                r = detach_mount_namespace();
+                if (userns_fd < 0)
+                        r = detach_mount_namespace_harder(0, 0);
+                else
+                        r = detach_mount_namespace_userns(userns_fd);
                 if (r < 0)
                         return log_error_errno(r, "Failed to detach mount namespace: %m");
 
+                /* Create a place we can mount things onto soon. We use a fixed path shared by all invocations. Given
+                 * the mounts are done in a mount namespace there's not going to be a collision here */
                 r = get_common_dissect_directory(&t);
                 if (r < 0)
                         return log_error_errno(r, "Failed generate private mount directory: %m");
@@ -1422,9 +1445,11 @@ static int action_list_or_mtree_or_copy_or_make_archive(DissectedImage *m, LoopD
 
                 mounted_dir = TAKE_PTR(t);
 
-                r = loop_device_flock(d, LOCK_UN);
-                if (r < 0)
-                        return log_error_errno(r, "Failed to unlock loopback block device: %m");
+                if (d) {
+                        r = loop_device_flock(d, LOCK_UN);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to unlock loopback block device: %m");
+                }
 
                 r = dissected_image_relinquish(m);
                 if (r < 0)
@@ -1433,6 +1458,8 @@ static int action_list_or_mtree_or_copy_or_make_archive(DissectedImage *m, LoopD
 
         root = mounted_dir ?: arg_root;
 
+        dissected_image_close(m);
+
         switch (arg_action) {
 
         case ACTION_COPY_FROM: {
@@ -1716,7 +1743,6 @@ static int action_with(DissectedImage *m, LoopDevice *d) {
         int r, rcode;
 
         assert(m);
-        assert(d);
         assert(arg_action == ACTION_WITH);
 
         r = tempfn_random_child(NULL, program_invocation_short_name, &temp);
@@ -1745,9 +1771,11 @@ static int action_with(DissectedImage *m, LoopDevice *d) {
         if (r < 0)
                 return log_error_errno(r, "Failed to relinquish DM and loopback block devices: %m");
 
-        r = loop_device_flock(d, LOCK_UN);
-        if (r < 0)
-                return log_error_errno(r, "Failed to unlock loopback block device: %m");
+        if (d) {
+                r = loop_device_flock(d, LOCK_UN);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to unlock loopback block device: %m");
+        }
 
         rcode = safe_fork("(with)", FORK_CLOSE_ALL_FDS|FORK_LOG|FORK_WAIT, NULL);
         if (rcode == 0) {
@@ -1788,14 +1816,16 @@ static int action_with(DissectedImage *m, LoopDevice *d) {
         }
 
         /* Let's manually detach everything, to make things synchronous */
-        r = loop_device_flock(d, LOCK_SH);
-        if (r < 0)
-                log_warning_errno(r, "Failed to lock loopback block device, ignoring: %m");
+        if (d) {
+                r = loop_device_flock(d, LOCK_SH);
+                if (r < 0)
+                        log_warning_errno(r, "Failed to lock loopback block device, ignoring: %m");
+        }
 
         r = umount_recursive(mounted_dir, 0);
         if (r < 0)
                 log_warning_errno(r, "Failed to unmount '%s', ignoring: %m", mounted_dir);
-        else
+        else if (d)
                 loop_device_unrelinquish(d); /* Let's try to destroy the loopback device */
 
         created_dir = TAKE_PTR(mounted_dir);
@@ -1982,8 +2012,8 @@ static int action_validate(void) {
 static int run(int argc, char *argv[]) {
         _cleanup_(dissected_image_unrefp) DissectedImage *m = NULL;
         _cleanup_(loop_device_unrefp) LoopDevice *d = NULL;
-        uint32_t loop_flags;
-        int open_flags, r;
+        _cleanup_close_ int userns_fd = -EBADF;
+        int r;
 
         log_setup();
 
@@ -2015,7 +2045,7 @@ static int run(int argc, char *argv[]) {
                 return action_discover();
 
         default:
-                /* All other actions need the image dissected */
+                /* All other actions need the image dissected (except for ACTION_VALIDATE, see below) */
                 break;
         }
 
@@ -2031,50 +2061,92 @@ static int run(int argc, char *argv[]) {
                                                                         * hence if there's external Verity data
                                                                         * available we turn off partition table
                                                                         * support */
+        }
 
-                if (arg_action == ACTION_VALIDATE)
-                        return action_validate();
+        if (arg_action == ACTION_VALIDATE)
+                return action_validate();
 
-                open_flags = FLAGS_SET(arg_flags, DISSECT_IMAGE_DEVICE_READ_ONLY) ? O_RDONLY : O_RDWR;
-                loop_flags = FLAGS_SET(arg_flags, DISSECT_IMAGE_NO_PARTITION_TABLE) ? 0 : LO_FLAGS_PARTSCAN;
-                if (arg_in_memory)
-                        r = loop_device_make_by_path_memory(arg_image, open_flags, /* sector_size= */ UINT32_MAX, loop_flags, LOCK_SH, &d);
-                else
-                        r = loop_device_make_by_path(arg_image, open_flags, /* sector_size= */ UINT32_MAX, loop_flags, LOCK_SH, &d);
-                if (r < 0)
-                        return log_error_errno(r, "Failed to set up loopback device for %s: %m", arg_image);
+        if (arg_image) {
+                /* First try locally, if we are allowed to */
+                if (!arg_via_service) {
+                        uint32_t loop_flags;
+                        int open_flags;
+
+                        open_flags = FLAGS_SET(arg_flags, DISSECT_IMAGE_DEVICE_READ_ONLY) ? O_RDONLY : O_RDWR;
+                        loop_flags = FLAGS_SET(arg_flags, DISSECT_IMAGE_NO_PARTITION_TABLE) ? 0 : LO_FLAGS_PARTSCAN;
+
+                        if (arg_in_memory)
+                                r = loop_device_make_by_path_memory(arg_image, open_flags, /* sector_size= */ UINT32_MAX, loop_flags, LOCK_SH, &d);
+                        else
+                                r = loop_device_make_by_path(arg_image, open_flags, /* sector_size= */ UINT32_MAX, loop_flags, LOCK_SH, &d);
+                        if (r < 0) {
+                                if (!ERRNO_IS_PRIVILEGE(r) || !IN_SET(arg_action, ACTION_DISSECT, ACTION_LIST, ACTION_MTREE, ACTION_COPY_FROM, ACTION_COPY_TO))
+                                        return log_error_errno(r, "Failed to set up loopback device for %s: %m", arg_image);
 
-                if (arg_loop_ref) {
-                        r = loop_device_set_filename(d, arg_loop_ref);
-                        if (r < 0)
-                                log_warning_errno(r, "Failed to set loop reference string to '%s', ignoring: %m", arg_loop_ref);
+                                log_debug_errno(r, "Lacking permissions to set up loopback block device for %s, using service: %m", arg_image);
+                                arg_via_service = true;
+                        } else {
+                                if (arg_loop_ref) {
+                                        r = loop_device_set_filename(d, arg_loop_ref);
+                                        if (r < 0)
+                                                log_warning_errno(r, "Failed to set loop reference string to '%s', ignoring: %m", arg_loop_ref);
+                                }
+
+                                r = dissect_loop_device_and_warn(
+                                                d,
+                                                &arg_verity_settings,
+                                                /* mount_options= */ NULL,
+                                                arg_image_policy,
+                                                arg_flags,
+                                                &m);
+                                if (r < 0)
+                                        return r;
+
+                                if (arg_action == ACTION_ATTACH)
+                                        return action_attach(m, d);
+
+                                r = dissected_image_load_verity_sig_partition(
+                                                m,
+                                                d->fd,
+                                                &arg_verity_settings);
+                                if (r < 0)
+                                        return log_error_errno(r, "Failed to load verity signature partition: %m");
+
+                                if (arg_action != ACTION_DISSECT) {
+                                        r = dissected_image_decrypt_interactively(
+                                                        m, NULL,
+                                                        &arg_verity_settings,
+                                                        arg_flags);
+                                        if (r < 0)
+                                                return r;
+                                }
+                        }
                 }
 
-                r = dissect_loop_device_and_warn(
-                                d,
-                                &arg_verity_settings,
-                                /* mount_options= */ NULL,
-                                arg_image_policy,
-                                arg_flags,
-                                &m);
-                if (r < 0)
-                        return r;
+                /* Try via service */
+                if (arg_via_service) {
+                        if (arg_in_memory)
+                                return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "--in-memory= not supported when operating via systemd-mountfsd.");
 
-                if (arg_action == ACTION_ATTACH)
-                        return action_attach(m, d);
+                        if (arg_loop_ref)
+                                return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "--loop-ref= not supported when operating via systemd-mountfsd.");
 
-                r = dissected_image_load_verity_sig_partition(
-                                m,
-                                d->fd,
-                                &arg_verity_settings);
-                if (r < 0)
-                        return log_error_errno(r, "Failed to load verity signature partition: %m");
+                        if (verity_settings_set(&arg_verity_settings))
+                                return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Externally configured verity settings not supported when operating via systemd-mountfsd.");
+
+                        /* Don't run things in private userns, if the mount shall be attached to the host */
+                        if (!IN_SET(arg_action, ACTION_MOUNT, ACTION_WITH)) {
+                                userns_fd = nsresource_allocate_userns(/* name= */ NULL, UINT64_C(0x10000)); /* allocate 64K users by default */
+                                if (userns_fd < 0)
+                                        return log_error_errno(userns_fd, "Failed to allocate user namespace with 64K users: %m");
+                        }
 
-                if (arg_action != ACTION_DISSECT) {
-                        r = dissected_image_decrypt_interactively(
-                                        m, NULL,
-                                        &arg_verity_settings,
-                                        arg_flags);
+                        r = mountfsd_mount_image(
+                                        arg_image,
+                                        userns_fd,
+                                        arg_image_policy,
+                                        arg_flags,
+                                        &m);
                         if (r < 0)
                                 return r;
                 }
@@ -2083,7 +2155,7 @@ static int run(int argc, char *argv[]) {
         switch (arg_action) {
 
         case ACTION_DISSECT:
-                return action_dissect(m, d);
+                return action_dissect(m, d, userns_fd);
 
         case ACTION_MOUNT:
                 return action_mount(m, d);
@@ -2093,7 +2165,7 @@ static int run(int argc, char *argv[]) {
         case ACTION_COPY_FROM:
         case ACTION_COPY_TO:
         case ACTION_MAKE_ARCHIVE:
-                return action_list_or_mtree_or_copy_or_make_archive(m, d);
+                return action_list_or_mtree_or_copy_or_make_archive(m, d, userns_fd);
 
         case ACTION_WITH:
                 return action_with(m, d);
index c6db83a9d3b0ca26d6c29f90cacc35ff8310c4e1..c7ed1bc8d73682b6e25e3b98bef772de9080d49c 100644 (file)
@@ -28,20 +28,7 @@ static KernelHibernateLocation* kernel_hibernate_location_free(KernelHibernateLo
 
 DEFINE_TRIVIAL_CLEANUP_FUNC(KernelHibernateLocation*, kernel_hibernate_location_free);
 
-typedef struct EFIHibernateLocation {
-        char *device;
-
-        sd_id128_t uuid;
-        uint64_t offset;
-
-        char *kernel_version;
-        char *id;
-        char *image_id;
-        char *version_id;
-        char *image_version;
-} EFIHibernateLocation;
-
-static EFIHibernateLocation* efi_hibernate_location_free(EFIHibernateLocation *e) {
+EFIHibernateLocation* efi_hibernate_location_free(EFIHibernateLocation *e) {
         if (!e)
                 return NULL;
 
@@ -55,8 +42,6 @@ static EFIHibernateLocation* efi_hibernate_location_free(EFIHibernateLocation *e
         return mfree(e);
 }
 
-DEFINE_TRIVIAL_CLEANUP_FUNC(EFIHibernateLocation*, efi_hibernate_location_free);
-
 void hibernate_info_done(HibernateInfo *info) {
         assert(info);
 
@@ -140,7 +125,7 @@ static bool validate_efi_hibernate_location(EFIHibernateLocation *e) {
 
         if (!streq_ptr(id, e->id) ||
             !streq_ptr(image_id, e->image_id)) {
-                log_notice("HibernateLocation system identifier doesn't match currently running system, not resuming from it.");
+                log_notice("HibernateLocation system identifier doesn't match currently running system, would not resume from it.");
                 return false;
         }
 
@@ -152,9 +137,10 @@ static bool validate_efi_hibernate_location(EFIHibernateLocation *e) {
 
         return true;
 }
+#endif
 
-static int get_efi_hibernate_location(EFIHibernateLocation **ret) {
-
+int get_efi_hibernate_location(EFIHibernateLocation **ret) {
+#if ENABLE_EFI
         static const JsonDispatch dispatch_table[] = {
                 { "uuid",                  JSON_VARIANT_STRING,        json_dispatch_id128,  offsetof(EFIHibernateLocation, uuid),           JSON_MANDATORY             },
                 { "offset",                _JSON_VARIANT_TYPE_INVALID, json_dispatch_uint64, offsetof(EFIHibernateLocation, offset),         JSON_MANDATORY             },
@@ -171,8 +157,6 @@ static int get_efi_hibernate_location(EFIHibernateLocation **ret) {
         _cleanup_free_ char *location_str = NULL;
         int r;
 
-        assert(ret);
-
         if (!is_efi_boot())
                 goto skip;
 
@@ -211,15 +195,19 @@ static int get_efi_hibernate_location(EFIHibernateLocation **ret) {
         if (asprintf(&e->device, "/dev/disk/by-uuid/" SD_ID128_UUID_FORMAT_STR, SD_ID128_FORMAT_VAL(e->uuid)) < 0)
                 return log_oom();
 
-        *ret = TAKE_PTR(e);
+        if (ret)
+                *ret = TAKE_PTR(e);
         return 1;
 
 skip:
-        *ret = NULL;
+#endif
+        if (ret)
+                *ret = NULL;
         return 0;
 }
 
 void compare_hibernate_location_and_warn(const HibernateInfo *info) {
+#if ENABLE_EFI
         int r;
 
         assert(info);
@@ -243,8 +231,8 @@ void compare_hibernate_location_and_warn(const HibernateInfo *info) {
         if (info->cmdline->offset != info->efi->offset)
                 log_warning("resume_offset=%" PRIu64 " doesn't match with EFI HibernateLocation offset %" PRIu64 ", proceeding anyway with resume_offset=.",
                             info->cmdline->offset, info->efi->offset);
-}
 #endif
+}
 
 int acquire_hibernate_info(HibernateInfo *ret) {
         _cleanup_(hibernate_info_done) HibernateInfo i = {};
@@ -254,11 +242,9 @@ int acquire_hibernate_info(HibernateInfo *ret) {
         if (r < 0)
                 return r;
 
-#if ENABLE_EFI
         r = get_efi_hibernate_location(&i.efi);
         if (r < 0)
                 return r;
-#endif
 
         if (i.cmdline) {
                 i.device = i.cmdline->device;
index c0aa9e00ad1348f53b51351124849ddb6995be46..68ef0753559adb3ea48fabdcac5e15ef07ff5e66 100644 (file)
@@ -5,8 +5,27 @@
 
 #include "sd-id128.h"
 
+#include "macro.h"
+
 typedef struct KernelHibernateLocation KernelHibernateLocation;
-typedef struct EFIHibernateLocation EFIHibernateLocation;
+
+typedef struct EFIHibernateLocation {
+        char *device;
+
+        sd_id128_t uuid;
+        uint64_t offset;
+
+        char *kernel_version;
+        char *id;
+        char *image_id;
+        char *version_id;
+        char *image_version;
+} EFIHibernateLocation;
+
+EFIHibernateLocation* efi_hibernate_location_free(EFIHibernateLocation *e);
+DEFINE_TRIVIAL_CLEANUP_FUNC(EFIHibernateLocation*, efi_hibernate_location_free);
+
+int get_efi_hibernate_location(EFIHibernateLocation **ret);
 
 typedef struct HibernateInfo {
         const char *device;
@@ -20,14 +39,4 @@ void hibernate_info_done(HibernateInfo *info);
 
 int acquire_hibernate_info(HibernateInfo *ret);
 
-#if ENABLE_EFI
-
 void compare_hibernate_location_and_warn(const HibernateInfo *info);
-
-#else
-
-static inline void compare_hibernate_location_and_warn(const HibernateInfo *info) {
-        return;
-}
-
-#endif
index 3f45836d591b127cfc44af4249ef4c7bc191a69b..c6494b99a91582b5d90d5e90fc6517144ff4fc73 100644 (file)
@@ -1,8 +1,10 @@
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 
 #include <errno.h>
+#include <getopt.h>
 #include <sys/stat.h>
 
+#include "build.h"
 #include "devnum-util.h"
 #include "hibernate-resume-config.h"
 #include "hibernate-util.h"
 #include "log.h"
 #include "main-func.h"
 #include "parse-util.h"
+#include "pretty-print.h"
 #include "static-destruct.h"
+#include "terminal-util.h"
 
 static HibernateInfo arg_info = {};
+static bool arg_clear = false;
 
 STATIC_DESTRUCTOR_REGISTER(arg_info, hibernate_info_done);
 
+static int help(void) {
+        _cleanup_free_ char *link = NULL;
+        int r;
+
+        r = terminal_urlify_man("systemd-hibernate-resume", "8", &link);
+        if (r < 0)
+                return log_oom();
+
+        printf("%s [OPTIONS...] [DEVICE [OFFSET]]\n"
+               "\n%sInitiate resume from hibernation.%s\n\n"
+               "  -h --help            Show this help\n"
+               "     --version         Show package version\n"
+               "     --clear           Clear hibernation storage information from EFI and exit\n"
+               "\nSee the %s for details.\n",
+               program_invocation_short_name,
+               ansi_highlight(),
+               ansi_normal(),
+               link);
+
+        return 0;
+}
+
+static int parse_argv(int argc, char *argv[]) {
+
+        enum {
+                ARG_VERSION = 0x100,
+                ARG_CLEAR,
+        };
+
+        static const struct option options[] = {
+                { "help",      no_argument,       NULL, 'h'           },
+                { "version",   no_argument,       NULL, ARG_VERSION   },
+                { "clear",     no_argument,       NULL, ARG_CLEAR     },
+                {}
+        };
+
+        int c;
+
+        assert(argc >= 0);
+        assert(argv);
+
+        while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
+
+                switch (c) {
+
+                case 'h':
+                        return help();
+
+                case ARG_VERSION:
+                        return version();
+
+                case ARG_CLEAR:
+                        arg_clear = true;
+                        break;
+
+                case '?':
+                        return -EINVAL;
+
+                default:
+                        assert_not_reached();
+                }
+
+        if (argc > optind && arg_clear)
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+                                       "Extraneous arguments specified with --clear, refusing.");
+
+        return 1;
+}
+
 static int setup_hibernate_info_and_warn(void) {
         int r;
 
@@ -32,42 +106,68 @@ static int setup_hibernate_info_and_warn(void) {
         return 1;
 }
 
+static int action_clear(void) {
+        int r;
+
+        assert(arg_clear);
+
+        /* Let's insist that the system identifier is verified still. After all if things don't match,
+         * the resume wouldn't get triggered in the first place. We should not erase the var if booted
+         * from LiveCD/portable systems/... */
+        r = get_efi_hibernate_location(/* ret = */ NULL);
+        if (r <= 0)
+                return r;
+
+        r = clear_efi_hibernate_location_and_warn();
+        if (r > 0)
+                log_notice("Successfully cleared HibernateLocation EFI variable.");
+        return r;
+}
+
 static int run(int argc, char *argv[]) {
         struct stat st;
         int r;
 
         log_setup();
 
-        if (argc < 1 || argc > 3)
+        r = parse_argv(argc, argv);
+        if (r <= 0)
+                return r;
+
+        if (argc - optind > 2)
                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "This program expects zero, one, or two arguments.");
 
         umask(0022);
 
-        if (!in_initrd())
-                return 0;
+        if (arg_clear)
+                return action_clear();
 
-        if (argc > 1) {
-                arg_info.device = argv[1];
+        if (!in_initrd())
+                return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
+                                       "Not running in initrd, refusing to initiate resume from hibernation.");
 
-                if (argc == 3) {
-                        r = safe_atou64(argv[2], &arg_info.offset);
-                        if (r < 0)
-                                return log_error_errno(r, "Failed to parse resume offset %s: %m", argv[2]);
-                }
-        } else {
+        if (argc <= optind) {
                 r = setup_hibernate_info_and_warn();
                 if (r <= 0)
                         return r;
 
                 if (arg_info.efi)
-                        clear_efi_hibernate_location_and_warn();
+                        (void) clear_efi_hibernate_location_and_warn();
+        } else {
+                arg_info.device = ASSERT_PTR(argv[optind]);
+
+                if (argc - optind == 2) {
+                        r = safe_atou64(argv[optind + 1], &arg_info.offset);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to parse resume offset %s: %m", argv[optind + 1]);
+                }
         }
 
         if (stat(arg_info.device, &st) < 0)
                 return log_error_errno(errno, "Failed to stat resume device '%s': %m", arg_info.device);
 
         if (!S_ISBLK(st.st_mode))
-                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+                return log_error_errno(SYNTHETIC_ERRNO(ENOTBLK),
                                        "Resume device '%s' is not a block device.", arg_info.device);
 
         /* The write shall not return if a resume takes place. */
index 2a7917d7f9f846e53ee90a640d345f14a24f54b4..d2abc25b9961a58f267483e3c2f74637a89682f6 100644 (file)
@@ -2810,102 +2810,102 @@ static int parse_argv(int argc, char *argv[]) {
         };
 
         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                   },
-                { "no-ask-password",             no_argument,       NULL, ARG_NO_ASK_PASSWORD             },
-                { "offline",                     no_argument,       NULL, ARG_OFFLINE                     },
-                { "host",                        required_argument, NULL, 'H'                             },
-                { "machine",                     required_argument, NULL, 'M'                             },
-                { "identity",                    required_argument, NULL, 'I'                             },
-                { "real-name",                   required_argument, NULL, 'c'                             },
-                { "comment",                     required_argument, NULL, 'c'                             }, /* Compat alias to keep thing in sync with useradd(8) */
-                { "realm",                       required_argument, NULL, ARG_REALM                       },
-                { "email-address",               required_argument, NULL, ARG_EMAIL_ADDRESS               },
-                { "location",                    required_argument, NULL, ARG_LOCATION                    },
-                { "password-hint",               required_argument, NULL, ARG_PASSWORD_HINT               },
-                { "icon-name",                   required_argument, NULL, ARG_ICON_NAME                   },
-                { "home-dir",                    required_argument, NULL, 'd'                             }, /* Compatible with useradd(8) */
-                { "uid",                         required_argument, NULL, 'u'                             }, /* Compatible with useradd(8) */
-                { "member-of",                   required_argument, NULL, 'G'                             },
-                { "groups",                      required_argument, NULL, 'G'                             }, /* Compat alias to keep thing in sync with useradd(8) */
-                { "skel",                        required_argument, NULL, 'k'                             }, /* Compatible with useradd(8) */
-                { "shell",                       required_argument, NULL, 's'                             }, /* Compatible with useradd(8) */
-                { "setenv",                      required_argument, NULL, ARG_SETENV                      },
-                { "timezone",                    required_argument, NULL, ARG_TIMEZONE                    },
-                { "language",                    required_argument, NULL, ARG_LANGUAGE                    },
-                { "locked",                      required_argument, NULL, ARG_LOCKED                      },
-                { "not-before",                  required_argument, NULL, ARG_NOT_BEFORE                  },
-                { "not-after",                   required_argument, NULL, ARG_NOT_AFTER                   },
-                { "expiredate",                  required_argument, NULL, 'e'                             }, /* Compat alias to keep thing in sync with useradd(8) */
-                { "ssh-authorized-keys",         required_argument, NULL, ARG_SSH_AUTHORIZED_KEYS         },
-                { "disk-size",                   required_argument, NULL, ARG_DISK_SIZE                   },
-                { "access-mode",                 required_argument, NULL, ARG_ACCESS_MODE                 },
-                { "umask",                       required_argument, NULL, ARG_UMASK                       },
-                { "nice",                        required_argument, NULL, ARG_NICE                        },
-                { "rlimit",                      required_argument, NULL, ARG_RLIMIT                      },
-                { "tasks-max",                   required_argument, NULL, ARG_TASKS_MAX                   },
-                { "memory-high",                 required_argument, NULL, ARG_MEMORY_HIGH                 },
-                { "memory-max",                  required_argument, NULL, ARG_MEMORY_MAX                  },
-                { "cpu-weight",                  required_argument, NULL, ARG_CPU_WEIGHT                  },
-                { "io-weight",                   required_argument, NULL, ARG_IO_WEIGHT                   },
-                { "storage",                     required_argument, NULL, ARG_STORAGE                     },
-                { "image-path",                  required_argument, NULL, ARG_IMAGE_PATH                  },
-                { "fs-type",                     required_argument, NULL, ARG_FS_TYPE                     },
-                { "luks-discard",                required_argument, NULL, ARG_LUKS_DISCARD                },
-                { "luks-offline-discard",        required_argument, NULL, ARG_LUKS_OFFLINE_DISCARD        },
-                { "luks-cipher",                 required_argument, NULL, ARG_LUKS_CIPHER                 },
-                { "luks-cipher-mode",            required_argument, NULL, ARG_LUKS_CIPHER_MODE            },
-                { "luks-volume-key-size",        required_argument, NULL, ARG_LUKS_VOLUME_KEY_SIZE        },
-                { "luks-pbkdf-type",             required_argument, NULL, ARG_LUKS_PBKDF_TYPE             },
-                { "luks-pbkdf-hash-algorithm",   required_argument, NULL, ARG_LUKS_PBKDF_HASH_ALGORITHM   },
-                { "luks-pbkdf-force-iterations", required_argument, NULL, ARG_LUKS_PBKDF_FORCE_ITERATIONS },
-                { "luks-pbkdf-time-cost",        required_argument, NULL, ARG_LUKS_PBKDF_TIME_COST        },
-                { "luks-pbkdf-memory-cost",      required_argument, NULL, ARG_LUKS_PBKDF_MEMORY_COST      },
-                { "luks-pbkdf-parallel-threads", required_argument, NULL, ARG_LUKS_PBKDF_PARALLEL_THREADS },
-                { "luks-sector-size",            required_argument, NULL, ARG_LUKS_SECTOR_SIZE            },
-                { "nosuid",                      required_argument, NULL, ARG_NOSUID                      },
-                { "nodev",                       required_argument, NULL, ARG_NODEV                       },
-                { "noexec",                      required_argument, NULL, ARG_NOEXEC                      },
-                { "cifs-user-name",              required_argument, NULL, ARG_CIFS_USER_NAME              },
-                { "cifs-domain",                 required_argument, NULL, ARG_CIFS_DOMAIN                 },
-                { "cifs-service",                required_argument, NULL, ARG_CIFS_SERVICE                },
-                { "cifs-extra-mount-options",    required_argument, NULL, ARG_CIFS_EXTRA_MOUNT_OPTIONS    },
-                { "rate-limit-interval",         required_argument, NULL, ARG_RATE_LIMIT_INTERVAL         },
-                { "rate-limit-burst",            required_argument, NULL, ARG_RATE_LIMIT_BURST            },
-                { "stop-delay",                  required_argument, NULL, ARG_STOP_DELAY                  },
-                { "kill-processes",              required_argument, NULL, ARG_KILL_PROCESSES              },
-                { "enforce-password-policy",     required_argument, NULL, ARG_ENFORCE_PASSWORD_POLICY     },
-                { "password-change-now",         required_argument, NULL, ARG_PASSWORD_CHANGE_NOW         },
-                { "password-change-min",         required_argument, NULL, ARG_PASSWORD_CHANGE_MIN         },
-                { "password-change-max",         required_argument, NULL, ARG_PASSWORD_CHANGE_MAX         },
-                { "password-change-warn",        required_argument, NULL, ARG_PASSWORD_CHANGE_WARN        },
-                { "password-change-inactive",    required_argument, NULL, ARG_PASSWORD_CHANGE_INACTIVE    },
-                { "auto-login",                  required_argument, NULL, ARG_AUTO_LOGIN                  },
-                { "session-launcher",            required_argument, NULL, ARG_SESSION_LAUNCHER,           },
-                { "session-type",                required_argument, NULL, ARG_SESSION_TYPE,               },
-                { "json",                        required_argument, NULL, ARG_JSON                        },
-                { "export-format",               required_argument, NULL, ARG_EXPORT_FORMAT               },
-                { "pkcs11-token-uri",            required_argument, NULL, ARG_PKCS11_TOKEN_URI            },
-                { "fido2-credential-algorithm",  required_argument, NULL, ARG_FIDO2_CRED_ALG              },
-                { "fido2-device",                required_argument, NULL, ARG_FIDO2_DEVICE                },
-                { "fido2-with-client-pin",       required_argument, NULL, ARG_FIDO2_WITH_PIN              },
-                { "fido2-with-user-presence",    required_argument, NULL, ARG_FIDO2_WITH_UP               },
-                { "fido2-with-user-verification",required_argument, NULL, ARG_FIDO2_WITH_UV               },
-                { "recovery-key",                required_argument, NULL, ARG_RECOVERY_KEY                },
-                { "and-resize",                  required_argument, NULL, ARG_AND_RESIZE                  },
-                { "and-change-password",         required_argument, NULL, ARG_AND_CHANGE_PASSWORD         },
-                { "drop-caches",                 required_argument, NULL, ARG_DROP_CACHES                 },
-                { "luks-extra-mount-options",    required_argument, NULL, ARG_LUKS_EXTRA_MOUNT_OPTIONS    },
-                { "auto-resize-mode",            required_argument, NULL, ARG_AUTO_RESIZE_MODE            },
-                { "rebalance-weight",            required_argument, NULL, ARG_REBALANCE_WEIGHT            },
-                { "capability-bounding-set",     required_argument, NULL, ARG_CAPABILITY_BOUNDING_SET     },
-                { "capability-ambient-set",      required_argument, NULL, ARG_CAPABILITY_AMBIENT_SET      },
-                { "prompt-new-user",             no_argument,       NULL, ARG_PROMPT_NEW_USER             },
-                { "blob",                        required_argument, NULL, 'b'                             },
-                { "avatar",                      required_argument, NULL, ARG_AVATAR                      },
-                { "login-background",            required_argument, NULL, ARG_LOGIN_BACKGROUND            },
+                { "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                   },
+                { "no-ask-password",              no_argument,       NULL, ARG_NO_ASK_PASSWORD             },
+                { "offline",                      no_argument,       NULL, ARG_OFFLINE                     },
+                { "host",                         required_argument, NULL, 'H'                             },
+                { "machine",                      required_argument, NULL, 'M'                             },
+                { "identity",                     required_argument, NULL, 'I'                             },
+                { "real-name",                    required_argument, NULL, 'c'                             },
+                { "comment",                      required_argument, NULL, 'c'                             }, /* Compat alias to keep thing in sync with useradd(8) */
+                { "realm",                        required_argument, NULL, ARG_REALM                       },
+                { "email-address",                required_argument, NULL, ARG_EMAIL_ADDRESS               },
+                { "location",                     required_argument, NULL, ARG_LOCATION                    },
+                { "password-hint",                required_argument, NULL, ARG_PASSWORD_HINT               },
+                { "icon-name",                    required_argument, NULL, ARG_ICON_NAME                   },
+                { "home-dir",                     required_argument, NULL, 'd'                             }, /* Compatible with useradd(8) */
+                { "uid",                          required_argument, NULL, 'u'                             }, /* Compatible with useradd(8) */
+                { "member-of",                    required_argument, NULL, 'G'                             },
+                { "groups",                       required_argument, NULL, 'G'                             }, /* Compat alias to keep thing in sync with useradd(8) */
+                { "skel",                         required_argument, NULL, 'k'                             }, /* Compatible with useradd(8) */
+                { "shell",                        required_argument, NULL, 's'                             }, /* Compatible with useradd(8) */
+                { "setenv",                       required_argument, NULL, ARG_SETENV                      },
+                { "timezone",                     required_argument, NULL, ARG_TIMEZONE                    },
+                { "language",                     required_argument, NULL, ARG_LANGUAGE                    },
+                { "locked",                       required_argument, NULL, ARG_LOCKED                      },
+                { "not-before",                   required_argument, NULL, ARG_NOT_BEFORE                  },
+                { "not-after",                    required_argument, NULL, ARG_NOT_AFTER                   },
+                { "expiredate",                   required_argument, NULL, 'e'                             }, /* Compat alias to keep thing in sync with useradd(8) */
+                { "ssh-authorized-keys",          required_argument, NULL, ARG_SSH_AUTHORIZED_KEYS         },
+                { "disk-size",                    required_argument, NULL, ARG_DISK_SIZE                   },
+                { "access-mode",                  required_argument, NULL, ARG_ACCESS_MODE                 },
+                { "umask",                        required_argument, NULL, ARG_UMASK                       },
+                { "nice",                         required_argument, NULL, ARG_NICE                        },
+                { "rlimit",                       required_argument, NULL, ARG_RLIMIT                      },
+                { "tasks-max",                    required_argument, NULL, ARG_TASKS_MAX                   },
+                { "memory-high",                  required_argument, NULL, ARG_MEMORY_HIGH                 },
+                { "memory-max",                   required_argument, NULL, ARG_MEMORY_MAX                  },
+                { "cpu-weight",                   required_argument, NULL, ARG_CPU_WEIGHT                  },
+                { "io-weight",                    required_argument, NULL, ARG_IO_WEIGHT                   },
+                { "storage",                      required_argument, NULL, ARG_STORAGE                     },
+                { "image-path",                   required_argument, NULL, ARG_IMAGE_PATH                  },
+                { "fs-type",                      required_argument, NULL, ARG_FS_TYPE                     },
+                { "luks-discard",                 required_argument, NULL, ARG_LUKS_DISCARD                },
+                { "luks-offline-discard",         required_argument, NULL, ARG_LUKS_OFFLINE_DISCARD        },
+                { "luks-cipher",                  required_argument, NULL, ARG_LUKS_CIPHER                 },
+                { "luks-cipher-mode",             required_argument, NULL, ARG_LUKS_CIPHER_MODE            },
+                { "luks-volume-key-size",         required_argument, NULL, ARG_LUKS_VOLUME_KEY_SIZE        },
+                { "luks-pbkdf-type",              required_argument, NULL, ARG_LUKS_PBKDF_TYPE             },
+                { "luks-pbkdf-hash-algorithm",    required_argument, NULL, ARG_LUKS_PBKDF_HASH_ALGORITHM   },
+                { "luks-pbkdf-force-iterations",  required_argument, NULL, ARG_LUKS_PBKDF_FORCE_ITERATIONS },
+                { "luks-pbkdf-time-cost",         required_argument, NULL, ARG_LUKS_PBKDF_TIME_COST        },
+                { "luks-pbkdf-memory-cost",       required_argument, NULL, ARG_LUKS_PBKDF_MEMORY_COST      },
+                { "luks-pbkdf-parallel-threads",  required_argument, NULL, ARG_LUKS_PBKDF_PARALLEL_THREADS },
+                { "luks-sector-size",             required_argument, NULL, ARG_LUKS_SECTOR_SIZE            },
+                { "nosuid",                       required_argument, NULL, ARG_NOSUID                      },
+                { "nodev",                        required_argument, NULL, ARG_NODEV                       },
+                { "noexec",                       required_argument, NULL, ARG_NOEXEC                      },
+                { "cifs-user-name",               required_argument, NULL, ARG_CIFS_USER_NAME              },
+                { "cifs-domain",                  required_argument, NULL, ARG_CIFS_DOMAIN                 },
+                { "cifs-service",                 required_argument, NULL, ARG_CIFS_SERVICE                },
+                { "cifs-extra-mount-options",     required_argument, NULL, ARG_CIFS_EXTRA_MOUNT_OPTIONS    },
+                { "rate-limit-interval",          required_argument, NULL, ARG_RATE_LIMIT_INTERVAL         },
+                { "rate-limit-burst",             required_argument, NULL, ARG_RATE_LIMIT_BURST            },
+                { "stop-delay",                   required_argument, NULL, ARG_STOP_DELAY                  },
+                { "kill-processes",               required_argument, NULL, ARG_KILL_PROCESSES              },
+                { "enforce-password-policy",      required_argument, NULL, ARG_ENFORCE_PASSWORD_POLICY     },
+                { "password-change-now",          required_argument, NULL, ARG_PASSWORD_CHANGE_NOW         },
+                { "password-change-min",          required_argument, NULL, ARG_PASSWORD_CHANGE_MIN         },
+                { "password-change-max",          required_argument, NULL, ARG_PASSWORD_CHANGE_MAX         },
+                { "password-change-warn",         required_argument, NULL, ARG_PASSWORD_CHANGE_WARN        },
+                { "password-change-inactive",     required_argument, NULL, ARG_PASSWORD_CHANGE_INACTIVE    },
+                { "auto-login",                   required_argument, NULL, ARG_AUTO_LOGIN                  },
+                { "session-launcher",             required_argument, NULL, ARG_SESSION_LAUNCHER,           },
+                { "session-type",                 required_argument, NULL, ARG_SESSION_TYPE,               },
+                { "json",                         required_argument, NULL, ARG_JSON                        },
+                { "export-format",                required_argument, NULL, ARG_EXPORT_FORMAT               },
+                { "pkcs11-token-uri",             required_argument, NULL, ARG_PKCS11_TOKEN_URI            },
+                { "fido2-credential-algorithm",   required_argument, NULL, ARG_FIDO2_CRED_ALG              },
+                { "fido2-device",                 required_argument, NULL, ARG_FIDO2_DEVICE                },
+                { "fido2-with-client-pin",        required_argument, NULL, ARG_FIDO2_WITH_PIN              },
+                { "fido2-with-user-presence",     required_argument, NULL, ARG_FIDO2_WITH_UP               },
+                { "fido2-with-user-verification", required_argument, NULL, ARG_FIDO2_WITH_UV               },
+                { "recovery-key",                 required_argument, NULL, ARG_RECOVERY_KEY                },
+                { "and-resize",                   required_argument, NULL, ARG_AND_RESIZE                  },
+                { "and-change-password",          required_argument, NULL, ARG_AND_CHANGE_PASSWORD         },
+                { "drop-caches",                  required_argument, NULL, ARG_DROP_CACHES                 },
+                { "luks-extra-mount-options",     required_argument, NULL, ARG_LUKS_EXTRA_MOUNT_OPTIONS    },
+                { "auto-resize-mode",             required_argument, NULL, ARG_AUTO_RESIZE_MODE            },
+                { "rebalance-weight",             required_argument, NULL, ARG_REBALANCE_WEIGHT            },
+                { "capability-bounding-set",      required_argument, NULL, ARG_CAPABILITY_BOUNDING_SET     },
+                { "capability-ambient-set",       required_argument, NULL, ARG_CAPABILITY_AMBIENT_SET      },
+                { "prompt-new-user",              no_argument,       NULL, ARG_PROMPT_NEW_USER             },
+                { "blob",                         required_argument, NULL, 'b'                             },
+                { "avatar",                       required_argument, NULL, ARG_AVATAR                      },
+                { "login-background",             required_argument, NULL, ARG_LOGIN_BACKGROUND            },
                 {}
         };
 
index 447e8c597cadd12ca9ce33b75157d1355432fce3..757881c2e6021719199c089c83f678d5a2648f19 100644 (file)
@@ -2810,7 +2810,8 @@ static int home_dispatch_acquire(Home *h, Operation *o) {
         case HOME_ABSENT:
                 r = sd_bus_error_setf(&error, BUS_ERROR_HOME_ABSENT,
                                       "Home %s is currently missing or not plugged in.", h->user_name);
-                goto check;
+                operation_result(o, r, &error);
+                return 1;
 
         case HOME_INACTIVE:
         case HOME_DIRTY:
@@ -2841,7 +2842,6 @@ static int home_dispatch_acquire(Home *h, Operation *o) {
         if (r >= 0)
                 r = call(h, o->secret, for_state, &error);
 
- check:
         if (r != 0) /* failure or completed */
                 operation_result(o, r, &error);
         else /* ongoing */
@@ -2871,33 +2871,35 @@ static int home_dispatch_release(Home *h, Operation *o) {
         assert(o);
         assert(o->type == OPERATION_RELEASE);
 
-        if (home_is_referenced(h))
+        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 {
-                switch (home_get_state(h)) {
-
-                case HOME_UNFIXATED:
-                case HOME_ABSENT:
-                case HOME_INACTIVE:
-                case HOME_DIRTY:
-                        r = 1; /* done */
-                        break;
-
-                case HOME_LOCKED:
-                        r = sd_bus_error_setf(&error, BUS_ERROR_HOME_LOCKED, "Home %s is currently locked.", h->user_name);
-                        break;
-
-                case HOME_ACTIVE:
-                case HOME_LINGERING:
-                        r = home_deactivate_internal(h, false, &error);
-                        break;
-
-                default:
-                        /* All other cases means we are currently executing an operation, which means the job remains
-                         * pending. */
-                        return 0;
-                }
+                operation_result(o, r, &error);
+                return 1;
+        }
+
+        switch (home_get_state(h)) {
+
+        case HOME_UNFIXATED:
+        case HOME_ABSENT:
+        case HOME_INACTIVE:
+        case HOME_DIRTY:
+                r = 1; /* done */
+                break;
+
+        case HOME_LOCKED:
+                r = sd_bus_error_setf(&error, BUS_ERROR_HOME_LOCKED, "Home %s is currently locked.", h->user_name);
+                break;
+
+        case HOME_ACTIVE:
+        case HOME_LINGERING:
+                r = home_deactivate_internal(h, false, &error);
+                break;
+
+        default:
+                /* All other cases means we are currently executing an operation, which means the job remains
+                 * pending. */
+                return 0;
         }
 
         assert(!h->current_operation);
index c0f1b83debad6ea0d5ca3d0758b29dccfec56fd4..7d466cdd326f61c8f504d9ceaf8d2e767308fab5 100644 (file)
@@ -202,7 +202,15 @@ 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);
+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);
 
 void home_process_notify(Home *h, char **l, int fd);
 
index 5f345b3d407664db18d440d0d7ee626a9fbb1fa4..90fd91c8a74b7c64071a99e8b8aa17f0addc8008 100644 (file)
@@ -1365,8 +1365,11 @@ static int manager_enumerate_devices(Manager *m) {
         if (r < 0)
                 return r;
 
-        FOREACH_DEVICE(e, d)
+        FOREACH_DEVICE(e, d) {
+                if (device_is_processed(d) <= 0)
+                        continue;
                 (void) manager_add_device(m, d);
+        }
 
         return 0;
 }
index d70926fe33deae3a1a52c172216b8fafd4d656d2..f9cc6eced8161ee9061480fdba19ecdd1a3fdc39 100644 (file)
@@ -2553,7 +2553,7 @@ static int can_resize_fs(int fd, uint64_t old_size, uint64_t new_size) {
 
                 /* btrfs can grow and shrink online */
 
-        } else if (is_fs_type(&sfs, XFS_SB_MAGIC)) {
+        } else if (is_fs_type(&sfs, XFS_SUPER_MAGIC)) {
 
                 if (new_size < XFS_MINIMAL_SIZE)
                         return log_error_errno(SYNTHETIC_ERRNO(ERANGE), "New file system size too small for xfs (needs to be 14M at least).");
@@ -2616,7 +2616,7 @@ static int ext4_offline_resize_fs(
                 return r;
         if (r == 0) {
                 /* Child */
-                execlp("e2fsck" ,"e2fsck", "-fp", setup->dm_node, NULL);
+                execlp("e2fsck""e2fsck", "-fp", setup->dm_node, NULL);
                 log_open();
                 log_error_errno(errno, "Failed to execute e2fsck: %m");
                 _exit(EXIT_FAILURE);
@@ -2648,7 +2648,7 @@ static int ext4_offline_resize_fs(
                 return r;
         if (r == 0) {
                 /* Child */
-                execlp("resize2fs" ,"resize2fs", setup->dm_node, size_str, NULL);
+                execlp("resize2fs""resize2fs", setup->dm_node, size_str, NULL);
                 log_open();
                 log_error_errno(errno, "Failed to execute resize2fs: %m");
                 _exit(EXIT_FAILURE);
index 508c0c01b22674095bb0870fc1760bdda6e783ef..7400937e95f10e13ac8c8d8946067b94316a430d 100644 (file)
@@ -99,7 +99,7 @@ int home_update_quota_auto(UserRecord *h, const char *path) {
         if (statfs(path, &sfs) < 0)
                 return log_error_errno(errno, "Failed to statfs() file system: %m");
 
-        if (is_fs_type(&sfs, XFS_SB_MAGIC) ||
+        if (is_fs_type(&sfs, XFS_SUPER_MAGIC) ||
             is_fs_type(&sfs, EXT4_SUPER_MAGIC))
                 return home_update_quota_classic(h, path);
 
index a1e912f6a0c629778ed20bd348bd221a073aa244..f573c5fb15af69ac57f25f45373293a75d768750 100644 (file)
@@ -106,6 +106,11 @@ executables += [
                         threads,
                 ],
         },
+        test_template + {
+                'sources' : files('test-homed-regression-31896.c'),
+                'conditions' : ['ENABLE_HOMED'],
+                'type' : 'manual',
+        },
 ]
 
 modules += [
diff --git a/src/home/test-homed-regression-31896.c b/src/home/test-homed-regression-31896.c
new file mode 100644 (file)
index 0000000..1530a2f
--- /dev/null
@@ -0,0 +1,35 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "bus-locator.h"
+#include "main-func.h"
+#include "tests.h"
+
+static int run(int argc, char **argv) {
+        _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
+        _cleanup_(sd_bus_message_unrefp) sd_bus_message *ref = NULL;
+        _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+        const char *username = NULL;
+
+        /* This is a regression test for the following bug:
+         * https://github.com/systemd/systemd/pull/31896
+         * It is run as part of TEST-46-HOMED
+         */
+
+        test_setup_logging(LOG_DEBUG);
+        assert_se(sd_bus_open_system(&bus) >= 0);
+
+        assert_se(argc == 2);
+        username = argv[1];
+
+        assert_se(bus_call_method(bus, bus_home_mgr, "RefHomeUnrestricted", NULL, &ref, "sb", username, true) >= 0);
+
+        assert_se(bus_call_method_async(bus, NULL, bus_home_mgr, "AuthenticateHome", NULL, NULL, "ss", username, "{}") >= 0);
+        assert_se(sd_bus_flush(bus) >= 0);
+
+        (void) bus_call_method(bus, bus_home_mgr, "ReleaseHome", &error, NULL, "s", username);
+        assert_se(!sd_bus_error_has_name(&error, SD_BUS_ERROR_NO_REPLY)); /* Make sure we didn't crash */
+
+        return 0;
+}
+
+DEFINE_MAIN_FUNCTION(run);
index a4feda40812539ba4d0326cefd6b38e490c3e5d8..848255179b80a87b7e5ff03d0aaaa994c68114eb 100644 (file)
@@ -431,7 +431,9 @@ static int pull_job_open_disk(PullJob *j) {
                         return log_error_errno(SYNTHETIC_ERRNO(EIO),
                                                "Failed to initialize hash context.");
 #else
-                initialize_libgcrypt(false);
+                r = initialize_libgcrypt(false);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to load libgcrypt: %m");
 
                 if (gcry_md_open(&j->checksum_ctx, GCRY_MD_SHA256, 0) != 0)
                         return log_error_errno(SYNTHETIC_ERRNO(EIO),
index c7472538581d183fef76a025faba3d596eb78848..08ea94f6d4065a1a16a1178f3d94283787213509 100644 (file)
@@ -50,6 +50,7 @@ static char **arg_file = NULL;
 STATIC_DESTRUCTOR_REGISTER(arg_key_pem, erase_and_freep);
 STATIC_DESTRUCTOR_REGISTER(arg_cert_pem, freep);
 STATIC_DESTRUCTOR_REGISTER(arg_trust_pem, freep);
+STATIC_DESTRUCTOR_REGISTER(arg_file, strv_freep);
 
 typedef struct RequestMeta {
         sd_journal *journal;
@@ -446,7 +447,7 @@ static int request_parse_range(
         }
 
         range_after_eq = startswith(range, "realtime=");
-        if (startswith(range, "realtime=")) {
+        if (range_after_eq) {
                 range_after_eq += strspn(range_after_eq, WHITESPACE);
                 return request_parse_range_time(m, range_after_eq);
         }
@@ -512,15 +513,7 @@ static mhd_result request_parse_arguments_iterator(
                 }
 
                 if (r) {
-                        sd_id128_t bid;
-
-                        r = sd_id128_get_boot(&bid);
-                        if (r < 0) {
-                                log_error_errno(r, "Failed to get boot ID: %m");
-                                return MHD_NO;
-                        }
-
-                        r = add_match_boot_id(m->journal, bid);
+                        r = add_match_boot_id(m->journal, SD_ID128_NULL);
                         if (r < 0) {
                                 m->argument_parse_error = r;
                                 return MHD_NO;
index 74bf6cfcc84bd944a7d509e13a7203a8ad1bdbc7..545a1c624010fd09b7862d27e3045d0784e984aa 100644 (file)
@@ -451,7 +451,7 @@ static int setup_microhttpd_server(RemoteServer *s,
         if (epoll_fd < 0)
                 return log_error_errno(SYNTHETIC_ERRNO(EUCLEAN), "μhttp epoll fd is invalid");
 
-        r = sd_event_add_io(s->events, &d->io_event,
+        r = sd_event_add_io(s->event, &d->io_event,
                             epoll_fd, EPOLLIN,
                             dispatch_http_event, d);
         if (r < 0)
@@ -461,7 +461,7 @@ static int setup_microhttpd_server(RemoteServer *s,
         if (r < 0)
                 return log_error_errno(r, "Failed to set source name: %m");
 
-        r = sd_event_add_time(s->events, &d->timer_event,
+        r = sd_event_add_time(s->event, &d->timer_event,
                               CLOCK_MONOTONIC, UINT64_MAX, 0,
                               null_timer_event_handler, d);
         if (r < 0)
@@ -535,24 +535,6 @@ static int dispatch_http_event(sd_event_source *event,
  **********************************************************************
  **********************************************************************/
 
-static int setup_signals(RemoteServer *s) {
-        int r;
-
-        assert(s);
-
-        assert_se(sigprocmask_many(SIG_SETMASK, NULL, SIGINT, SIGTERM) >= 0);
-
-        r = sd_event_add_signal(s->events, &s->sigterm_event, SIGTERM, NULL, s);
-        if (r < 0)
-                return r;
-
-        r = sd_event_add_signal(s->events, &s->sigint_event, SIGINT, NULL, s);
-        if (r < 0)
-                return r;
-
-        return 0;
-}
-
 static int setup_raw_socket(RemoteServer *s, const char *address) {
         int fd;
 
@@ -580,9 +562,9 @@ static int create_remoteserver(
         if (r < 0)
                 return r;
 
-        r = setup_signals(s);
+        r = sd_event_set_signal_exit(s->event, true);
         if (r < 0)
-                return log_error_errno(r, "Failed to set up signals: %m");
+                return log_error_errno(r, "Failed to install SIGINT/SIGTERM handlers: %m");
 
         n = sd_listen_fds(true);
         if (n < 0)
@@ -1128,7 +1110,7 @@ static int run(int argc, char **argv) {
         if (r < 0)
                 return r;
 
-        r = sd_event_set_watchdog(s.events, true);
+        r = sd_event_set_watchdog(s.event, true);
         if (r < 0)
                 return log_error_errno(r, "Failed to enable watchdog: %m");
 
@@ -1140,13 +1122,13 @@ static int run(int argc, char **argv) {
         notify_message = notify_start(NOTIFY_READY, NOTIFY_STOPPING);
 
         while (s.active) {
-                r = sd_event_get_state(s.events);
+                r = sd_event_get_state(s.event);
                 if (r < 0)
                         return r;
                 if (r == SD_EVENT_FINISHED)
                         break;
 
-                r = sd_event_run(s.events, -1);
+                r = sd_event_run(s.event, -1);
                 if (r < 0)
                         return log_error_errno(r, "Failed to run event loop: %m");
         }
index 084a3d8c913ee4c1f9a51458af517dbef514d4c4..92187b7420c59f1ca1ff0415fc15062510e1f986 100644 (file)
@@ -257,19 +257,19 @@ int journal_remote_add_source(RemoteServer *s, int fd, char* name, bool own_name
                 return r;
         }
 
-        r = sd_event_add_io(s->events, &source->event,
+        r = sd_event_add_io(s->event, &source->event,
                             fd, EPOLLIN|EPOLLRDHUP|EPOLLPRI,
                             dispatch_raw_source_event, source);
         if (r == 0) {
                 /* Add additional source for buffer processing. It will be
                  * enabled later. */
-                r = sd_event_add_defer(s->events, &source->buffer_event,
+                r = sd_event_add_defer(s->event, &source->buffer_event,
                                        dispatch_raw_source_until_block, source);
                 if (r == 0)
                         r = sd_event_source_set_enabled(source->buffer_event, SD_EVENT_OFF);
         } else if (r == -EPERM) {
                 log_debug("Falling back to sd_event_add_defer for fd:%d (%s)", fd, name);
-                r = sd_event_add_defer(s->events, &source->event,
+                r = sd_event_add_defer(s->event, &source->event,
                                        dispatch_blocking_source_event, source);
                 if (r == 0)
                         r = sd_event_source_set_enabled(source->event, SD_EVENT_ON);
@@ -301,7 +301,7 @@ int journal_remote_add_raw_socket(RemoteServer *s, int fd) {
         assert(s);
         assert(fd >= 0);
 
-        r = sd_event_add_io(s->events, &s->listen_event,
+        r = sd_event_add_io(s->event, &s->listen_event,
                             fd, EPOLLIN,
                             dispatch_raw_connection_event, s);
         if (r < 0)
@@ -347,7 +347,7 @@ int journal_remote_server_init(
         else
                 assert_not_reached();
 
-        r = sd_event_default(&s->events);
+        r = sd_event_default(&s->event);
         if (r < 0)
                 return log_error_errno(r, "Failed to allocate event loop: %m");
 
@@ -375,10 +375,8 @@ void journal_remote_server_destroy(RemoteServer *s) {
         writer_unref(s->_single_writer);
         hashmap_free(s->writers);
 
-        sd_event_source_unref(s->sigterm_event);
-        sd_event_source_unref(s->sigint_event);
         sd_event_source_unref(s->listen_event);
-        sd_event_unref(s->events);
+        sd_event_unref(s->event);
 
         if (s == journal_remote_server_global)
                 journal_remote_server_global = NULL;
index 1c0cde4db344a544e07d285c99c1c367a2128aea..f1ec5d9a543d0d45e39576b9aa2f771980fc7f21 100644 (file)
@@ -29,8 +29,8 @@ struct RemoteServer {
         RemoteSource **sources;
         size_t active;
 
-        sd_event *events;
-        sd_event_source *sigterm_event, *sigint_event, *listen_event;
+        sd_event *event;
+        sd_event_source *listen_event;
 
         Hashmap *writers;
         Writer *_single_writer;
index 8206ca89bf2ead1a480d5935e66f58cf187ec2a3..23ad3b2d3121b0940a55d968efc52edbd2f5b30a 100644 (file)
@@ -388,7 +388,7 @@ int open_journal_for_upload(Uploader *u,
                 else
                         u->timeout = JOURNAL_UPLOAD_POLL_TIMEOUT;
 
-                r = sd_event_add_io(u->events, &u->input_event,
+                r = sd_event_add_io(u->event, &u->input_event,
                                     fd, events, dispatch_journal_input, u);
                 if (r < 0)
                         return log_error_errno(r, "Failed to register input event: %m");
index 1198791ed121f6d02929a94bca6e5868289b03a2..657e574d2d3631141ac7e9b237f5ba27da0d5d31 100644 (file)
@@ -59,6 +59,8 @@ static int arg_follow = -1;
 static const char *arg_save_state = NULL;
 static usec_t arg_network_timeout_usec = USEC_INFINITY;
 
+STATIC_DESTRUCTOR_REGISTER(arg_file, strv_freep);
+
 static void close_fd_input(Uploader *u);
 
 #define SERVER_ANSWER_KEEP 2048
@@ -364,7 +366,7 @@ static int open_file_for_upload(Uploader *u, const char *filename) {
         u->input = fd;
 
         if (arg_follow != 0) {
-                r = sd_event_add_io(u->events, &u->input_event,
+                r = sd_event_add_io(u->event, &u->input_event,
                                     fd, EPOLLIN, dispatch_fd_input, u);
                 if (r < 0) {
                         if (r != -EPERM || arg_follow > 0)
@@ -378,38 +380,6 @@ static int open_file_for_upload(Uploader *u, const char *filename) {
         return r;
 }
 
-static int dispatch_sigterm(sd_event_source *event,
-                            const struct signalfd_siginfo *si,
-                            void *userdata) {
-        Uploader *u = ASSERT_PTR(userdata);
-
-        log_received_signal(LOG_INFO, si);
-
-        close_fd_input(u);
-        close_journal_input(u);
-
-        sd_event_exit(u->events, 0);
-        return 0;
-}
-
-static int setup_signals(Uploader *u) {
-        int r;
-
-        assert(u);
-
-        assert_se(sigprocmask_many(SIG_SETMASK, NULL, SIGINT, SIGTERM) >= 0);
-
-        r = sd_event_add_signal(u->events, &u->sigterm_event, SIGTERM, dispatch_sigterm, u);
-        if (r < 0)
-                return r;
-
-        r = sd_event_add_signal(u->events, &u->sigint_event, SIGINT, dispatch_sigterm, u);
-        if (r < 0)
-                return r;
-
-        return 0;
-}
-
 static int setup_uploader(Uploader *u, const char *url, const char *state_file) {
         int r;
         const char *host, *proto = "";
@@ -445,13 +415,13 @@ static int setup_uploader(Uploader *u, const char *url, const char *state_file)
 
         u->state_file = state_file;
 
-        r = sd_event_default(&u->events);
+        r = sd_event_default(&u->event);
         if (r < 0)
                 return log_error_errno(r, "sd_event_default failed: %m");
 
-        r = setup_signals(u);
+        r = sd_event_set_signal_exit(u->event, true);
         if (r < 0)
-                return log_error_errno(r, "Failed to set up signals: %m");
+                return log_error_errno(r, "Failed to install SIGINT/SIGTERM handlers: %m");
 
         (void) sd_watchdog_enabled(false, &u->watchdog_usec);
 
@@ -475,9 +445,7 @@ static void destroy_uploader(Uploader *u) {
         close_fd_input(u);
         close_journal_input(u);
 
-        sd_event_source_unref(u->sigterm_event);
-        sd_event_source_unref(u->sigint_event);
-        sd_event_unref(u->events);
+        sd_event_unref(u->event);
 }
 
 static int perform_upload(Uploader *u) {
@@ -816,7 +784,7 @@ static int run(int argc, char **argv) {
         if (r < 0)
                 return r;
 
-        sd_event_set_watchdog(u.events, true);
+        sd_event_set_watchdog(u.event, true);
 
         r = check_cursor_updating(&u);
         if (r < 0)
@@ -844,7 +812,7 @@ static int run(int argc, char **argv) {
                                       NOTIFY_STOPPING);
 
         for (;;) {
-                r = sd_event_get_state(u.events);
+                r = sd_event_get_state(u.event);
                 if (r < 0)
                         return r;
                 if (r == SD_EVENT_FINISHED)
@@ -871,7 +839,7 @@ static int run(int argc, char **argv) {
                                 return r;
                 }
 
-                r = sd_event_run(u.events, u.timeout);
+                r = sd_event_run(u.event, u.timeout);
                 if (r < 0)
                         return log_error_errno(r, "Failed to run event loop: %m");
         }
index 9ff5a7ba5805dd2c29ab133d17743b473667fa80..5ba3c4f1a0e7ed08f28a9e461c0e12e0e5cb87db 100644 (file)
@@ -24,8 +24,7 @@ typedef enum {
 } entry_state;
 
 typedef struct Uploader {
-        sd_event *events;
-        sd_event_source *sigint_event, *sigterm_event;
+        sd_event *event;
 
         char *url;
         CURL *easy;
index a4788b828cf81ba39ea288fa3b91380654fcdff4..0da0df9ee701ddfb5e15230f83301108d4594614 100644 (file)
@@ -75,12 +75,12 @@ static int acquire_first_emergency_log_message(char **ret) {
         if (r < 0)
                 return log_error_errno(r, "Failed to add boot ID filter: %m");
 
-        r = sd_journal_add_match(j, "_UID=0", 0);
+        r = sd_journal_add_match(j, "_UID=0", SIZE_MAX);
         if (r < 0)
                 return log_error_errno(r, "Failed to add User ID filter: %m");
 
         assert_cc(0 == LOG_EMERG);
-        r = sd_journal_add_match(j, "PRIORITY=0", 0);
+        r = sd_journal_add_match(j, "PRIORITY=0", SIZE_MAX);
         if (r < 0)
                 return log_error_errno(r, "Failed to add Emergency filter: %m");
 
index 79f09b1fb0a522167fae1089a6029a09e49998d6..10630f5e6cc47bff7625c7307704229fc9302b98 100644 (file)
 #include "journalctl.h"
 #include "journalctl-authenticate.h"
 #include "memstream-util.h"
+#include "path-util.h"
 #include "qrcode-util.h"
 #include "random-util.h"
+#include "stat-util.h"
 #include "terminal-util.h"
 #include "tmpfile-util.h"
 
-static int format_journal_url(
+static int format_key(
                 const void *seed,
                 size_t seed_size,
                 uint64_t start,
                 uint64_t interval,
-                const char *hn,
-                sd_id128_t machine,
-                bool full,
-                char **ret_url) {
+                char **ret) {
 
         _cleanup_(memstream_done) MemStream m = {};
         FILE *f;
 
         assert(seed);
         assert(seed_size > 0);
+        assert(ret);
 
         f = memstream_init(&m);
         if (!f)
                 return -ENOMEM;
 
-        if (full)
-                fputs("fss://", f);
-
         for (size_t i = 0; i < seed_size; i++) {
                 if (i > 0 && i % 3 == 0)
                         fputc('-', f);
@@ -47,37 +44,29 @@ static int format_journal_url(
 
         fprintf(f, "/%"PRIx64"-%"PRIx64, start, interval);
 
-        if (full) {
-                fprintf(f, "?machine=" SD_ID128_FORMAT_STR, SD_ID128_FORMAT_VAL(machine));
-                if (hn)
-                        fprintf(f, ";hostname=%s", hn);
-        }
-
-        return memstream_finalize(&m, ret_url, NULL);
+        return memstream_finalize(&m, ret, NULL);
 }
 
 int action_setup_keys(void) {
+        _cleanup_(unlink_and_freep) char *tmpfile = NULL;
+        _cleanup_close_ int fd = -EBADF;
+        _cleanup_free_ char *path = NULL;
         size_t mpk_size, seed_size, state_size;
-        _cleanup_(unlink_and_freep) char *k = NULL;
-        _cleanup_free_ char *p = NULL;
         uint8_t *mpk, *seed, *state;
-        _cleanup_close_ int fd = -EBADF;
         sd_id128_t machine, boot;
-        struct stat st;
         uint64_t n;
         int r;
 
         assert(arg_action == ACTION_SETUP_KEYS);
 
-        r = stat("/var/log/journal", &st);
-        if (r < 0 && !IN_SET(errno, ENOENT, ENOTDIR))
-                return log_error_errno(errno, "stat(\"%s\") failed: %m", "/var/log/journal");
-
-        if (r < 0 || !S_ISDIR(st.st_mode)) {
-                log_error("%s is not a directory, must be using persistent logging for FSS.",
-                          "/var/log/journal");
-                return r < 0 ? -errno : -ENOTDIR;
-        }
+        r = is_dir("/var/log/journal/", /* follow = */ false);
+        if (r == 0)
+                return log_error_errno(SYNTHETIC_ERRNO(ENOTDIR),
+                                       "/var/log/journal is not a directory, must be using persistent logging for FSS.");
+        if (r == -ENOENT)
+                return log_error_errno(r, "Directory /var/log/journal/ does not exist, must be using persistent logging for FSS.");
+        if (r < 0)
+                return log_error_errno(r, "Failed to check if /var/log/journal/ is a directory: %m");
 
         r = sd_id128_get_machine(&machine);
         if (r < 0)
@@ -87,21 +76,16 @@ int action_setup_keys(void) {
         if (r < 0)
                 return log_error_errno(r, "Failed to get boot ID: %m");
 
-        if (asprintf(&p, "/var/log/journal/" SD_ID128_FORMAT_STR "/fss",
-                     SD_ID128_FORMAT_VAL(machine)) < 0)
+        path = path_join("/var/log/journal/", SD_ID128_TO_STRING(machine), "/fss");
+        if (!path)
                 return log_oom();
 
         if (arg_force) {
-                r = unlink(p);
-                if (r < 0 && errno != ENOENT)
-                        return log_error_errno(errno, "unlink(\"%s\") failed: %m", p);
-        } else if (access(p, F_OK) >= 0)
+                if (unlink(path) < 0 && errno != ENOENT)
+                        return log_error_errno(errno, "Failed to remove \"%s\": %m", path);
+        } else if (access(path, F_OK) >= 0)
                 return log_error_errno(SYNTHETIC_ERRNO(EEXIST),
-                                       "Sealing key file %s exists already. Use --force to recreate.", p);
-
-        if (asprintf(&k, "/var/log/journal/" SD_ID128_FORMAT_STR "/fss.tmp.XXXXXX",
-                     SD_ID128_FORMAT_VAL(machine)) < 0)
-                return log_oom();
+                                       "Sealing key file %s exists already. Use --force to recreate.", path);
 
         mpk_size = FSPRG_mskinbytes(FSPRG_RECOMMENDED_SECPAR);
         mpk = alloca_safe(mpk_size);
@@ -118,25 +102,27 @@ int action_setup_keys(void) {
                 return log_error_errno(r, "Failed to acquire random seed: %m");
 
         log_info("Generating key pair...");
-        FSPRG_GenMK(NULL, mpk, seed, seed_size, FSPRG_RECOMMENDED_SECPAR);
+        r = FSPRG_GenMK(NULL, mpk, seed, seed_size, FSPRG_RECOMMENDED_SECPAR);
+        if (r < 0)
+                return log_error_errno(r, "Failed to generate key pair: %m");
 
         log_info("Generating sealing key...");
-        FSPRG_GenState0(state, mpk, seed, seed_size);
+        r = FSPRG_GenState0(state, mpk, seed, seed_size);
+        if (r < 0)
+                return log_error_errno(r, "Failed to generate sealing key: %m");
 
         assert(arg_interval > 0);
-
         n = now(CLOCK_REALTIME);
         n /= arg_interval;
 
-        safe_close(fd);
-        fd = mkostemp_safe(k);
+        fd = open_tmpfile_linkable(path, O_WRONLY|O_CLOEXEC, &tmpfile);
         if (fd < 0)
-                return log_error_errno(fd, "Failed to open %s: %m", k);
+                return log_error_errno(fd, "Failed to open a temporary file for %s: %m", path);
 
         r = chattr_secret(fd, CHATTR_WARN_UNSUPPORTED_FLAGS);
         if (r < 0)
                 log_full_errno(ERRNO_IS_NOT_SUPPORTED(r) ? LOG_DEBUG : LOG_WARNING,
-                               r, "Failed to set file attributes on '%s', ignoring: %m", k);
+                               r, "Failed to set file attributes on a temporary file for '%s', ignoring: %m", path);
 
         struct FSSHeader h = {
                 .signature = { 'K', 'S', 'H', 'H', 'R', 'H', 'L', 'P' },
@@ -157,61 +143,65 @@ int action_setup_keys(void) {
         if (r < 0)
                 return log_error_errno(r, "Failed to write state: %m");
 
-        if (rename(k, p) < 0)
-                return log_error_errno(errno, "Failed to link file: %m");
-
-        k = mfree(k);
+        r = link_tmpfile(fd, tmpfile, path, /* flags = */ 0);
+        if (r < 0)
+                return log_error_errno(r, "Failed to link file: %m");
 
-        _cleanup_free_ char *hn = NULL, *key = NULL;
+        tmpfile = mfree(tmpfile);
 
-        r = format_journal_url(seed, seed_size, n, arg_interval, hn, machine, false, &key);
+        _cleanup_free_ char *key = NULL;
+        r = format_key(seed, seed_size, n, arg_interval, &key);
         if (r < 0)
                 return r;
 
-        if (on_tty()) {
-                hn = gethostname_malloc();
-                if (hn)
-                        hostname_cleanup(hn);
-
-                fprintf(stderr,
-                        "\nNew keys have been generated for host %s%s" SD_ID128_FORMAT_STR ".\n"
-                        "\n"
-                        "The %ssecret sealing key%s has been written to the following local file.\n"
-                        "This key file is automatically updated when the sealing key is advanced.\n"
-                        "It should not be used on multiple hosts.\n"
-                        "\n"
-                        "\t%s\n"
-                        "\n"
-                        "The sealing key is automatically changed every %s.\n"
-                        "\n"
-                        "Please write down the following %ssecret verification key%s. It should be stored\n"
-                        "in a safe location and should not be saved locally on disk.\n"
-                        "\n\t%s",
-                        strempty(hn), hn ? "/" : "",
-                        SD_ID128_FORMAT_VAL(machine),
-                        ansi_highlight(), ansi_normal(),
-                        p,
-                        FORMAT_TIMESPAN(arg_interval, 0),
-                        ansi_highlight(), ansi_normal(),
-                        ansi_highlight_red());
-                fflush(stderr);
+        if (!on_tty()) {
+                /* If we are not on a TTY, show only the key. */
+                puts(key);
+                return 0;
         }
 
+        _cleanup_free_ char *hn = NULL;
+        hn = gethostname_malloc();
+        if (hn)
+                hostname_cleanup(hn);
+
+        fprintf(stderr,
+                "\nNew keys have been generated for host %s%s" SD_ID128_FORMAT_STR ".\n"
+                "\n"
+                "The %ssecret sealing key%s has been written to the following local file.\n"
+                "This key file is automatically updated when the sealing key is advanced.\n"
+                "It should not be used on multiple hosts.\n"
+                "\n"
+                "\t%s\n"
+                "\n"
+                "The sealing key is automatically changed every %s.\n"
+                "\n"
+                "Please write down the following %ssecret verification key%s. It should be stored\n"
+                "in a safe location and should not be saved locally on disk.\n"
+                "\n\t%s",
+                strempty(hn), hn ? "/" : "",
+                SD_ID128_FORMAT_VAL(machine),
+                ansi_highlight(), ansi_normal(),
+                path,
+                FORMAT_TIMESPAN(arg_interval, 0),
+                ansi_highlight(), ansi_normal(),
+                ansi_highlight_red());
+        fflush(stderr);
+
         puts(key);
 
-        if (on_tty()) {
-                fprintf(stderr, "%s", ansi_normal());
+        fputs(ansi_normal(), stderr);
+
 #if HAVE_QRENCODE
-                _cleanup_free_ char *url = NULL;
-                r = format_journal_url(seed, seed_size, n, arg_interval, hn, machine, true, &url);
-                if (r < 0)
-                        return r;
-
-                (void) print_qrcode(stderr,
-                                    "To transfer the verification key to your phone scan the QR code below",
-                                    url);
+        _cleanup_free_ char *url = NULL;
+        url = strjoin("fss://", key, "?machine=", SD_ID128_TO_STRING(machine), hn ? ";hostname=" : "", hn);
+        if (!url)
+                return log_oom();
+
+        (void) print_qrcode(stderr,
+                            "To transfer the verification key to your phone scan the QR code below",
+                            url);
 #endif
-        }
 
         return 0;
 }
index 9c0e422460e11fae20d8ccbcdb5c024712d0b469..1fdde5eb37932c0a787def0103497343171dc2d2 100644 (file)
@@ -69,30 +69,25 @@ static int add_dmesg(sd_journal *j) {
         if (!arg_dmesg)
                 return 0;
 
-        r = sd_journal_add_match(j, "_TRANSPORT=kernel",
-                                 STRLEN("_TRANSPORT=kernel"));
+        r = sd_journal_add_match(j, "_TRANSPORT=kernel", SIZE_MAX);
         if (r < 0)
-                return log_error_errno(r, "Failed to add match: %m");
-
-        r = sd_journal_add_conjunction(j);
-        if (r < 0)
-                return log_error_errno(r, "Failed to add conjunction: %m");
+                return r;
 
-        return 0;
+        return sd_journal_add_conjunction(j);
 }
 
 static int get_possible_units(
                 sd_journal *j,
                 const char *fields,
                 char **patterns,
-                Set **units) {
+                Set **ret) {
 
-        _cleanup_set_free_free_ Set *found = NULL;
+        _cleanup_set_free_ Set *found = NULL;
         int r;
 
-        found = set_new(&string_hash_ops);
-        if (!found)
-                return -ENOMEM;
+        assert(j);
+        assert(fields);
+        assert(ret);
 
         NULSTR_FOREACH(field, fields) {
                 const void *data;
@@ -103,36 +98,31 @@ static int get_possible_units(
                         return r;
 
                 SD_JOURNAL_FOREACH_UNIQUE(j, data, size) {
-                        char *eq;
-                        size_t prefix;
                         _cleanup_free_ char *u = NULL;
+                        char *eq;
 
                         eq = memchr(data, '=', size);
-                        if (eq)
-                                prefix = eq - (char*) data + 1;
-                        else
-                                prefix = 0;
+                        if (eq) {
+                                size -= eq - (char*) data + 1;
+                                data = ++eq;
+                        }
 
-                        u = strndup((char*) data + prefix, size - prefix);
+                        u = strndup(data, size);
                         if (!u)
                                 return -ENOMEM;
 
-                        STRV_FOREACH(pattern, patterns)
-                                if (fnmatch(*pattern, u, FNM_NOESCAPE) == 0) {
-                                        log_debug("Matched %s with pattern %s=%s", u, field, *pattern);
+                        size_t i;
+                        if (!strv_fnmatch_full(patterns, u, FNM_NOESCAPE, &i))
+                                continue;
 
-                                        r = set_consume(found, u);
-                                        u = NULL;
-                                        if (r < 0 && r != -EEXIST)
-                                                return r;
-
-                                        break;
-                                }
+                        log_debug("Matched %s with pattern %s=%s", u, field, patterns[i]);
+                        r = set_ensure_consume(&found, &string_hash_ops_free, TAKE_PTR(u));
+                        if (r < 0)
+                                return r;
                 }
         }
 
-        *units = TAKE_PTR(found);
-
+        *ret = TAKE_PTR(found);
         return 0;
 }
 
@@ -155,10 +145,14 @@ static int get_possible_units(
 
 static int add_units(sd_journal *j) {
         _cleanup_strv_free_ char **patterns = NULL;
-        int r, count = 0;
+        bool added = false;
+        int r;
 
         assert(j);
 
+        if (strv_isempty(arg_system_units) && strv_isempty(arg_user_units))
+                return 0;
+
         STRV_FOREACH(i, arg_system_units) {
                 _cleanup_free_ char *u = NULL;
 
@@ -167,10 +161,9 @@ static int add_units(sd_journal *j) {
                         return r;
 
                 if (string_is_glob(u)) {
-                        r = strv_push(&patterns, u);
+                        r = strv_consume(&patterns, TAKE_PTR(u));
                         if (r < 0)
                                 return r;
-                        u = NULL;
                 } else {
                         r = add_matches_for_unit(j, u);
                         if (r < 0)
@@ -178,12 +171,12 @@ static int add_units(sd_journal *j) {
                         r = sd_journal_add_disjunction(j);
                         if (r < 0)
                                 return r;
-                        count++;
+                        added = true;
                 }
         }
 
         if (!strv_isempty(patterns)) {
-                _cleanup_set_free_free_ Set *units = NULL;
+                _cleanup_set_free_ Set *units = NULL;
                 char *u;
 
                 r = get_possible_units(j, SYSTEM_UNITS, patterns, &units);
@@ -197,7 +190,7 @@ static int add_units(sd_journal *j) {
                         r = sd_journal_add_disjunction(j);
                         if (r < 0)
                                 return r;
-                        count++;
+                        added = true;
                 }
         }
 
@@ -211,10 +204,9 @@ static int add_units(sd_journal *j) {
                         return r;
 
                 if (string_is_glob(u)) {
-                        r = strv_push(&patterns, u);
+                        r = strv_consume(&patterns, TAKE_PTR(u));
                         if (r < 0)
                                 return r;
-                        u = NULL;
                 } else {
                         r = add_matches_for_user_unit(j, u, getuid());
                         if (r < 0)
@@ -222,12 +214,12 @@ static int add_units(sd_journal *j) {
                         r = sd_journal_add_disjunction(j);
                         if (r < 0)
                                 return r;
-                        count++;
+                        added = true;
                 }
         }
 
         if (!strv_isempty(patterns)) {
-                _cleanup_set_free_free_ Set *units = NULL;
+                _cleanup_set_free_ Set *units = NULL;
                 char *u;
 
                 r = get_possible_units(j, USER_UNITS, patterns, &units);
@@ -241,20 +233,16 @@ static int add_units(sd_journal *j) {
                         r = sd_journal_add_disjunction(j);
                         if (r < 0)
                                 return r;
-                        count++;
+                        added = true;
                 }
         }
 
-        /* Complain if the user request matches but nothing whatsoever was
-         * found, since otherwise everything would be matched. */
-        if (!(strv_isempty(arg_system_units) && strv_isempty(arg_user_units)) && count == 0)
+        /* Complain if the user request matches but nothing whatsoever was found, since otherwise everything
+         * would be matched. */
+        if (!added)
                 return -ENODATA;
 
-        r = sd_journal_add_conjunction(j);
-        if (r < 0)
-                return r;
-
-        return 0;
+        return sd_journal_add_conjunction(j);
 }
 
 static int add_syslog_identifier(sd_journal *j) {
@@ -262,13 +250,11 @@ static int add_syslog_identifier(sd_journal *j) {
 
         assert(j);
 
-        STRV_FOREACH(i, arg_syslog_identifier) {
-                _cleanup_free_ char *u = NULL;
+        if (strv_isempty(arg_syslog_identifier))
+                return 0;
 
-                u = strjoin("SYSLOG_IDENTIFIER=", *i);
-                if (!u)
-                        return -ENOMEM;
-                r = sd_journal_add_match(j, u, 0);
+        STRV_FOREACH(i, arg_syslog_identifier) {
+                r = journal_add_match_pair(j, "SYSLOG_IDENTIFIER", *i);
                 if (r < 0)
                         return r;
                 r = sd_journal_add_disjunction(j);
@@ -276,11 +262,7 @@ static int add_syslog_identifier(sd_journal *j) {
                         return r;
         }
 
-        r = sd_journal_add_conjunction(j);
-        if (r < 0)
-                return r;
-
-        return 0;
+        return sd_journal_add_conjunction(j);
 }
 
 static int add_exclude_identifier(sd_journal *j) {
@@ -297,40 +279,65 @@ static int add_exclude_identifier(sd_journal *j) {
 }
 
 static int add_priorities(sd_journal *j) {
-        char match[] = "PRIORITY=0";
-        int i, r;
+        int r;
 
         assert(j);
 
-        if (arg_priorities == 0xFF)
+        if (arg_priorities == 0)
                 return 0;
 
-        for (i = LOG_EMERG; i <= LOG_DEBUG; i++)
+        for (int i = LOG_EMERG; i <= LOG_DEBUG; i++)
                 if (arg_priorities & (1 << i)) {
-                        match[sizeof(match)-2] = '0' + i;
-
-                        r = sd_journal_add_match(j, match, strlen(match));
+                        r = journal_add_matchf(j, "PRIORITY=%d", i);
                         if (r < 0)
-                                return log_error_errno(r, "Failed to add match: %m");
+                                return r;
                 }
 
-        r = sd_journal_add_conjunction(j);
-        if (r < 0)
-                return log_error_errno(r, "Failed to add conjunction: %m");
-
-        return 0;
+        return sd_journal_add_conjunction(j);
 }
 
 static int add_facilities(sd_journal *j) {
-        void *p;
         int r;
 
+        assert(j);
+
+        if (set_isempty(arg_facilities))
+                return 0;
+
+        void *p;
         SET_FOREACH(p, arg_facilities) {
-                char match[STRLEN("SYSLOG_FACILITY=") + DECIMAL_STR_MAX(int)];
+                r = journal_add_matchf(j, "SYSLOG_FACILITY=%d", PTR_TO_INT(p));
+                if (r < 0)
+                        return r;
+        }
+
+        return sd_journal_add_conjunction(j);
+}
+
+static int add_matches_for_executable(sd_journal *j, const char *path) {
+        _cleanup_free_ char *interpreter = NULL;
+        int r;
 
-                xsprintf(match, "SYSLOG_FACILITY=%d", PTR_TO_INT(p));
+        assert(j);
+        assert(path);
+
+        if (executable_is_script(path, &interpreter) > 0) {
+                _cleanup_free_ char *comm = NULL;
+
+                r = path_extract_filename(path, &comm);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to extract filename of '%s': %m", path);
 
-                r = sd_journal_add_match(j, match, strlen(match));
+                r = journal_add_match_pair(j, "_COMM", strshorten(comm, TASK_COMM_LEN-1));
+                if (r < 0)
+                        return log_error_errno(r, "Failed to add match: %m");
+
+                /* Append _EXE only if the interpreter is not a link. Otherwise, it might be outdated often. */
+                path = is_symlink(interpreter) > 0 ? interpreter : NULL;
+        }
+
+        if (path) {
+                r = journal_add_match_pair(j, "_EXE", path);
                 if (r < 0)
                         return log_error_errno(r, "Failed to add match: %m");
         }
@@ -340,28 +347,17 @@ static int add_facilities(sd_journal *j) {
 
 static int add_matches_for_device(sd_journal *j, const char *devpath) {
         _cleanup_(sd_device_unrefp) sd_device *device = NULL;
-        sd_device *d = NULL;
-        struct stat st;
         int r;
 
         assert(j);
         assert(devpath);
 
-        if (!path_startswith(devpath, "/dev/"))
-                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
-                                       "Devpath does not start with /dev/");
-
-        if (stat(devpath, &st) < 0)
-                return log_error_errno(errno, "Couldn't stat file: %m");
-
-        r = sd_device_new_from_stat_rdev(&device, &st);
+        r = sd_device_new_from_devname(&device, devpath);
         if (r < 0)
-                return log_error_errno(r, "Failed to get device from devnum " DEVNUM_FORMAT_STR ": %m", DEVNUM_FORMAT_VAL(st.st_rdev));
+                return log_error_errno(r, "Failed to get device '%s': %m", devpath);
 
-        for (d = device; d; ) {
-                _cleanup_free_ char *match = NULL;
-                const char *subsys, *sysname, *devnode;
-                sd_device *parent;
+        for (sd_device *d = device; d; ) {
+                const char *subsys, *sysname;
 
                 r = sd_device_get_subsystem(d, &subsys);
                 if (r < 0)
@@ -371,121 +367,87 @@ static int add_matches_for_device(sd_journal *j, const char *devpath) {
                 if (r < 0)
                         goto get_parent;
 
-                match = strjoin("_KERNEL_DEVICE=+", subsys, ":", sysname);
-                if (!match)
-                        return log_oom();
-
-                r = sd_journal_add_match(j, match, 0);
+                r = journal_add_matchf(j, "_KERNEL_DEVICE=+%s:%s", subsys, sysname);
                 if (r < 0)
                         return log_error_errno(r, "Failed to add match: %m");
 
-                if (sd_device_get_devname(d, &devnode) >= 0) {
-                        _cleanup_free_ char *match1 = NULL;
-
-                        r = stat(devnode, &st);
-                        if (r < 0)
-                                return log_error_errno(r, "Failed to stat() device node \"%s\": %m", devnode);
-
-                        r = asprintf(&match1, "_KERNEL_DEVICE=%c" DEVNUM_FORMAT_STR, S_ISBLK(st.st_mode) ? 'b' : 'c', DEVNUM_FORMAT_VAL(st.st_rdev));
-                        if (r < 0)
-                                return log_oom();
-
-                        r = sd_journal_add_match(j, match1, 0);
+                dev_t devnum;
+                if (sd_device_get_devnum(d, &devnum) >= 0) {
+                        r = journal_add_matchf(j, "_KERNEL_DEVICE=%c" DEVNUM_FORMAT_STR,
+                                               streq(subsys, "block") ? 'b' : 'c',
+                                               DEVNUM_FORMAT_VAL(devnum));
                         if (r < 0)
                                 return log_error_errno(r, "Failed to add match: %m");
                 }
 
 get_parent:
-                if (sd_device_get_parent(d, &parent) < 0)
+                if (sd_device_get_parent(d, &d) < 0)
                         break;
-
-                d = parent;
         }
 
-        r = add_match_this_boot(j, arg_machine);
+        return add_match_boot_id(j, SD_ID128_NULL);
+}
+
+static int add_matches_for_path(sd_journal *j, const char *path) {
+        _cleanup_free_ char *p = NULL;
+        struct stat st;
+        int r;
+
+        assert(j);
+        assert(path);
+
+        if (arg_root || arg_machine)
+                return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
+                                       "An extra path in match filter is currently not supported with --root, --image, or -M/--machine.");
+
+        r = chase_and_stat(path, NULL, 0, &p, &st);
         if (r < 0)
-                return log_error_errno(r, "Failed to add match for the current boot: %m");
+                return log_error_errno(r, "Couldn't canonicalize path '%s': %m", path);
 
-        return 0;
+        if (S_ISREG(st.st_mode) && (0111 & st.st_mode))
+                return add_matches_for_executable(j, p);
+
+        if (S_ISCHR(st.st_mode) || S_ISBLK(st.st_mode))
+                return add_matches_for_device(j, p);
+
+        return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "File is neither a device node nor executable: %s", p);
 }
 
 static int add_matches(sd_journal *j, char **args) {
         bool have_term = false;
+        int r;
 
         assert(j);
 
-        STRV_FOREACH(i, args) {
-                int r;
+        if (strv_isempty(args))
+                return 0;
 
+        STRV_FOREACH(i, args)
                 if (streq(*i, "+")) {
                         if (!have_term)
                                 break;
+
                         r = sd_journal_add_disjunction(j);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to add disjunction: %m");
+
                         have_term = false;
 
                 } else if (path_is_absolute(*i)) {
-                        _cleanup_free_ char *p = NULL, *t = NULL, *t2 = NULL, *interpreter = NULL;
-                        struct stat st;
-
-                        r = chase(*i, NULL, CHASE_TRAIL_SLASH, &p, NULL);
+                        r = add_matches_for_path(j, *i);
                         if (r < 0)
-                                return log_error_errno(r, "Couldn't canonicalize path: %m");
-
-                        if (lstat(p, &st) < 0)
-                                return log_error_errno(errno, "Couldn't stat file: %m");
-
-                        if (S_ISREG(st.st_mode) && (0111 & st.st_mode)) {
-                                if (executable_is_script(p, &interpreter) > 0) {
-                                        _cleanup_free_ char *comm = NULL;
-
-                                        r = path_extract_filename(p, &comm);
-                                        if (r < 0)
-                                                return log_error_errno(r, "Failed to extract filename of '%s': %m", p);
-
-                                        t = strjoin("_COMM=", strshorten(comm, TASK_COMM_LEN-1));
-                                        if (!t)
-                                                return log_oom();
-
-                                        /* Append _EXE only if the interpreter is not a link.
-                                           Otherwise, it might be outdated often. */
-                                        if (lstat(interpreter, &st) == 0 && !S_ISLNK(st.st_mode)) {
-                                                t2 = strjoin("_EXE=", interpreter);
-                                                if (!t2)
-                                                        return log_oom();
-                                        }
-                                } else {
-                                        t = strjoin("_EXE=", p);
-                                        if (!t)
-                                                return log_oom();
-                                }
-
-                                r = sd_journal_add_match(j, t, 0);
-
-                                if (r >=0 && t2)
-                                        r = sd_journal_add_match(j, t2, 0);
-
-                        } else if (S_ISCHR(st.st_mode) || S_ISBLK(st.st_mode)) {
-                                r = add_matches_for_device(j, p);
-                                if (r < 0)
-                                        return r;
-                        } else
-                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
-                                                       "File is neither a device node, nor regular file, nor executable: %s",
-                                                       *i);
-
+                                return r;
                         have_term = true;
+
                 } else {
-                        r = sd_journal_add_match(j, *i, 0);
+                        r = sd_journal_add_match(j, *i, SIZE_MAX);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to add match '%s': %m", *i);
                         have_term = true;
                 }
 
-                if (r < 0)
-                        return log_error_errno(r, "Failed to add match '%s': %m", *i);
-        }
-
-        if (!strv_isempty(args) && !have_term)
-                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
-                                       "\"+\" can only be used between terms");
+        if (!have_term)
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "\"+\" can only be used between terms.");
 
         return 0;
 }
@@ -503,7 +465,7 @@ int add_filters(sd_journal *j, char **matches) {
 
         r = add_dmesg(j);
         if (r < 0)
-                return r;
+                return log_error_errno(r, "Failed to add filter for dmesg: %m");
 
         r = add_units(j);
         if (r < 0)
@@ -519,11 +481,11 @@ int add_filters(sd_journal *j, char **matches) {
 
         r = add_priorities(j);
         if (r < 0)
-                return r;
+                return log_error_errno(r, "Failed to add filter for priorities: %m");
 
         r = add_facilities(j);
         if (r < 0)
-                return r;
+                return log_error_errno(r, "Failed to add filter for facilities: %m");
 
         r = add_matches(j, matches);
         if (r < 0)
index c7a49eefea2a35e22a61c01ac10dd9bb64ce4675..47eb72575ab92e86d9bbeab1a174d8a848535f82 100644 (file)
@@ -211,7 +211,6 @@ int action_list_field_names(void) {
 int action_list_namespaces(void) {
         _cleanup_(table_unrefp) Table *table = NULL;
         sd_id128_t machine;
-        char machine_id[SD_ID128_STRING_MAX];
         int r;
 
         assert(arg_action == ACTION_LIST_NAMESPACES);
@@ -220,8 +219,6 @@ int action_list_namespaces(void) {
         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();
@@ -243,27 +240,42 @@ int action_list_namespaces(void) {
                 }
 
                 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))
+                        const char *e = strchr(de->d_name, '.');
+                        if (!e)
+                                continue;
+
+                        _cleanup_free_ char *ids = strndup(de->d_name, e - de->d_name);
+                        if (!ids)
+                                return log_oom();
+
+                        sd_id128_t id;
+                        r = sd_id128_from_string(ids, &id);
+                        if (r < 0)
                                 continue;
 
-                        dot = strchr(de->d_name, '.');
-                        if (!dot)
+                        if (!sd_id128_equal(machine, id))
                                 continue;
 
-                        if (!log_namespace_name_valid(dot + 1))
+                        e++;
+
+                        if (!log_namespace_name_valid(e))
                                 continue;
 
-                        r = table_add_cell(table, NULL, TABLE_STRING, dot + 1);
+                        r = table_add_cell(table, NULL, TABLE_STRING, e);
                         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);
+        if (table_isempty(table) && FLAGS_SET(arg_json_format_flags, JSON_FORMAT_OFF)) {
+                if (!arg_quiet)
+                        log_notice("No namespaces found.");
+        } else {
+                r = table_print_with_pager(table, arg_json_format_flags, arg_pager_flags, !arg_quiet);
+                if (r < 0)
+                        return r;
+        }
 
         return 0;
 }
index 371d6957cf4e538ad24e29562404abcd97f4c3a4..e8ffc72bb8ac34b0be181e5d0ae417702fa7d72f 100644 (file)
@@ -1,5 +1,7 @@
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 
+#include <unistd.h>
+
 #include "sd-event.h"
 
 #include "fileio.h"
@@ -10,7 +12,7 @@
 #include "logs-show.h"
 #include "terminal-util.h"
 
-#define PROCESS_INOTIFY_INTERVAL 1024   /* Every 1,024 messages processed */
+#define PROCESS_INOTIFY_INTERVAL 1024   /* Every 1024 messages processed */
 
 typedef struct Context {
         sd_journal *journal;
index f8f5ac61a9a7bb9ab33d64bd93aedfa39b7dbedd..575488bee1ad74ceb8f6412e49585b519d25bcdc 100644 (file)
@@ -1,5 +1,7 @@
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 
+#include <unistd.h>
+
 #include "journal-util.h"
 #include "journalctl.h"
 #include "journalctl-util.h"
index bbc6aa6dacb9660feeb1af73a1009642fcba0f97..cc598697905010df1018a27f91906bc781a14d23 100644 (file)
@@ -57,7 +57,7 @@ bool arg_show_cursor = false;
 const char *arg_directory = NULL;
 char **arg_file = NULL;
 bool arg_file_stdin = false;
-int arg_priorities = 0xFF;
+int arg_priorities = 0;
 Set *arg_facilities = NULL;
 char *arg_verify_key = NULL;
 #if HAVE_GCRYPT
@@ -265,6 +265,7 @@ static int help(void) {
                "  -N --fields                List all field names currently used\n"
                "  -F --field=FIELD           List all values that a specified field takes\n"
                "     --list-boots            Show terse information about recorded boots\n"
+               "     --list-namespaces       Show list of journal namespaces\n"
                "     --disk-usage            Show total disk usage of all journal files\n"
                "     --vacuum-size=BYTES     Reduce disk usage below specified size\n"
                "     --vacuum-files=INT      Leave only the specified number of journal files\n"
index 11bb6fa1f223ff32d9fbd83686c92ca144e68ce2..7d87754e0e565c858d8935042f1b1e06443b2eff 100644 (file)
@@ -47,7 +47,6 @@ if get_option('link-journalctl-shared')
         journalctl_link_with = [libshared]
 else
         journalctl_link_with = [
-                libbasic_gcrypt,
                 libshared_static,
                 libsystemd_static,
         ]
index 610460b829d52c9995d746a9530ea4e892f9d53f..766d32159502fd99c678e5db9123672edbdddb72 100755 (executable)
@@ -66,7 +66,7 @@ elif [ -f /usr/lib/os-release ]; then
     . /usr/lib/os-release
 fi
 
-[ -n "$PRETTY_NAME" ] || PRETTY_NAME="Linux"
+[ -n "$PRETTY_NAME" ] || PRETTY_NAME="Linux $KERNEL_VERSION"
 
 SORT_KEY="$IMAGE_ID"
 [ -z "$SORT_KEY" ] && SORT_KEY="$ID"
@@ -182,7 +182,7 @@ mkdir -p "${LOADER_ENTRY%/*}" || {
 {
     echo "# Boot Loader Specification type#1 entry"
     echo "# File created by $0 (systemd {{VERSION_TAG}})"
-    echo "title      $PRETTY_NAME $KERNEL_VERSION"
+    echo "title      $PRETTY_NAME"
     echo "version    $KERNEL_VERSION"
     if [ "$ENTRY_TOKEN" = "$MACHINE_ID" ]; then
         # See similar logic above for the systemd.machine_id= kernel command line option
index 1f4ad095d5dedc147355daaf6a85747276ee4d27..2e73983d6bc474ea6a9c3b64301b6edd0af11f33 100644 (file)
@@ -3,15 +3,16 @@
   Copyright © 2013 Intel Corporation. All rights reserved.
 ***/
 
+/* Make sure the net/if.h header is included before any linux/ one */
+#include <net/if.h>
 #include <errno.h>
+#include <linux/filter.h>
+#include <linux/if_infiniband.h>
+#include <linux/if_packet.h>
 #include <net/ethernet.h>
-#include <net/if.h>
 #include <net/if_arp.h>
 #include <stdio.h>
 #include <string.h>
-#include <linux/filter.h>
-#include <linux/if_infiniband.h>
-#include <linux/if_packet.h>
 
 #include "dhcp-network.h"
 #include "dhcp-protocol.h"
index 3fbfc028e99e70ab83dc44cd1e081439d33d1fb6..ecd62ea802c48ec50b2d44684cd5ac59a8dee6a9 100644 (file)
@@ -84,9 +84,8 @@ struct sd_dhcp6_client {
         bool send_release;
 };
 
-int dhcp6_network_bind_udp_socket(int ifindex, struct in6_addr *address);
-int dhcp6_network_send_udp_socket(int s, struct in6_addr *address,
-                                  const void *packet, size_t len);
+int dhcp6_network_bind_udp_socket(int ifindex, const struct in6_addr *address);
+int dhcp6_network_send_udp_socket(int s, const struct in6_addr *address, const void *packet, size_t len);
 
 int dhcp6_client_send_message(sd_dhcp6_client *client);
 int dhcp6_client_set_transaction_id(sd_dhcp6_client *client, uint32_t transaction_id);
index a3e4e19e8e164cf839e31c8fe7323615926c87e3..0aa8469cc39dbc10e82c37021460e436acdfafbc 100644 (file)
 #include "fd-util.h"
 #include "socket-util.h"
 
-int dhcp6_network_bind_udp_socket(int ifindex, struct in6_addr *local_address) {
+int dhcp6_network_bind_udp_socket(int ifindex, const struct in6_addr *local_address) {
         union sockaddr_union src = {
                 .in6.sin6_family = AF_INET6,
+                .in6.sin6_addr = *ASSERT_PTR(local_address),
                 .in6.sin6_port = htobe16(DHCP6_PORT_CLIENT),
                 .in6.sin6_scope_id = ifindex,
         };
@@ -27,9 +28,6 @@ int dhcp6_network_bind_udp_socket(int ifindex, struct in6_addr *local_address) {
         int r;
 
         assert(ifindex > 0);
-        assert(local_address);
-
-        src.in6.sin6_addr = *local_address;
 
         s = socket(AF_INET6, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, IPPROTO_UDP);
         if (s < 0)
@@ -58,20 +56,14 @@ int dhcp6_network_bind_udp_socket(int ifindex, struct in6_addr *local_address) {
         return TAKE_FD(s);
 }
 
-int dhcp6_network_send_udp_socket(int s, struct in6_addr *server_address,
-                                  const void *packet, size_t len) {
+int dhcp6_network_send_udp_socket(int s, const struct in6_addr *server_address, const void *packet, size_t len) {
         union sockaddr_union dest = {
                 .in6.sin6_family = AF_INET6,
+                .in6.sin6_addr = *ASSERT_PTR(server_address),
                 .in6.sin6_port = htobe16(DHCP6_PORT_SERVER),
         };
-        int r;
-
-        assert(server_address);
 
-        memcpy(&dest.in6.sin6_addr, server_address, sizeof(dest.in6.sin6_addr));
-
-        r = sendto(s, packet, len, 0, &dest.sa, sizeof(dest.in6));
-        if (r < 0)
+        if (sendto(s, packet, len, 0, &dest.sa, sizeof(dest.in6)) < 0)
                 return -errno;
 
         return 0;
index c70f93203d199b985311af2334a9d3c598f273e9..ab75bad434a9b9839049404679ab8aeba0a85b42 100644 (file)
@@ -28,9 +28,11 @@ typedef struct DHCP6Message DHCP6Message;
 #define DHCP6_MIN_OPTIONS_SIZE \
         1280 - sizeof(struct ip6_hdr) - sizeof(struct udphdr)
 
-#define IN6ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS_INIT                 \
-        { { { 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,           \
-              0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02 } } }
+#define IN6_ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS                 \
+        ((const struct in6_addr) { { {                              \
+                0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,     \
+                0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02,     \
+         } } } )
 
 enum {
         DHCP6_PORT_SERVER                       = 547,
index 2d4284476c60cbd885fe985af14c9e4d36aedf2a..2b6e3357e9558abe60cc4c10a2cb0324c1ebaed6 100644 (file)
 
 static int test_dhcp_fd[2] = EBADF_PAIR;
 
-int dhcp6_network_send_udp_socket(int s, struct in6_addr *server_address, const void *packet, size_t len) {
+int dhcp6_network_send_udp_socket(int s, const struct in6_addr *server_address, const void *packet, size_t len) {
         return len;
 }
 
-int dhcp6_network_bind_udp_socket(int index, struct in6_addr *local_address) {
+int dhcp6_network_bind_udp_socket(int index, const struct in6_addr *local_address) {
         assert_se(socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0, test_dhcp_fd) >= 0);
         return TAKE_FD(test_dhcp_fd[0]);
 }
index eed670fb6f017180c09784f02be677aa3ecdfa39..780a5d7f5a92e291408a92f58f0874f62b69cb5a 100644 (file)
@@ -5,6 +5,7 @@
 #include <unistd.h>
 
 #include "sd-ndisc.h"
+#include "sd-radv.h"
 
 #include "alloc-util.h"
 #include "fd-util.h"
@@ -31,15 +32,33 @@ static void test_with_sd_ndisc(const uint8_t *data, size_t size) {
         assert_se(write(test_fd[1], data, size) == (ssize_t) size);
         (void) sd_event_run(e, UINT64_MAX);
         assert_se(sd_ndisc_stop(nd) >= 0);
-        close(test_fd[1]);
+        test_fd[1] = safe_close(test_fd[1]);
+        TAKE_FD(test_fd[0]); /* It should be already closed by sd_ndisc_stop(). */
 }
 
-static void test_with_icmp6_packet(const uint8_t *data, size_t size) {
-        static const struct sockaddr_in6 dst = {
-                .sin6_family = AF_INET6,
-                .sin6_addr = IN6ADDR_ALL_ROUTERS_MULTICAST_INIT,
+static void test_with_sd_radv(const uint8_t *data, size_t size) {
+        struct ether_addr mac_addr = {
+                .ether_addr_octet = {'A', 'B', 'C', '1', '2', '3'}
         };
+        _cleanup_(sd_event_unrefp) sd_event *e = NULL;
+        _cleanup_(sd_radv_unrefp) sd_radv *ra = NULL;
+
+        assert_se(socketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC | SOCK_NONBLOCK, 0, test_fd) >= 0);
 
+        assert_se(sd_event_new(&e) >= 0);
+        assert_se(sd_radv_new(&ra) >= 0);
+        assert_se(sd_radv_attach_event(ra, e, 0) >= 0);
+        assert_se(sd_radv_set_ifindex(ra, 42) >= 0);
+        assert_se(sd_radv_set_mac(ra, &mac_addr) >= 0);
+        assert_se(sd_radv_start(ra) >= 0);
+        assert_se(write(test_fd[0], data, size) == (ssize_t) size);
+        (void) sd_event_run(e, UINT64_MAX);
+        assert_se(sd_radv_stop(ra) >= 0);
+        test_fd[0] = safe_close(test_fd[0]);
+        TAKE_FD(test_fd[1]); /* It should be already closed by sd_radv_stop(). */
+}
+
+static void test_with_icmp6_packet(const uint8_t *data, size_t size) {
         _cleanup_close_pair_ int fd_pair[2] = EBADF_PAIR;
         _cleanup_(icmp6_packet_unrefp) ICMP6Packet *packet = NULL;
         _cleanup_set_free_ Set *options = NULL;
@@ -53,7 +72,8 @@ static void test_with_icmp6_packet(const uint8_t *data, size_t size) {
         if (ndisc_parse_options(packet, &options) < 0)
                 return;
 
-        if (ndisc_send(fd_pair[1], &dst, icmp6_packet_get_header(packet), options) < 0)
+        if (ndisc_send(fd_pair[1], &IN6_ADDR_ALL_ROUTERS_MULTICAST,
+                       icmp6_packet_get_header(packet), options, /* timestamp = */ 0) < 0)
                 return;
 
         packet = icmp6_packet_unref(packet);
@@ -62,7 +82,7 @@ static void test_with_icmp6_packet(const uint8_t *data, size_t size) {
         if (icmp6_packet_receive(fd_pair[0], &packet) < 0)
                 return;
 
-        (void) ndisc_parse_options(packet, &options);
+        assert_se(ndisc_parse_options(packet, &options) >= 0);
 }
 
 int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
@@ -72,6 +92,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
         fuzz_setup_logging();
 
         test_with_sd_ndisc(data, size);
+        test_with_sd_radv(data, size);
         test_with_icmp6_packet(data, size);
         return 0;
 }
index 35459d11a29003a81c18ed0347880b4889e4703f..21d744beaa49b84f6d5d708d5049b1fba67dc0b9 100644 (file)
@@ -27,6 +27,17 @@ static ICMP6Packet* icmp6_packet_new(size_t size) {
         return p;
 }
 
+int icmp6_packet_set_sender_address(ICMP6Packet *p, const struct in6_addr *addr) {
+        assert(p);
+
+        if (addr)
+                p->sender_address = *addr;
+        else
+                p->sender_address = (const struct in6_addr) {};
+
+        return 0;
+}
+
 int icmp6_packet_get_sender_address(ICMP6Packet *p, struct in6_addr *ret) {
         assert(p);
 
index 7d02534a8c27c84042b7f2960149e063ae906d41..16f354fc95a4fc033f4c89fa79ff7def60373c0f 100644 (file)
@@ -21,6 +21,7 @@ ICMP6Packet* icmp6_packet_ref(ICMP6Packet *p);
 ICMP6Packet* icmp6_packet_unref(ICMP6Packet *p);
 DEFINE_TRIVIAL_CLEANUP_FUNC(ICMP6Packet*, icmp6_packet_unref);
 
+int icmp6_packet_set_sender_address(ICMP6Packet *p, const struct in6_addr *addr);
 int icmp6_packet_get_sender_address(ICMP6Packet *p, struct in6_addr *ret);
 int icmp6_packet_get_timestamp(ICMP6Packet *p, clockid_t clock, usec_t *ret);
 const struct icmp6_hdr* icmp6_packet_get_header(ICMP6Packet *p);
index c400e4205ec24456b2a6fdfea09774f431f21cef..d6d505717d8ecb366b0b9e8466b4df46995c2e04 100644 (file)
@@ -23,7 +23,7 @@ int icmp6_bind(int ifindex, bool is_router) {
         return test_fd[is_router];
 }
 
-int icmp6_send(int fd, const struct sockaddr_in6 *dst, const struct iovec *iov, size_t n_iov) {
+int icmp6_send(int fd, const struct in6_addr *dst, const struct iovec *iov, size_t n_iov) {
         return writev(fd, iov, n_iov);
 }
 
index 7b3786e518e60a23816a323becfecf37b2385bfa..a28f175b5e5afb5f1f477dbefe3a9310d630c54b 100644 (file)
@@ -3,7 +3,10 @@
   Copyright © 2014 Intel Corporation. All rights reserved.
 ***/
 
+/* Make sure the net/if.h header is included before any linux/ one */
+#include <net/if.h>
 #include <errno.h>
+#include <linux/if_packet.h>
 #include <netinet/icmp6.h>
 #include <netinet/in.h>
 #include <netinet/ip6.h>
@@ -11,8 +14,6 @@
 #include <string.h>
 #include <sys/types.h>
 #include <unistd.h>
-#include <net/if.h>
-#include <linux/if_packet.h>
 
 #include "fd-util.h"
 #include "icmp6-util.h"
@@ -32,16 +33,18 @@ int icmp6_bind(int ifindex, bool is_router) {
         ICMP6_FILTER_SETBLOCKALL(&filter);
         if (is_router) {
                 mreq = (struct ipv6_mreq) {
-                        .ipv6mr_multiaddr = IN6ADDR_ALL_ROUTERS_MULTICAST_INIT,
+                        .ipv6mr_multiaddr = IN6_ADDR_ALL_ROUTERS_MULTICAST,
                         .ipv6mr_interface = ifindex,
                 };
                 ICMP6_FILTER_SETPASS(ND_ROUTER_SOLICIT, &filter);
         } else {
                 mreq = (struct ipv6_mreq) {
-                        .ipv6mr_multiaddr = IN6ADDR_ALL_NODES_MULTICAST_INIT,
+                        .ipv6mr_multiaddr = IN6_ADDR_ALL_NODES_MULTICAST,
                         .ipv6mr_interface = ifindex,
                 };
                 ICMP6_FILTER_SETPASS(ND_ROUTER_ADVERT, &filter);
+                ICMP6_FILTER_SETPASS(ND_NEIGHBOR_ADVERT, &filter);
+                ICMP6_FILTER_SETPASS(ND_REDIRECT, &filter);
         }
 
         s = socket(AF_INET6, SOCK_RAW | SOCK_CLOEXEC | SOCK_NONBLOCK, IPPROTO_ICMPV6);
@@ -88,14 +91,20 @@ int icmp6_bind(int ifindex, bool is_router) {
         return TAKE_FD(s);
 }
 
-int icmp6_send(int fd, const struct sockaddr_in6 *dst, const struct iovec *iov, size_t n_iov) {
+int icmp6_send(int fd, const struct in6_addr *dst, const struct iovec *iov, size_t n_iov) {
+        struct sockaddr_in6 sa = {
+                .sin6_family = AF_INET6,
+                .sin6_addr = *ASSERT_PTR(dst),
+        };
         struct msghdr msg = {
-                .msg_name = (struct sockaddr_in6*) dst,
+                .msg_name = &sa,
                 .msg_namelen = sizeof(struct sockaddr_in6),
                 .msg_iov = (struct iovec*) iov,
                 .msg_iovlen = n_iov,
         };
 
+        assert(fd >= 0);
+
         if (sendmsg(fd, &msg, 0) < 0)
                 return -errno;
 
index 49cdcba3ff711f89d80f8eff57f58af98fc02e47..9e5063f51ba4b7b4c1b17d43f9a6de666d9b67fb 100644 (file)
 
 #include "time-util.h"
 
-#define IN6ADDR_ALL_ROUTERS_MULTICAST_INIT \
-        { { { 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
-              0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 } } }
+#define IN6_ADDR_ALL_ROUTERS_MULTICAST                                  \
+        ((const struct in6_addr) { { {                                  \
+                0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,         \
+                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,         \
+         } } } )
 
-#define IN6ADDR_ALL_NODES_MULTICAST_INIT \
-        { { { 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
-              0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 } } }
+#define IN6_ADDR_ALL_NODES_MULTICAST                                    \
+        ((const struct in6_addr) { { {                                  \
+                0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,         \
+                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,         \
+         } } } )
 
 int icmp6_bind(int ifindex, bool is_router);
-int icmp6_send(int fd, const struct sockaddr_in6 *dst, const struct iovec *iov, size_t n_iov);
+int icmp6_send(int fd, const struct in6_addr *dst, const struct iovec *iov, size_t n_iov);
 int icmp6_receive(
                 int fd,
                 void *buffer,
index e4340f995794756f0a098b4afdf0c5b7000e8ad5..fdd8806a2324bcffdba9c7c56ee7e2a1ec3a12b8 100644 (file)
@@ -28,7 +28,10 @@ sources = files(
         'sd-lldp-rx.c',
         'sd-lldp-tx.c',
         'sd-ndisc.c',
+        'sd-ndisc-neighbor.c',
+        'sd-ndisc-redirect.c',
         'sd-ndisc-router.c',
+        'sd-ndisc-router-solicit.c',
         'sd-radv.c',
 )
 
@@ -97,6 +100,10 @@ executables += [
                         'icmp6-util-unix.c',
                 ),
         },
+        network_test_template + {
+                'sources' : files('test-ndisc-send.c'),
+                'type' : 'manual',
+        },
         network_test_template + {
                 'sources' : files('test-sd-dhcp-lease.c'),
         },
diff --git a/src/libsystemd-network/ndisc-neighbor-internal.h b/src/libsystemd-network/ndisc-neighbor-internal.h
new file mode 100644 (file)
index 0000000..aee6556
--- /dev/null
@@ -0,0 +1,21 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include "sd-ndisc.h"
+
+#include "icmp6-packet.h"
+#include "set.h"
+
+struct sd_ndisc_neighbor {
+        unsigned n_ref;
+
+        ICMP6Packet *packet;
+
+        uint32_t flags;
+        struct in6_addr target_address;
+
+        Set *options;
+};
+
+sd_ndisc_neighbor* ndisc_neighbor_new(ICMP6Packet *packet);
+int ndisc_neighbor_parse(sd_ndisc *nd, sd_ndisc_neighbor *na);
index 61c58d2971758c151c9b96a9689487115f99b69c..901a3b399bb12d0fad2efde6fab1e5f27e75c65e 100644 (file)
@@ -131,6 +131,7 @@ static int ndisc_option_compare_func(const sd_ndisc_option *x, const sd_ndisc_op
         case SD_NDISC_OPTION_TARGET_LL_ADDRESS:
         case SD_NDISC_OPTION_REDIRECTED_HEADER:
         case SD_NDISC_OPTION_MTU:
+        case SD_NDISC_OPTION_HOME_AGENT:
         case SD_NDISC_OPTION_FLAGS_EXTENSION:
         case SD_NDISC_OPTION_CAPTIVE_PORTAL:
                 /* These options cannot be specified multiple times. */
@@ -179,6 +180,7 @@ static void ndisc_option_hash_func(const sd_ndisc_option *option, struct siphash
         case SD_NDISC_OPTION_TARGET_LL_ADDRESS:
         case SD_NDISC_OPTION_REDIRECTED_HEADER:
         case SD_NDISC_OPTION_MTU:
+        case SD_NDISC_OPTION_HOME_AGENT:
         case SD_NDISC_OPTION_FLAGS_EXTENSION:
         case SD_NDISC_OPTION_CAPTIVE_PORTAL:
                 break;
@@ -211,6 +213,9 @@ DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(
                 ndisc_option_free);
 
 static int ndisc_option_consume(Set **options, sd_ndisc_option *p) {
+        assert(options);
+        assert(p);
+
         if (set_size(*options) >= MAX_OPTIONS) {
                 ndisc_option_free(p);
                 return -ETOOMANYREFS; /* recognizable error code */
@@ -219,7 +224,7 @@ static int ndisc_option_consume(Set **options, sd_ndisc_option *p) {
         return set_ensure_consume(options, &ndisc_option_hash_ops, p);
 }
 
-int ndisc_option_add_raw(Set **options, size_t offset, size_t length, const uint8_t *bytes) {
+int ndisc_option_set_raw(Set **options, size_t length, const uint8_t *bytes) {
         _cleanup_free_ uint8_t *copy = NULL;
 
         assert(options);
@@ -232,7 +237,7 @@ int ndisc_option_add_raw(Set **options, size_t offset, size_t length, const uint
         if (!copy)
                 return -ENOMEM;
 
-        sd_ndisc_option *p = ndisc_option_new(/* type = */ 0, offset);
+        sd_ndisc_option *p = ndisc_option_new(/* type = */ 0, /* offset = */ 0);
         if (!p)
                 return -ENOMEM;
 
@@ -257,15 +262,29 @@ static int ndisc_option_build_raw(const sd_ndisc_option *option, uint8_t **ret)
         return 0;
 }
 
-int ndisc_option_add_link_layer_address(Set **options, uint8_t opt, size_t offset, const struct ether_addr *mac) {
+int ndisc_option_add_link_layer_address(Set **options, uint8_t type, size_t offset, const struct ether_addr *mac) {
         assert(options);
-        assert(IN_SET(opt, SD_NDISC_OPTION_SOURCE_LL_ADDRESS, SD_NDISC_OPTION_TARGET_LL_ADDRESS));
-        assert(mac);
+        assert(IN_SET(type, SD_NDISC_OPTION_SOURCE_LL_ADDRESS, SD_NDISC_OPTION_TARGET_LL_ADDRESS));
 
-        if (ether_addr_is_null(mac))
-                return -EINVAL;
+        if (!mac || ether_addr_is_null(mac)) {
+                ndisc_option_remove_by_type(*options, type);
+                return 0;
+        }
 
-        sd_ndisc_option *p = ndisc_option_new(opt, offset);
+        sd_ndisc_option *p = ndisc_option_get_by_type(*options, type);
+        if (p) {
+                /* offset == 0 means that we are now building a packet to be sent, and in that case we allow
+                 * to override the option we previously set.
+                 * offset != 0 means that we are now parsing a received packet, and we refuse to override
+                 * conflicting options. */
+                if (offset != 0)
+                        return -EEXIST;
+
+                p->mac = *mac;
+                return 0;
+        }
+
+        p = ndisc_option_new(type, offset);
         if (!p)
                 return -ENOMEM;
 
@@ -287,6 +306,9 @@ static int ndisc_option_parse_link_layer_address(Set **options, size_t offset, s
         struct ether_addr mac;
         memcpy(&mac, opt + 2, sizeof(struct ether_addr));
 
+        if (ether_addr_is_null(&mac))
+                return -EBADMSG;
+
         return ndisc_option_add_link_layer_address(options, opt[0], offset, &mac);
 }
 
@@ -309,14 +331,16 @@ static int ndisc_option_build_link_layer_address(const sd_ndisc_option *option,
         return 0;
 }
 
-int ndisc_option_add_prefix(
+int ndisc_option_add_prefix_internal(
                 Set **options,
                 size_t offset,
                 uint8_t flags,
                 uint8_t prefixlen,
                 const struct in6_addr *address,
                 usec_t valid_lifetime,
-                usec_t preferred_lifetime) {
+                usec_t preferred_lifetime,
+                usec_t valid_until,
+                usec_t preferred_until) {
 
         assert(options);
         assert(address);
@@ -324,26 +348,53 @@ int ndisc_option_add_prefix(
         if (prefixlen > 128)
                 return -EINVAL;
 
-        if (in6_addr_is_link_local(address))
+        struct in6_addr addr = *address;
+        in6_addr_mask(&addr, prefixlen);
+
+        /* RFC 4861 and 4862 only state that link-local prefix should be ignored.
+         * But here we also ignore null and multicast addresses. */
+        if (in6_addr_is_link_local(&addr) || in6_addr_is_null(&addr) || in6_addr_is_multicast(&addr))
                 return -EINVAL;
 
         if (preferred_lifetime > valid_lifetime)
                 return -EINVAL;
 
-        sd_ndisc_option *p = ndisc_option_new(SD_NDISC_OPTION_PREFIX_INFORMATION, offset);
+        if (preferred_until > valid_until)
+                return -EINVAL;
+
+        sd_ndisc_option *p = ndisc_option_get(
+                                *options,
+                                &(const sd_ndisc_option) {
+                                        .type = SD_NDISC_OPTION_PREFIX_INFORMATION,
+                                        .prefix.prefixlen = prefixlen,
+                                        .prefix.address = addr,
+                                });
+        if (p) {
+                if (offset != 0)
+                        return -EEXIST;
+
+                p->prefix.flags = flags;
+                p->prefix.valid_lifetime = valid_lifetime;
+                p->prefix.preferred_lifetime = preferred_lifetime;
+                p->prefix.valid_until = valid_until;
+                p->prefix.preferred_until = preferred_until;
+                return 0;
+        }
+
+        p = ndisc_option_new(SD_NDISC_OPTION_PREFIX_INFORMATION, offset);
         if (!p)
                 return -ENOMEM;
 
         p->prefix = (sd_ndisc_prefix) {
                 .flags = flags,
                 .prefixlen = prefixlen,
-                .address = *address,
+                .address = addr,
                 .valid_lifetime = valid_lifetime,
                 .preferred_lifetime = preferred_lifetime,
+                .valid_until = valid_until,
+                .preferred_until = preferred_until,
         };
 
-        in6_addr_mask(&p->prefix.address, p->prefix.prefixlen);
-
         return ndisc_option_consume(options, p);
 }
 
@@ -366,10 +417,12 @@ static int ndisc_option_parse_prefix(Set **options, size_t offset, size_t len, c
         if (FLAGS_SET(flags, ND_OPT_PI_FLAG_AUTO) && pi->nd_opt_pi_prefix_len != 64)
                 flags &= ~ND_OPT_PI_FLAG_AUTO;
 
-        return ndisc_option_add_prefix(options, offset, flags, pi->nd_opt_pi_prefix_len, &pi->nd_opt_pi_prefix, valid, pref);
+        return ndisc_option_add_prefix(options, offset, flags,
+                                       pi->nd_opt_pi_prefix_len, &pi->nd_opt_pi_prefix,
+                                       valid, pref);
 }
 
-static int ndisc_option_build_prefix(const sd_ndisc_option *option, uint8_t **ret) {
+static int ndisc_option_build_prefix(const sd_ndisc_option *option, usec_t timestamp, uint8_t **ret) {
         assert(option);
         assert(option->type == SD_NDISC_OPTION_PREFIX_INFORMATION);
         assert(ret);
@@ -380,13 +433,19 @@ static int ndisc_option_build_prefix(const sd_ndisc_option *option, uint8_t **re
         if (!buf)
                 return -ENOMEM;
 
+        usec_t valid = MIN(option->prefix.valid_lifetime,
+                           usec_sub_unsigned(option->prefix.valid_until, timestamp));
+        usec_t pref = MIN3(valid,
+                           option->prefix.preferred_lifetime,
+                           usec_sub_unsigned(option->prefix.preferred_until, timestamp));
+
         *buf = (struct nd_opt_prefix_info) {
                 .nd_opt_pi_type = SD_NDISC_OPTION_PREFIX_INFORMATION,
                 .nd_opt_pi_len = sizeof(struct nd_opt_prefix_info) / 8,
                 .nd_opt_pi_prefix_len = option->prefix.prefixlen,
                 .nd_opt_pi_flags_reserved = option->prefix.flags,
-                .nd_opt_pi_valid_time = usec_to_be32_sec(option->prefix.valid_lifetime),
-                .nd_opt_pi_preferred_time = usec_to_be32_sec(option->prefix.preferred_lifetime),
+                .nd_opt_pi_valid_time = usec_to_be32_sec(valid),
+                .nd_opt_pi_preferred_time = usec_to_be32_sec(pref),
                 .nd_opt_pi_prefix = option->prefix.address,
         };
 
@@ -396,9 +455,22 @@ static int ndisc_option_build_prefix(const sd_ndisc_option *option, uint8_t **re
 
 int ndisc_option_add_redirected_header(Set **options, size_t offset, const struct ip6_hdr *hdr) {
         assert(options);
-        assert(hdr);
 
-        sd_ndisc_option *p = ndisc_option_new(SD_NDISC_OPTION_REDIRECTED_HEADER, offset);
+        if (!hdr) {
+                ndisc_option_remove_by_type(*options, SD_NDISC_OPTION_REDIRECTED_HEADER);
+                return 0;
+        }
+
+        sd_ndisc_option *p = ndisc_option_get_by_type(*options, SD_NDISC_OPTION_REDIRECTED_HEADER);
+        if (p) {
+                if (offset != 0)
+                        return -EEXIST;
+
+                memcpy(&p->hdr, hdr, sizeof(struct ip6_hdr));
+                return 0;
+        }
+
+        p = ndisc_option_new(SD_NDISC_OPTION_REDIRECTED_HEADER, offset);
         if (!p)
                 return -ENOMEM;
 
@@ -453,7 +525,16 @@ int ndisc_option_add_mtu(Set **options, size_t offset, uint32_t mtu) {
         if (mtu < IPV6_MIN_MTU)
                 return -EINVAL;
 
-        sd_ndisc_option *p = ndisc_option_new(SD_NDISC_OPTION_MTU, offset);
+        sd_ndisc_option *p = ndisc_option_get_by_type(*options, SD_NDISC_OPTION_MTU);
+        if (p) {
+                if (offset != 0)
+                        return -EEXIST;
+
+                p->mtu = mtu;
+                return 0;
+        }
+
+        p = ndisc_option_new(SD_NDISC_OPTION_MTU, offset);
         if (!p)
                 return -ENOMEM;
 
@@ -497,13 +578,94 @@ static int ndisc_option_build_mtu(const sd_ndisc_option *option, uint8_t **ret)
         return 0;
 }
 
-int ndisc_option_add_route(
+int ndisc_option_add_home_agent_internal(
+                Set **options,
+                size_t offset,
+                uint16_t preference,
+                usec_t lifetime,
+                usec_t valid_until) {
+
+        assert(options);
+
+        if (lifetime > UINT16_MAX * USEC_PER_SEC)
+                return -EINVAL;
+
+        sd_ndisc_option *p = ndisc_option_get_by_type(*options, SD_NDISC_OPTION_HOME_AGENT);
+        if (p) {
+                if (offset != 0)
+                        return -EEXIST;
+
+                p->home_agent = (sd_ndisc_home_agent) {
+                        .preference = preference,
+                        .lifetime = lifetime,
+                        .valid_until = valid_until,
+                };
+                return 0;
+        }
+
+        p = ndisc_option_new(SD_NDISC_OPTION_HOME_AGENT, offset);
+        if (!p)
+                return -ENOMEM;
+
+        p->home_agent = (sd_ndisc_home_agent) {
+                .preference = preference,
+                .lifetime = lifetime,
+                .valid_until = valid_until,
+        };
+
+        return ndisc_option_consume(options, p);
+}
+
+static int ndisc_option_parse_home_agent(Set **options, size_t offset, size_t len, const uint8_t *opt) {
+        const struct nd_opt_home_agent_info *p = (const struct nd_opt_home_agent_info*) ASSERT_PTR(opt);
+
+        assert(options);
+
+        if (len != sizeof(struct nd_opt_home_agent_info))
+                return -EBADMSG;
+
+        if (p->nd_opt_home_agent_info_type != SD_NDISC_OPTION_HOME_AGENT)
+                return -EBADMSG;
+
+        return ndisc_option_add_home_agent(
+                        options, offset,
+                        be16toh(p->nd_opt_home_agent_info_preference),
+                        be16_sec_to_usec(p->nd_opt_home_agent_info_lifetime, /* max_as_infinity = */ false));
+}
+
+static int ndisc_option_build_home_agent(const sd_ndisc_option *option, usec_t timestamp, uint8_t **ret) {
+        assert(option);
+        assert(option->type == SD_NDISC_OPTION_HOME_AGENT);
+        assert(ret);
+
+        assert_cc(sizeof(struct nd_opt_home_agent_info) % 8 == 0);
+
+        usec_t lifetime = MIN(option->home_agent.lifetime,
+                              usec_sub_unsigned(option->home_agent.valid_until, timestamp));
+
+        _cleanup_free_ struct nd_opt_home_agent_info *buf = new(struct nd_opt_home_agent_info, 1);
+        if (!buf)
+                return -ENOMEM;
+
+        *buf = (struct nd_opt_home_agent_info) {
+                .nd_opt_home_agent_info_type = SD_NDISC_OPTION_HOME_AGENT,
+                .nd_opt_home_agent_info_len = sizeof(struct nd_opt_home_agent_info) / 8,
+                .nd_opt_home_agent_info_preference = htobe16(option->home_agent.preference),
+                .nd_opt_home_agent_info_lifetime = usec_to_be16_sec(lifetime),
+        };
+
+        *ret = (uint8_t*) TAKE_PTR(buf);
+        return 0;
+}
+
+int ndisc_option_add_route_internal(
                 Set **options,
                 size_t offset,
                 uint8_t preference,
                 uint8_t prefixlen,
                 const struct in6_addr *prefix,
-                usec_t lifetime) {
+                usec_t lifetime,
+                usec_t valid_until) {
 
         assert(options);
         assert(prefix);
@@ -519,19 +681,38 @@ int ndisc_option_add_route(
         if (!IN_SET(preference, SD_NDISC_PREFERENCE_LOW, SD_NDISC_PREFERENCE_MEDIUM, SD_NDISC_PREFERENCE_HIGH))
                 return -EINVAL;
 
-        sd_ndisc_option *p = ndisc_option_new(SD_NDISC_OPTION_ROUTE_INFORMATION, offset);
+        struct in6_addr addr = *prefix;
+        in6_addr_mask(&addr, prefixlen);
+
+        sd_ndisc_option *p = ndisc_option_get(
+                                *options,
+                                &(const sd_ndisc_option) {
+                                        .type = SD_NDISC_OPTION_ROUTE_INFORMATION,
+                                        .route.prefixlen = prefixlen,
+                                        .route.address = addr,
+                                });
+        if (p) {
+                if (offset != 0)
+                        return -EEXIST;
+
+                p->route.preference = preference;
+                p->route.lifetime = lifetime;
+                p->route.valid_until = valid_until;
+                return 0;
+        }
+
+        p = ndisc_option_new(SD_NDISC_OPTION_ROUTE_INFORMATION, offset);
         if (!p)
                 return -ENOMEM;
 
         p->route = (sd_ndisc_route) {
                 .preference = preference,
                 .prefixlen = prefixlen,
-                .address = *prefix,
+                .address = addr,
                 .lifetime = lifetime,
+                .valid_until = valid_until,
         };
 
-        in6_addr_mask(&p->route.address, p->route.prefixlen);
-
         return ndisc_option_consume(options, p);
 }
 
@@ -562,14 +743,15 @@ static int ndisc_option_parse_route(Set **options, size_t offset, size_t len, co
         return ndisc_option_add_route(options, offset, preference, prefixlen, &prefix, lifetime);
 }
 
-static int ndisc_option_build_route(const sd_ndisc_option *option, uint8_t **ret) {
+static int ndisc_option_build_route(const sd_ndisc_option *option, usec_t timestamp, uint8_t **ret) {
         assert(option);
         assert(option->type == SD_NDISC_OPTION_ROUTE_INFORMATION);
         assert(option->route.prefixlen <= 128);
         assert(ret);
 
         size_t len = 1 + DIV_ROUND_UP(option->route.prefixlen, 64);
-        be32_t lifetime = usec_to_be32_sec(option->route.lifetime);
+        be32_t lifetime = usec_to_be32_sec(MIN(option->route.lifetime,
+                                               usec_sub_unsigned(option->route.valid_until, timestamp)));
 
         _cleanup_free_ uint8_t *buf = new(uint8_t, len * 8);
         if (!buf)
@@ -586,12 +768,13 @@ static int ndisc_option_build_route(const sd_ndisc_option *option, uint8_t **ret
         return 0;
 }
 
-int ndisc_option_add_rdnss(
+int ndisc_option_add_rdnss_internal(
                 Set **options,
                 size_t offset,
                 size_t n_addresses,
                 const struct in6_addr *addresses,
-                usec_t lifetime) {
+                usec_t lifetime,
+                usec_t valid_until) {
 
         assert(options);
         assert(addresses);
@@ -611,6 +794,7 @@ int ndisc_option_add_rdnss(
                 .n_addresses = n_addresses,
                 .addresses = TAKE_PTR(addrs),
                 .lifetime = lifetime,
+                .valid_until = valid_until,
         };
 
         return ndisc_option_consume(options, p);
@@ -632,13 +816,14 @@ static int ndisc_option_parse_rdnss(Set **options, size_t offset, size_t len, co
         return ndisc_option_add_rdnss(options, offset, n_addrs, (const struct in6_addr*) (opt + 8), lifetime);
 }
 
-static int ndisc_option_build_rdnss(const sd_ndisc_option *option, uint8_t **ret) {
+static int ndisc_option_build_rdnss(const sd_ndisc_option *option, usec_t timestamp, uint8_t **ret) {
         assert(option);
         assert(option->type == SD_NDISC_OPTION_RDNSS);
         assert(ret);
 
         size_t len = option->rdnss.n_addresses * 2 + 1;
-        be32_t lifetime = usec_to_be32_sec(option->rdnss.lifetime);
+        be32_t lifetime = usec_to_be32_sec(MIN(option->rdnss.lifetime,
+                                               usec_sub_unsigned(option->rdnss.valid_until, timestamp)));
 
         _cleanup_free_ uint8_t *buf = new(uint8_t, len * 8);
         if (!buf)
@@ -661,7 +846,16 @@ int ndisc_option_add_flags_extension(Set **options, size_t offset, uint64_t flag
         if ((flags & UINT64_C(0x00ffffffffffff00)) != flags)
                 return -EINVAL;
 
-        sd_ndisc_option *p = ndisc_option_new(SD_NDISC_OPTION_FLAGS_EXTENSION, offset);
+        sd_ndisc_option *p = ndisc_option_get_by_type(*options, SD_NDISC_OPTION_FLAGS_EXTENSION);
+        if (p) {
+                if (offset != 0)
+                        return -EEXIST;
+
+                p->extended_flags = flags;
+                return 0;
+        }
+
+        p = ndisc_option_new(SD_NDISC_OPTION_FLAGS_EXTENSION, offset);
         if (!p)
                 return -ENOMEM;
 
@@ -701,7 +895,13 @@ static int ndisc_option_build_flags_extension(const sd_ndisc_option *option, uin
         return 0;
 }
 
-int ndisc_option_add_dnssl(Set **options, size_t offset, char * const *domains, usec_t lifetime) {
+int ndisc_option_add_dnssl_internal(
+                Set **options,
+                size_t offset, char *
+                const *domains,
+                usec_t lifetime,
+                usec_t valid_until) {
+
         int r;
 
         assert(options);
@@ -729,6 +929,7 @@ int ndisc_option_add_dnssl(Set **options, size_t offset, char * const *domains,
         p->dnssl = (sd_ndisc_dnssl) {
                 .domains = TAKE_PTR(copy),
                 .lifetime = lifetime,
+                .valid_until = valid_until,
         };
 
         return ndisc_option_consume(options, p);
@@ -805,7 +1006,7 @@ static int ndisc_option_parse_dnssl(Set **options, size_t offset, size_t len, co
         return ndisc_option_add_dnssl(options, offset, l, lifetime);
 }
 
-static int ndisc_option_build_dnssl(const sd_ndisc_option *option, uint8_t **ret) {
+ static int ndisc_option_build_dnssl(const sd_ndisc_option *option, usec_t timestamp, uint8_t **ret) {
         int r;
 
         assert(option);
@@ -817,7 +1018,8 @@ static int ndisc_option_build_dnssl(const sd_ndisc_option *option, uint8_t **ret
                 len += strlen(*s) + 2;
         len = DIV_ROUND_UP(len, 8);
 
-        be32_t lifetime = usec_to_be32_sec(option->dnssl.lifetime);
+        be32_t lifetime = usec_to_be32_sec(MIN(option->dnssl.lifetime,
+                                               usec_sub_unsigned(option->dnssl.valid_until, timestamp)));
 
         _cleanup_free_ uint8_t *buf = new(uint8_t, len * 8);
         if (!buf)
@@ -842,8 +1044,7 @@ static int ndisc_option_build_dnssl(const sd_ndisc_option *option, uint8_t **ret
                 remaining -= r;
         }
 
-        if (remaining > 0)
-                memset(p, 0, remaining);
+        memzero(p, remaining);
 
         *ret = TAKE_PTR(buf);
         return 0;
@@ -858,11 +1059,19 @@ int ndisc_option_add_captive_portal(Set **options, size_t offset, const char *po
         if (!in_charset(portal, URI_VALID))
                 return -EINVAL;
 
+        sd_ndisc_option *p = ndisc_option_get_by_type(*options, SD_NDISC_OPTION_CAPTIVE_PORTAL);
+        if (p) {
+                if (offset != 0)
+                        return -EEXIST;
+
+                return free_and_strdup(&p->captive_portal, portal);
+        }
+
         _cleanup_free_ char *copy = strdup(portal);
         if (!copy)
                 return -ENOMEM;
 
-        sd_ndisc_option *p = ndisc_option_new(SD_NDISC_OPTION_CAPTIVE_PORTAL, offset);
+        p = ndisc_option_new(SD_NDISC_OPTION_CAPTIVE_PORTAL, offset);
         if (!p)
                 return -ENOMEM;
 
@@ -914,8 +1123,8 @@ static int ndisc_option_build_captive_portal(const sd_ndisc_option *option, uint
 
         uint8_t *p = mempcpy(buf + 2, option->captive_portal, len_portal);
         size_t remaining = len * 8 - 2 - len_portal;
-        if (remaining > 0)
-                memset(p, 0, remaining);
+
+        memzero(p, remaining);
 
         *ret = TAKE_PTR(buf);
         return 0;
@@ -953,12 +1162,13 @@ static int pref64_lifetime_and_plc_parse(uint16_t lifetime_and_plc, uint8_t *ret
         return 0;
 }
 
-int ndisc_option_add_prefix64(
+int ndisc_option_add_prefix64_internal(
                 Set **options,
                 size_t offset,
                 uint8_t prefixlen,
                 const struct in6_addr *prefix,
-                usec_t lifetime) {
+                usec_t lifetime,
+                usec_t valid_until) {
 
         int r;
 
@@ -972,18 +1182,36 @@ int ndisc_option_add_prefix64(
         if (lifetime > PREF64_MAX_LIFETIME_USEC)
                 return -EINVAL;
 
-        sd_ndisc_option *p = ndisc_option_new(SD_NDISC_OPTION_PREF64, offset);
+        struct in6_addr addr = *prefix;
+        in6_addr_mask(&addr, prefixlen);
+
+        sd_ndisc_option *p = ndisc_option_get(
+                                *options,
+                                &(const sd_ndisc_option) {
+                                        .type = SD_NDISC_OPTION_PREF64,
+                                        .prefix64.prefixlen = prefixlen,
+                                        .prefix64.prefix = addr,
+                                });
+        if (p) {
+                if (offset != 0)
+                        return -EEXIST;
+
+                p->prefix64.lifetime = lifetime;
+                p->prefix64.valid_until = valid_until;
+                return 0;
+        }
+
+        p = ndisc_option_new(SD_NDISC_OPTION_PREF64, offset);
         if (!p)
                 return -ENOMEM;
 
         p->prefix64 = (sd_ndisc_prefix64) {
                 .prefixlen = prefixlen,
-                .prefix = *prefix,
+                .prefix = addr,
                 .lifetime = lifetime,
+                .valid_until = valid_until,
         };
 
-        in6_addr_mask(&p->prefix64.prefix, p->prefix64.prefixlen);
-
         return ndisc_option_consume(options, p);
 }
 
@@ -1012,7 +1240,7 @@ static int ndisc_option_parse_prefix64(Set **options, size_t offset, size_t len,
         return ndisc_option_add_prefix64(options, offset, prefixlen, &prefix, lifetime);
 }
 
-static int ndisc_option_build_prefix64(const sd_ndisc_option *option, uint8_t **ret) {
+static int ndisc_option_build_prefix64(const sd_ndisc_option *option, usec_t timestamp, uint8_t **ret) {
         int r;
 
         assert(option);
@@ -1024,11 +1252,9 @@ static int ndisc_option_build_prefix64(const sd_ndisc_option *option, uint8_t **
         if (r < 0)
                 return r;
 
-        uint16_t lifetime;
-        if (option->prefix64.lifetime >= PREF64_SCALED_LIFETIME_MASK * USEC_PER_SEC)
-                lifetime = PREF64_SCALED_LIFETIME_MASK;
-        else
-                lifetime = (uint16_t) DIV_ROUND_UP(option->prefix64.lifetime, USEC_PER_SEC) & PREF64_SCALED_LIFETIME_MASK;
+        uint16_t lifetime = (uint16_t) DIV_ROUND_UP(MIN(option->prefix64.lifetime,
+                                                        usec_sub_unsigned(option->prefix64.valid_until, timestamp)),
+                                                    USEC_PER_SEC) & PREF64_SCALED_LIFETIME_MASK;
 
         _cleanup_free_ uint8_t *buf = new(uint8_t, 2 * 8);
         if (!buf)
@@ -1121,6 +1347,10 @@ int ndisc_parse_options(ICMP6Packet *packet, Set **ret_options) {
                         r = ndisc_option_parse_mtu(&options, offset, length, opt);
                         break;
 
+                case SD_NDISC_OPTION_HOME_AGENT:
+                        r = ndisc_option_parse_home_agent(&options, offset, length, opt);
+                        break;
+
                 case SD_NDISC_OPTION_ROUTE_INFORMATION:
                         r = ndisc_option_parse_route(&options, offset, length, opt);
                         break;
@@ -1170,7 +1400,7 @@ int ndisc_option_get_mac(Set *options, uint8_t type, struct ether_addr *ret) {
         return 0;
 }
 
-int ndisc_send(int fd, const struct sockaddr_in6 *dst, const struct icmp6_hdr *hdr, Set *options) {
+int ndisc_send(int fd, const struct in6_addr *dst, const struct icmp6_hdr *hdr, Set *options, usec_t timestamp) {
         int r;
 
         assert(fd >= 0);
@@ -1217,7 +1447,7 @@ int ndisc_send(int fd, const struct sockaddr_in6 *dst, const struct icmp6_hdr *h
                         break;
 
                 case SD_NDISC_OPTION_PREFIX_INFORMATION:
-                        r = ndisc_option_build_prefix(option, &buf);
+                        r = ndisc_option_build_prefix(option, timestamp, &buf);
                         break;
 
                 case SD_NDISC_OPTION_REDIRECTED_HEADER:
@@ -1228,12 +1458,16 @@ int ndisc_send(int fd, const struct sockaddr_in6 *dst, const struct icmp6_hdr *h
                         r = ndisc_option_build_mtu(option, &buf);
                         break;
 
+                case SD_NDISC_OPTION_HOME_AGENT:
+                        r = ndisc_option_build_home_agent(option, timestamp, &buf);
+                        break;
+
                 case SD_NDISC_OPTION_ROUTE_INFORMATION:
-                        r = ndisc_option_build_route(option, &buf);
+                        r = ndisc_option_build_route(option, timestamp, &buf);
                         break;
 
                 case SD_NDISC_OPTION_RDNSS:
-                        r = ndisc_option_build_rdnss(option, &buf);
+                        r = ndisc_option_build_rdnss(option, timestamp, &buf);
                         break;
 
                 case SD_NDISC_OPTION_FLAGS_EXTENSION:
@@ -1241,7 +1475,7 @@ int ndisc_send(int fd, const struct sockaddr_in6 *dst, const struct icmp6_hdr *h
                         break;
 
                 case SD_NDISC_OPTION_DNSSL:
-                        r = ndisc_option_build_dnssl(option, &buf);
+                        r = ndisc_option_build_dnssl(option, timestamp, &buf);
                         break;
 
                 case SD_NDISC_OPTION_CAPTIVE_PORTAL:
@@ -1249,7 +1483,7 @@ int ndisc_send(int fd, const struct sockaddr_in6 *dst, const struct icmp6_hdr *h
                         break;
 
                 case SD_NDISC_OPTION_PREF64:
-                        r = ndisc_option_build_prefix64(option, &buf);
+                        r = ndisc_option_build_prefix64(option, timestamp, &buf);
                         break;
 
                 default:
index 7e7a702b809f161f325e4a01bcc6b8c097ac2b56..d7bd86147b27ae14a8e7055f64c4e034f517cfed 100644 (file)
@@ -27,30 +27,43 @@ typedef struct sd_ndisc_prefix {
         struct in6_addr address;
         usec_t valid_lifetime;
         usec_t preferred_lifetime;
+        /* timestamp in CLOCK_BOOTTIME, used when sending option for adjusting lifetime. */
+        usec_t valid_until;
+        usec_t preferred_until;
 } sd_ndisc_prefix;
 
+typedef struct sd_ndisc_home_agent {
+        uint16_t preference;
+        usec_t lifetime;
+        usec_t valid_until;
+} sd_ndisc_home_agent;
+
 typedef struct sd_ndisc_route {
         uint8_t preference;
         uint8_t prefixlen;
         struct in6_addr address;
         usec_t lifetime;
+        usec_t valid_until;
 } sd_ndisc_route;
 
 typedef struct sd_ndisc_rdnss {
         size_t n_addresses;
         struct in6_addr *addresses;
         usec_t lifetime;
+        usec_t valid_until;
 } sd_ndisc_rdnss;
 
 typedef struct sd_ndisc_dnssl {
         char **domains;
         usec_t lifetime;
+        usec_t valid_until;
 } sd_ndisc_dnssl;
 
 typedef struct sd_ndisc_prefix64 {
         uint8_t prefixlen;
         struct in6_addr prefix;
         usec_t lifetime;
+        usec_t valid_until;
 } sd_ndisc_prefix64;
 
 typedef struct sd_ndisc_option {
@@ -58,17 +71,18 @@ typedef struct sd_ndisc_option {
         size_t offset;
 
         union {
-                sd_ndisc_raw raw;           /* for testing or unsupported options */
-                struct ether_addr mac;      /* SD_NDISC_OPTION_SOURCE_LL_ADDRESS or SD_NDISC_OPTION_TARGET_LL_ADDRESS */
-                sd_ndisc_prefix prefix;     /* SD_NDISC_OPTION_PREFIX_INFORMATION */
-                struct ip6_hdr hdr;         /* SD_NDISC_OPTION_REDIRECTED_HEADER */
-                uint32_t mtu;               /* SD_NDISC_OPTION_MTU */
-                sd_ndisc_route route;       /* SD_NDISC_OPTION_ROUTE_INFORMATION */
-                sd_ndisc_rdnss rdnss;       /* SD_NDISC_OPTION_RDNSS */
-                uint64_t extended_flags;    /* SD_NDISC_OPTION_FLAGS_EXTENSION */
-                sd_ndisc_dnssl dnssl;       /* SD_NDISC_OPTION_DNSSL */
-                char *captive_portal;       /* SD_NDISC_OPTION_CAPTIVE_PORTAL */
-                sd_ndisc_prefix64 prefix64; /* SD_NDISC_OPTION_PREF64 */
+                sd_ndisc_raw raw;               /* for testing or unsupported options */
+                struct ether_addr mac;          /* SD_NDISC_OPTION_SOURCE_LL_ADDRESS or SD_NDISC_OPTION_TARGET_LL_ADDRESS */
+                sd_ndisc_prefix prefix;         /* SD_NDISC_OPTION_PREFIX_INFORMATION */
+                struct ip6_hdr hdr;             /* SD_NDISC_OPTION_REDIRECTED_HEADER */
+                uint32_t mtu;                   /* SD_NDISC_OPTION_MTU */
+                sd_ndisc_home_agent home_agent; /* SD_NDISC_OPTION_HOME_AGENT */
+                sd_ndisc_route route;           /* SD_NDISC_OPTION_ROUTE_INFORMATION */
+                sd_ndisc_rdnss rdnss;           /* SD_NDISC_OPTION_RDNSS */
+                uint64_t extended_flags;        /* SD_NDISC_OPTION_FLAGS_EXTENSION */
+                sd_ndisc_dnssl dnssl;           /* SD_NDISC_OPTION_DNSSL */
+                char *captive_portal;           /* SD_NDISC_OPTION_CAPTIVE_PORTAL */
+                sd_ndisc_prefix64 prefix64;     /* SD_NDISC_OPTION_PREF64 */
         };
 } sd_ndisc_option;
 
@@ -124,24 +138,56 @@ static inline void ndisc_option_remove_by_type(Set *options, uint8_t type) {
         ndisc_option_remove(options, &(const sd_ndisc_option) { .type = type });
 }
 
-int ndisc_option_add_raw(
+int ndisc_option_set_raw(
                 Set **options,
-                size_t offset,
                 size_t length,
                 const uint8_t *bytes);
 int ndisc_option_add_link_layer_address(
                 Set **options,
-                uint8_t opt,
+                uint8_t type,
                 size_t offset,
                 const struct ether_addr *mac);
-int ndisc_option_add_prefix(
+static inline int ndisc_option_set_link_layer_address(
+                Set **options,
+                uint8_t type,
+                const struct ether_addr *mac) {
+        return ndisc_option_add_link_layer_address(options, type, 0, mac);
+}
+int ndisc_option_add_prefix_internal(
                 Set **options,
                 size_t offset,
                 uint8_t flags,
                 uint8_t prefixlen,
                 const struct in6_addr *address,
                 usec_t valid_lifetime,
-                usec_t preferred_lifetime);
+                usec_t preferred_lifetime,
+                usec_t valid_until,
+                usec_t preferred_until);
+static inline int ndisc_option_add_prefix(
+                Set **options,
+                size_t offset,
+                uint8_t flags,
+                uint8_t prefixlen,
+                const struct in6_addr *address,
+                usec_t valid_lifetime,
+                usec_t preferred_lifetime) {
+        return ndisc_option_add_prefix_internal(options, offset, flags, prefixlen, address,
+                                                valid_lifetime, preferred_lifetime,
+                                                USEC_INFINITY, USEC_INFINITY);
+}
+static inline int ndisc_option_set_prefix(
+                Set **options,
+                uint8_t flags,
+                uint8_t prefixlen,
+                const struct in6_addr *address,
+                usec_t valid_lifetime,
+                usec_t preferred_lifetime,
+                usec_t valid_until,
+                usec_t preferred_until) {
+        return ndisc_option_add_prefix_internal(options, 0, flags, prefixlen, address,
+                                                valid_lifetime, preferred_lifetime,
+                                                valid_until, preferred_until);
+}
 int ndisc_option_add_redirected_header(
                 Set **options,
                 size_t offset,
@@ -150,37 +196,135 @@ int ndisc_option_add_mtu(
                 Set **options,
                 size_t offset,
                 uint32_t mtu);
-int ndisc_option_add_route(
+static inline int ndisc_option_set_mtu(
+                Set **options,
+                uint32_t mtu) {
+        return ndisc_option_add_mtu(options, 0, mtu);
+}
+int ndisc_option_add_home_agent_internal(
+                Set **options,
+                size_t offset,
+                uint16_t preference,
+                usec_t lifetime,
+                usec_t valid_until);
+static inline int ndisc_option_add_home_agent(
+                Set **options,
+                size_t offset,
+                uint16_t preference,
+                usec_t lifetime) {
+        return ndisc_option_add_home_agent_internal(options, offset, preference, lifetime, USEC_INFINITY);
+}
+static inline int ndisc_option_set_home_agent(
+                Set **options,
+                uint16_t preference,
+                usec_t lifetime,
+                usec_t valid_until) {
+        return ndisc_option_add_home_agent_internal(options, 0, preference, lifetime, valid_until);
+}
+int ndisc_option_add_route_internal(
                 Set **options,
                 size_t offset,
                 uint8_t preference,
                 uint8_t prefixlen,
                 const struct in6_addr *prefix,
-                usec_t lifetime);
-int ndisc_option_add_rdnss(
+                usec_t lifetime,
+                usec_t valid_until);
+static inline int ndisc_option_add_route(
                 Set **options,
                 size_t offset,
+                uint8_t preference,
+                uint8_t prefixlen,
+                const struct in6_addr *prefix,
+                usec_t lifetime) {
+        return ndisc_option_add_route_internal(options, offset, preference, prefixlen, prefix, lifetime, USEC_INFINITY);
+}
+static inline int ndisc_option_set_route(
+                Set **options,
+                uint8_t preference,
+                uint8_t prefixlen,
+                const struct in6_addr *prefix,
+                usec_t lifetime,
+                usec_t valid_until) {
+        return ndisc_option_add_route_internal(options, 0, preference, prefixlen, prefix, lifetime, valid_until);
+}
+int ndisc_option_add_rdnss_internal(
+                Set **options,
+                size_t offset,
+                size_t n_addresses,
+                const struct in6_addr *addresses,
+                usec_t lifetime,
+                usec_t valid_until);
+static inline int ndisc_option_add_rdnss(
+                Set **options,
+                size_t offset,
+                size_t n_addresses,
+                const struct in6_addr *addresses,
+                usec_t lifetime) {
+        return ndisc_option_add_rdnss_internal(options, offset, n_addresses, addresses, lifetime, USEC_INFINITY);
+}
+static inline int ndisc_option_set_rdnss(
+                Set **options,
                 size_t n_addresses,
                 const struct in6_addr *addresses,
-                usec_t lifetime);
+                usec_t lifetime,
+                usec_t valid_until) {
+        return ndisc_option_add_rdnss_internal(options, 0, n_addresses, addresses, lifetime, valid_until);
+}
 int ndisc_option_add_flags_extension(
                 Set **options,
                 size_t offset,
                 uint64_t flags);
-int ndisc_option_add_dnssl(
+int ndisc_option_add_dnssl_internal(
+                Set **options,
+                size_t offset,
+                char * const *domains,
+                usec_t lifetime,
+                usec_t valid_until);
+static inline int ndisc_option_add_dnssl(
                 Set **options,
                 size_t offset,
                 char * const *domains,
-                usec_t lifetime);
+                usec_t lifetime) {
+        return ndisc_option_add_dnssl_internal(options, offset, domains, lifetime, USEC_INFINITY);
+}
+static inline int ndisc_option_set_dnssl(
+                Set **options,
+                char * const *domains,
+                usec_t lifetime,
+                usec_t valid_until) {
+        return ndisc_option_add_dnssl_internal(options, 0, domains, lifetime, valid_until);
+}
 int ndisc_option_add_captive_portal(
                 Set **options,
                 size_t offset,
                 const char *portal);
-int ndisc_option_add_prefix64(
+static inline int ndisc_option_set_captive_portal(
+                Set **options,
+                const char *portal) {
+        return ndisc_option_add_captive_portal(options, 0, portal);
+}
+int ndisc_option_add_prefix64_internal(
+                Set **options,
+                size_t offset,
+                uint8_t prefixlen,
+                const struct in6_addr *prefix,
+                usec_t lifetime,
+                usec_t valid_until);
+static inline int ndisc_option_add_prefix64(
                 Set **options,
                 size_t offset,
                 uint8_t prefixlen,
                 const struct in6_addr *prefix,
-                usec_t lifetime);
+                usec_t lifetime) {
+        return ndisc_option_add_prefix64_internal(options, offset, prefixlen, prefix, lifetime, USEC_INFINITY);
+}
+static inline int ndisc_option_set_prefix64(
+                Set **options,
+                uint8_t prefixlen,
+                const struct in6_addr *prefix,
+                usec_t lifetime,
+                usec_t valid_until) {
+        return ndisc_option_add_prefix64_internal(options, 0, prefixlen, prefix, lifetime, valid_until);
+}
 
-int ndisc_send(int fd, const struct sockaddr_in6 *dst, const struct icmp6_hdr *hdr, Set *options);
+int ndisc_send(int fd, const struct in6_addr *dst, const struct icmp6_hdr *hdr, Set *options, usec_t timestamp);
diff --git a/src/libsystemd-network/ndisc-redirect-internal.h b/src/libsystemd-network/ndisc-redirect-internal.h
new file mode 100644 (file)
index 0000000..e35263a
--- /dev/null
@@ -0,0 +1,21 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include "sd-ndisc.h"
+
+#include "icmp6-packet.h"
+#include "set.h"
+
+struct sd_ndisc_redirect {
+        unsigned n_ref;
+
+        ICMP6Packet *packet;
+
+        struct in6_addr target_address;
+        struct in6_addr destination_address;
+
+        Set *options;
+};
+
+sd_ndisc_redirect* ndisc_redirect_new(ICMP6Packet *packet);
+int ndisc_redirect_parse(sd_ndisc *nd, sd_ndisc_redirect *rd);
diff --git a/src/libsystemd-network/ndisc-router-solicit-internal.h b/src/libsystemd-network/ndisc-router-solicit-internal.h
new file mode 100644 (file)
index 0000000..6f0b0af
--- /dev/null
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include "sd-radv.h"
+
+#include "icmp6-packet.h"
+#include "set.h"
+
+struct sd_ndisc_router_solicit {
+        unsigned n_ref;
+
+        ICMP6Packet *packet;
+
+        Set *options;
+};
+
+sd_ndisc_router_solicit* ndisc_router_solicit_new(ICMP6Packet *packet);
+int ndisc_router_solicit_parse(sd_radv *ra, sd_ndisc_router_solicit *rs);
index 6ec84405d9a279a0b8ad89ee2c0c9f23342cc6f6..c3b0f82dc4f87dfd4a69446241d636f888b54d01 100644 (file)
@@ -1592,6 +1592,7 @@ int sd_dhcp_server_set_lease_file(sd_dhcp_server *server, int dir_fd, const char
         int r;
 
         assert_return(server, -EINVAL);
+        assert_return(!path || (dir_fd >= 0 || dir_fd == AT_FDCWD), -EBADF);
         assert_return(!sd_dhcp_server_is_running(server), -EBUSY);
 
         if (!path) {
@@ -1604,11 +1605,7 @@ int sd_dhcp_server_set_lease_file(sd_dhcp_server *server, int dir_fd, const char
         if (!path_is_safe(path))
                 return -EINVAL;
 
-        if (dir_fd < 0 && dir_fd != AT_FDCWD)
-                return -EBADF;
-
-        _cleanup_close_ int fd = -EBADF;
-        fd = fd_reopen(dir_fd, O_CLOEXEC | O_DIRECTORY | O_PATH);
+        _cleanup_close_ int fd = fd_reopen(dir_fd, O_CLOEXEC | O_DIRECTORY | O_PATH);
         if (fd < 0)
                 return fd;
 
@@ -1616,6 +1613,7 @@ int sd_dhcp_server_set_lease_file(sd_dhcp_server *server, int dir_fd, const char
         if (r < 0)
                 return r;
 
-        server->lease_dir_fd = TAKE_FD(fd);
+        close_and_replace(server->lease_dir_fd, fd);
+
         return 0;
 }
index 5a26102e232b0fe4567fd906c19cd2ea5f8b714a..9f14a32896da4c822ef428429a623b4765e509ee 100644 (file)
@@ -743,8 +743,6 @@ static int client_append_mudurl(sd_dhcp6_client *client, uint8_t **buf, size_t *
 
 int dhcp6_client_send_message(sd_dhcp6_client *client) {
         _cleanup_free_ uint8_t *buf = NULL;
-        struct in6_addr all_servers =
-                IN6ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS_INIT;
         struct sd_dhcp6_option *j;
         usec_t elapsed_usec, time_now;
         be16_t elapsed_time;
@@ -839,7 +837,7 @@ int dhcp6_client_send_message(sd_dhcp6_client *client) {
         if (r < 0)
                 return r;
 
-        r = dhcp6_network_send_udp_socket(client->fd, &all_servers, buf, offset);
+        r = dhcp6_network_send_udp_socket(client->fd, &IN6_ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS, buf, offset);
         if (r < 0)
                 return r;
 
@@ -1417,7 +1415,8 @@ int sd_dhcp6_client_stop(sd_dhcp6_client *client) {
 }
 
 int sd_dhcp6_client_is_running(sd_dhcp6_client *client) {
-        assert_return(client, -EINVAL);
+        if (!client)
+                return false;
 
         return client->state != DHCP6_STATE_STOPPED;
 }
index 1a9a8f10ab61008d6629d2033de0e4aff0e6c660..51d2b2219dd7b35a7326c6bd9922b3e36c5a09e3 100644 (file)
@@ -564,7 +564,8 @@ int sd_ipv4acd_get_address(sd_ipv4acd *acd, struct in_addr *address) {
 }
 
 int sd_ipv4acd_is_running(sd_ipv4acd *acd) {
-        assert_return(acd, false);
+        if (!acd)
+                return false;
 
         return acd->state != IPV4ACD_STATE_INIT;
 }
index a29279e6afc5210337ce19bfa9323e13ddceb890..5bf98332a9f184e6968ace16bd96b5c08a7a5fdf 100644 (file)
@@ -206,7 +206,8 @@ int sd_ipv4ll_set_address_seed(sd_ipv4ll *ll, uint64_t seed) {
 }
 
 int sd_ipv4ll_is_running(sd_ipv4ll *ll) {
-        assert_return(ll, false);
+        if (!ll)
+                return false;
 
         return sd_ipv4acd_is_running(ll->acd);
 }
diff --git a/src/libsystemd-network/sd-ndisc-neighbor.c b/src/libsystemd-network/sd-ndisc-neighbor.c
new file mode 100644 (file)
index 0000000..1bb6ebf
--- /dev/null
@@ -0,0 +1,126 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <netinet/icmp6.h>
+
+#include "sd-ndisc.h"
+
+#include "alloc-util.h"
+#include "in-addr-util.h"
+#include "ndisc-internal.h"
+#include "ndisc-neighbor-internal.h"
+#include "ndisc-option.h"
+
+static sd_ndisc_neighbor* ndisc_neighbor_free(sd_ndisc_neighbor *na) {
+        if (!na)
+                return NULL;
+
+        icmp6_packet_unref(na->packet);
+        set_free(na->options);
+        return mfree(na);
+}
+
+DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_ndisc_neighbor, sd_ndisc_neighbor, ndisc_neighbor_free);
+
+sd_ndisc_neighbor* ndisc_neighbor_new(ICMP6Packet *packet) {
+        sd_ndisc_neighbor *na;
+
+        assert(packet);
+
+        na = new(sd_ndisc_neighbor, 1);
+        if (!na)
+                return NULL;
+
+        *na = (sd_ndisc_neighbor) {
+                .n_ref = 1,
+                .packet = icmp6_packet_ref(packet),
+        };
+
+        return na;
+}
+
+int ndisc_neighbor_parse(sd_ndisc *nd, sd_ndisc_neighbor *na) {
+        int r;
+
+        assert(na);
+
+        if (na->packet->raw_size < sizeof(struct nd_neighbor_advert))
+                return log_ndisc_errno(nd, SYNTHETIC_ERRNO(EBADMSG),
+                                       "Too small to be a neighbor advertisement, ignoring datagram.");
+
+        /* Neighbor advertisement packets are neatly aligned to 64-bit boundaries, hence we can access them directly */
+        const struct nd_neighbor_advert *a = (const struct nd_neighbor_advert*) na->packet->raw_packet;
+        assert(a->nd_na_type == ND_NEIGHBOR_ADVERT);
+        assert(a->nd_na_code == 0);
+
+        na->flags = a->nd_na_flags_reserved; /* the first 3 bits */
+        na->target_address = a->nd_na_target;
+
+        /* RFC 4861 section 4.4:
+         * For solicited advertisements, the Target Address field in the Neighbor Solicitation message that
+         * prompted this advertisement. For an unsolicited advertisement, the address whose link-layer
+         * address has changed. The Target Address MUST NOT be a multicast address.
+         *
+         * Here, we only check if the target address is a link-layer address (or a null address, for safety)
+         * when the message is an unsolicited neighbor advertisement. */
+        if (!FLAGS_SET(na->flags, ND_NA_FLAG_SOLICITED))
+                if (!in6_addr_is_link_local(&na->target_address) && !in6_addr_is_null(&na->target_address))
+                        return log_ndisc_errno(nd, SYNTHETIC_ERRNO(EBADMSG),
+                                               "Received ND packet with an invalid target address (%s), ignoring datagram.",
+                                               IN6_ADDR_TO_STRING(&na->target_address));
+
+        r = ndisc_parse_options(na->packet, &na->options);
+        if (r < 0)
+                return log_ndisc_errno(nd, r, "Failed to parse NDisc options in neighbor advertisement message, ignoring: %m");
+
+        return 0;
+}
+
+int sd_ndisc_neighbor_get_sender_address(sd_ndisc_neighbor *na, struct in6_addr *ret) {
+        assert_return(na, -EINVAL);
+
+        return icmp6_packet_get_sender_address(na->packet, ret);
+}
+
+int sd_ndisc_neighbor_get_target_address(sd_ndisc_neighbor *na, struct in6_addr *ret) {
+        assert_return(na, -EINVAL);
+
+        if (in6_addr_is_null(&na->target_address))
+                /* fall back to the sender address, for safety. */
+                return sd_ndisc_neighbor_get_sender_address(na, ret);
+
+        if (ret)
+                *ret = na->target_address;
+        return 0;
+}
+
+int sd_ndisc_neighbor_get_target_mac(sd_ndisc_neighbor *na, struct ether_addr *ret) {
+        assert_return(na, -EINVAL);
+
+        return ndisc_option_get_mac(na->options, SD_NDISC_OPTION_TARGET_LL_ADDRESS, ret);
+}
+
+int sd_ndisc_neighbor_get_flags(sd_ndisc_neighbor *na, uint32_t *ret) {
+        assert_return(na, -EINVAL);
+
+        if (ret)
+                *ret = na->flags;
+        return 0;
+}
+
+int sd_ndisc_neighbor_is_router(sd_ndisc_neighbor *na) {
+        assert_return(na, -EINVAL);
+
+        return FLAGS_SET(na->flags, ND_NA_FLAG_ROUTER);
+}
+
+int sd_ndisc_neighbor_is_solicited(sd_ndisc_neighbor *na) {
+        assert_return(na, -EINVAL);
+
+        return FLAGS_SET(na->flags, ND_NA_FLAG_SOLICITED);
+}
+
+int sd_ndisc_neighbor_is_override(sd_ndisc_neighbor *na) {
+        assert_return(na, -EINVAL);
+
+        return FLAGS_SET(na->flags, ND_NA_FLAG_OVERRIDE);
+}
diff --git a/src/libsystemd-network/sd-ndisc-redirect.c b/src/libsystemd-network/sd-ndisc-redirect.c
new file mode 100644 (file)
index 0000000..a1fceb2
--- /dev/null
@@ -0,0 +1,128 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <netinet/icmp6.h>
+
+#include "sd-ndisc.h"
+
+#include "alloc-util.h"
+#include "in-addr-util.h"
+#include "ndisc-internal.h"
+#include "ndisc-option.h"
+#include "ndisc-redirect-internal.h"
+
+static sd_ndisc_redirect* ndisc_redirect_free(sd_ndisc_redirect *rd) {
+        if (!rd)
+                return NULL;
+
+        icmp6_packet_unref(rd->packet);
+        set_free(rd->options);
+        return mfree(rd);
+}
+
+DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_ndisc_redirect, sd_ndisc_redirect, ndisc_redirect_free);
+
+sd_ndisc_redirect* ndisc_redirect_new(ICMP6Packet *packet) {
+        sd_ndisc_redirect *rd;
+
+        assert(packet);
+
+        rd = new(sd_ndisc_redirect, 1);
+        if (!rd)
+                return NULL;
+
+        *rd = (sd_ndisc_redirect) {
+                .n_ref = 1,
+                .packet = icmp6_packet_ref(packet),
+        };
+
+        return rd;
+}
+
+int ndisc_redirect_parse(sd_ndisc *nd, sd_ndisc_redirect *rd) {
+        int r;
+
+        assert(rd);
+        assert(rd->packet);
+
+        if (rd->packet->raw_size < sizeof(struct nd_redirect))
+                return log_ndisc_errno(nd, SYNTHETIC_ERRNO(EBADMSG),
+                                       "Too small to be a redirect message, ignoring.");
+
+        const struct nd_redirect *a = (const struct nd_redirect*) rd->packet->raw_packet;
+        assert(a->nd_rd_type == ND_REDIRECT);
+        assert(a->nd_rd_code == 0);
+
+        rd->target_address = a->nd_rd_target;
+        rd->destination_address = a->nd_rd_dst;
+
+        /* RFC 4861 section 8.1
+         * The ICMP Destination Address field in the redirect message does not contain a multicast address. */
+        if (in6_addr_is_null(&rd->destination_address) || in6_addr_is_multicast(&rd->destination_address))
+                return log_ndisc_errno(nd, SYNTHETIC_ERRNO(EBADMSG),
+                                       "Received Redirect message with an invalid destination address, ignoring datagram: %m");
+
+        /* RFC 4861 section 8.1
+         * The ICMP Target Address is either a link-local address (when redirected to a router) or the same
+         * as the ICMP Destination Address (when redirected to the on-link destination). */
+        if (!in6_addr_is_link_local(&rd->target_address) && !in6_addr_equal(&rd->target_address, &rd->destination_address))
+                return log_ndisc_errno(nd, SYNTHETIC_ERRNO(EBADMSG),
+                                       "Received Redirect message with an invalid target address, ignoring datagram: %m");
+
+        r = ndisc_parse_options(rd->packet, &rd->options);
+        if (r < 0)
+                return log_ndisc_errno(nd, r, "Failed to parse NDisc options in Redirect message, ignoring datagram: %m");
+
+        return 0;
+}
+
+int sd_ndisc_redirect_set_sender_address(sd_ndisc_redirect *rd, const struct in6_addr *addr) {
+        assert_return(rd, -EINVAL);
+
+        return icmp6_packet_set_sender_address(rd->packet, addr);
+}
+
+int sd_ndisc_redirect_get_sender_address(sd_ndisc_redirect *rd, struct in6_addr *ret) {
+        assert_return(rd, -EINVAL);
+
+        return icmp6_packet_get_sender_address(rd->packet, ret);
+}
+
+int sd_ndisc_redirect_get_target_address(sd_ndisc_redirect *rd, struct in6_addr *ret) {
+        assert_return(rd, -EINVAL);
+
+        if (in6_addr_is_null(&rd->target_address))
+                return -ENODATA;
+
+        if (ret)
+                *ret = rd->target_address;
+        return 0;
+}
+
+int sd_ndisc_redirect_get_destination_address(sd_ndisc_redirect *rd, struct in6_addr *ret) {
+        assert_return(rd, -EINVAL);
+
+        if (in6_addr_is_null(&rd->destination_address))
+                return -ENODATA;
+
+        if (ret)
+                *ret = rd->destination_address;
+        return 0;
+}
+
+int sd_ndisc_redirect_get_target_mac(sd_ndisc_redirect *rd, struct ether_addr *ret) {
+        assert_return(rd, -EINVAL);
+
+        return ndisc_option_get_mac(rd->options, SD_NDISC_OPTION_TARGET_LL_ADDRESS, ret);
+}
+
+int sd_ndisc_redirect_get_redirected_header(sd_ndisc_redirect *rd, struct ip6_hdr *ret) {
+        assert_return(rd, -EINVAL);
+
+        sd_ndisc_option *p = ndisc_option_get_by_type(rd->options, SD_NDISC_OPTION_REDIRECTED_HEADER);
+        if (!p)
+                return -ENODATA;
+
+        if (ret)
+                *ret = p->hdr;
+        return 0;
+}
diff --git a/src/libsystemd-network/sd-ndisc-router-solicit.c b/src/libsystemd-network/sd-ndisc-router-solicit.c
new file mode 100644 (file)
index 0000000..04e7c26
--- /dev/null
@@ -0,0 +1,83 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <netinet/icmp6.h>
+
+#include "sd-radv.h"
+
+#include "alloc-util.h"
+#include "in-addr-util.h"
+#include "ndisc-option.h"
+#include "ndisc-router-solicit-internal.h"
+#include "radv-internal.h"
+
+static sd_ndisc_router_solicit* ndisc_router_solicit_free(sd_ndisc_router_solicit *rs) {
+        if (!rs)
+                return NULL;
+
+        icmp6_packet_unref(rs->packet);
+        set_free(rs->options);
+        return mfree(rs);
+}
+
+DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_ndisc_router_solicit, sd_ndisc_router_solicit, ndisc_router_solicit_free);
+
+sd_ndisc_router_solicit* ndisc_router_solicit_new(ICMP6Packet *packet) {
+        sd_ndisc_router_solicit *rs;
+
+        assert(packet);
+
+        rs = new(sd_ndisc_router_solicit, 1);
+        if (!rs)
+                return NULL;
+
+        *rs = (sd_ndisc_router_solicit) {
+                .n_ref = 1,
+                .packet = icmp6_packet_ref(packet),
+        };
+
+        return rs;
+}
+
+int ndisc_router_solicit_parse(sd_radv *ra, sd_ndisc_router_solicit *rs) {
+        int r;
+
+        assert(rs);
+        assert(rs->packet);
+
+        if (rs->packet->raw_size < sizeof(struct nd_router_solicit))
+                return log_radv_errno(ra, SYNTHETIC_ERRNO(EBADMSG),
+                                      "Too small to be a router solicit, ignoring.");
+
+        const struct nd_router_solicit *a = (const struct nd_router_solicit*) rs->packet->raw_packet;
+        assert(a);
+        assert(a->nd_rs_type == ND_ROUTER_SOLICIT);
+        assert(a->nd_rs_code == 0);
+
+        r = ndisc_parse_options(rs->packet, &rs->options);
+        if (r < 0)
+                return log_radv_errno(ra, r, "Failed to parse NDisc options in router solicit, ignoring datagram: %m");
+
+        /* RFC 4861 section 4.1.
+         * Source link-layer address:
+         * The link-layer address of the sender, if known. MUST NOT be included if the Source
+         * Address is the unspecified address. Otherwise, it SHOULD be included on link
+         * layers that have addresses. */
+        if (ndisc_option_get_mac(rs->options, SD_NDISC_OPTION_SOURCE_LL_ADDRESS, NULL) >= 0&&
+            sd_ndisc_router_solicit_get_sender_address(rs, NULL) == -ENODATA)
+                return log_radv_errno(ra, SYNTHETIC_ERRNO(EBADMSG),
+                                      "Router Solicitation message from null address unexpectedly contains source link-layer address option, ignoring datagaram.");
+
+        return 0;
+}
+
+int sd_ndisc_router_solicit_get_sender_address(sd_ndisc_router_solicit *rs, struct in6_addr *ret) {
+        assert_return(rs, -EINVAL);
+
+        return icmp6_packet_get_sender_address(rs->packet, ret);
+}
+
+int sd_ndisc_router_solicit_get_sender_mac(sd_ndisc_router_solicit *rs, struct ether_addr *ret) {
+        assert_return(rs, -EINVAL);
+
+        return ndisc_option_get_mac(rs->options, SD_NDISC_OPTION_SOURCE_LL_ADDRESS, ret);
+}
index 4b4cc530a69021ad1c61b9f41c7327b906539b23..f8476c4d6bd7fca2ad3a7f038cbe10a6afa3ae80 100644 (file)
@@ -40,6 +40,12 @@ sd_ndisc_router* ndisc_router_new(ICMP6Packet *packet) {
         return rt;
 }
 
+int sd_ndisc_router_set_sender_address(sd_ndisc_router *rt, const struct in6_addr *addr) {
+        assert_return(rt, -EINVAL);
+
+        return icmp6_packet_set_sender_address(rt->packet, addr);
+}
+
 int sd_ndisc_router_get_sender_address(sd_ndisc_router *rt, struct in6_addr *ret) {
         assert_return(rt, -EINVAL);
 
index 4dbf1b95d6e17f161e93cb87de11202a1baa4888..b4136563e1f98da83c842b89527d2de361a04a38 100644 (file)
@@ -16,6 +16,8 @@
 #include "in-addr-util.h"
 #include "memory-util.h"
 #include "ndisc-internal.h"
+#include "ndisc-neighbor-internal.h"
+#include "ndisc-redirect-internal.h"
 #include "ndisc-router-internal.h"
 #include "network-common.h"
 #include "random-util.h"
 #define NDISC_TIMEOUT_NO_RA_USEC (NDISC_ROUTER_SOLICITATION_INTERVAL * NDISC_MAX_ROUTER_SOLICITATIONS)
 
 static const char * const ndisc_event_table[_SD_NDISC_EVENT_MAX] = {
-        [SD_NDISC_EVENT_TIMEOUT] = "timeout",
-        [SD_NDISC_EVENT_ROUTER] = "router",
+        [SD_NDISC_EVENT_TIMEOUT]  = "timeout",
+        [SD_NDISC_EVENT_ROUTER]   = "router",
+        [SD_NDISC_EVENT_NEIGHBOR] = "neighbor",
+        [SD_NDISC_EVENT_REDIRECT] = "redirect",
 };
 
 DEFINE_STRING_TABLE_LOOKUP(ndisc_event, sd_ndisc_event_t);
@@ -225,6 +229,65 @@ static int ndisc_handle_router(sd_ndisc *nd, ICMP6Packet *packet) {
         return 0;
 }
 
+static int ndisc_handle_neighbor(sd_ndisc *nd, ICMP6Packet *packet) {
+        _cleanup_(sd_ndisc_neighbor_unrefp) sd_ndisc_neighbor *na = NULL;
+        struct in6_addr a;
+        int r;
+
+        assert(nd);
+        assert(packet);
+
+        na = ndisc_neighbor_new(packet);
+        if (!na)
+                return -ENOMEM;
+
+        r = ndisc_neighbor_parse(nd, na);
+        if (r < 0)
+                return r;
+
+        r = sd_ndisc_neighbor_get_sender_address(na, &a);
+        if (r < 0)
+                return r;
+
+        log_ndisc(nd, "Received Neighbor Advertisement from %s: Router=%s, Solicited=%s, Override=%s",
+                  IN6_ADDR_TO_STRING(&a),
+                  yes_no(sd_ndisc_neighbor_is_router(na) > 0),
+                  yes_no(sd_ndisc_neighbor_is_solicited(na) > 0),
+                  yes_no(sd_ndisc_neighbor_is_override(na) > 0));
+
+        ndisc_callback(nd, SD_NDISC_EVENT_NEIGHBOR, na);
+        return 0;
+}
+
+static int ndisc_handle_redirect(sd_ndisc *nd, ICMP6Packet *packet) {
+        _cleanup_(sd_ndisc_redirect_unrefp) sd_ndisc_redirect *rd = NULL;
+        struct in6_addr a;
+        int r;
+
+        assert(nd);
+        assert(packet);
+
+        rd = ndisc_redirect_new(packet);
+        if (!rd)
+                return -ENOMEM;
+
+        r = ndisc_redirect_parse(nd, rd);
+        if (r < 0)
+                return r;
+
+        r = sd_ndisc_redirect_get_sender_address(rd, &a);
+        if (r < 0)
+                return r;
+
+        log_ndisc(nd, "Received Redirect message from %s: Target=%s, Destination=%s",
+                  IN6_ADDR_TO_STRING(&a),
+                  IN6_ADDR_TO_STRING(&rd->target_address),
+                  IN6_ADDR_TO_STRING(&rd->destination_address));
+
+        ndisc_callback(nd, SD_NDISC_EVENT_REDIRECT, rd);
+        return 0;
+}
+
 static int ndisc_recv(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
         _cleanup_(icmp6_packet_unrefp) ICMP6Packet *packet = NULL;
         sd_ndisc *nd = ASSERT_PTR(userdata);
@@ -262,6 +325,14 @@ static int ndisc_recv(sd_event_source *s, int fd, uint32_t revents, void *userda
                 (void) ndisc_handle_router(nd, packet);
                 break;
 
+        case ND_NEIGHBOR_ADVERT:
+                (void) ndisc_handle_neighbor(nd, packet);
+                break;
+
+        case ND_REDIRECT:
+                (void) ndisc_handle_redirect(nd, packet);
+                break;
+
         default:
                 log_ndisc(nd, "Received an ICMPv6 packet with unexpected type %i, ignoring.", r);
         }
@@ -270,10 +341,6 @@ static int ndisc_recv(sd_event_source *s, int fd, uint32_t revents, void *userda
 }
 
 static int ndisc_send_router_solicitation(sd_ndisc *nd) {
-        static const struct sockaddr_in6 dst = {
-                .sin6_family = AF_INET6,
-                .sin6_addr = IN6ADDR_ALL_ROUTERS_MULTICAST_INIT,
-        };
         static const struct nd_router_solicit header = {
                 .nd_rs_type = ND_ROUTER_SOLICIT,
         };
@@ -284,12 +351,12 @@ static int ndisc_send_router_solicitation(sd_ndisc *nd) {
         assert(nd);
 
         if (!ether_addr_is_null(&nd->mac_addr)) {
-                r = ndisc_option_add_link_layer_address(&options, SD_NDISC_OPTION_SOURCE_LL_ADDRESS, 0, &nd->mac_addr);
+                r = ndisc_option_set_link_layer_address(&options, SD_NDISC_OPTION_SOURCE_LL_ADDRESS, &nd->mac_addr);
                 if (r < 0)
                         return r;
         }
 
-        return ndisc_send(nd->fd, &dst, &header.nd_rs_hdr, options);
+        return ndisc_send(nd->fd, &IN6_ADDR_ALL_ROUTERS_MULTICAST, &header.nd_rs_hdr, options, USEC_INFINITY);
 }
 
 static usec_t ndisc_timeout_compute_random(usec_t val) {
index 71e7a71897024b6abc63588133c7ba7b4c1769b2..79210f9e2ca182352ac6ac0a1e52b6e1290604bf 100644 (file)
@@ -19,6 +19,7 @@
 #include "iovec-util.h"
 #include "macro.h"
 #include "memory-util.h"
+#include "ndisc-router-solicit-internal.h"
 #include "network-common.h"
 #include "radv-internal.h"
 #include "random-util.h"
@@ -81,7 +82,8 @@ sd_event *sd_radv_get_event(sd_radv *ra) {
 }
 
 int sd_radv_is_running(sd_radv *ra) {
-        assert_return(ra, false);
+        if (!ra)
+                return false;
 
         return ra->state != RADV_STATE_IDLE;
 }
@@ -133,7 +135,7 @@ static int radv_send_router(sd_radv *ra, const struct in6_addr *dst, usec_t life
 
         struct sockaddr_in6 dst_addr = {
                 .sin6_family = AF_INET6,
-                .sin6_addr = IN6ADDR_ALL_NODES_MULTICAST_INIT,
+                .sin6_addr = IN6_ADDR_ALL_NODES_MULTICAST,
         };
         struct nd_router_advert adv = {
                 .nd_ra_type = ND_ROUTER_ADVERT,
@@ -236,64 +238,51 @@ static int radv_send_router(sd_radv *ra, const struct in6_addr *dst, usec_t life
         return 0;
 }
 
-static int radv_recv(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
-        sd_radv *ra = ASSERT_PTR(userdata);
-        struct in6_addr src;
-        triple_timestamp timestamp;
+static int radv_process_packet(sd_radv *ra, ICMP6Packet *packet) {
         int r;
 
-        assert(s);
-        assert(ra->event);
+        assert(ra);
+        assert(packet);
 
-        ssize_t buflen = next_datagram_size_fd(fd);
-        if (ERRNO_IS_NEG_TRANSIENT(buflen) || ERRNO_IS_NEG_DISCONNECT(buflen))
-                return 0;
-        if (buflen < 0) {
-                log_radv_errno(ra, buflen, "Failed to determine datagram size to read, ignoring: %m");
-                return 0;
-        }
+        if (icmp6_packet_get_type(packet) != ND_ROUTER_SOLICIT)
+                return log_radv_errno(ra, SYNTHETIC_ERRNO(EBADMSG), "Received ICMP6 packet with unexpected type, ignoring.");
 
-        _cleanup_free_ char *buf = new0(char, buflen);
-        if (!buf)
-                return -ENOMEM;
+        _cleanup_(sd_ndisc_router_solicit_unrefp) sd_ndisc_router_solicit *rs = NULL;
+        rs = ndisc_router_solicit_new(packet);
+        if (!rs)
+                return log_oom_debug();
 
-        r = icmp6_receive(fd, buf, buflen, &src, &timestamp);
-        if (ERRNO_IS_NEG_TRANSIENT(r) || ERRNO_IS_NEG_DISCONNECT(r))
-                return 0;
+        r = ndisc_router_solicit_parse(ra, rs);
         if (r < 0)
-                switch (r) {
-                case -EADDRNOTAVAIL:
-                        log_radv(ra, "Received RS from neither link-local nor null address, ignoring.");
-                        return 0;
+                return r;
 
-                case -EMULTIHOP:
-                        log_radv(ra, "Received RS with invalid hop limit, ignoring.");
-                        return 0;
+        struct in6_addr src = {};
+        r = sd_ndisc_router_solicit_get_sender_address(rs, &src);
+        if (r < 0 && r != -ENODATA) /* null address is allowed */
+                return log_radv_errno(ra, r, "Failed to get sender address of RS, ignoring: %m");
 
-                case -EPFNOSUPPORT:
-                        log_radv(ra, "Received invalid source address from ICMPv6 socket, ignoring.");
-                        return 0;
+        r = radv_send_router(ra, &src, ra->lifetime_usec);
+        if (r < 0)
+                return log_radv_errno(ra, r, "Unable to send solicited Router Advertisement to %s, ignoring: %m", IN6_ADDR_TO_STRING(&src));
 
-                default:
-                        log_radv_errno(ra, r, "Unexpected error receiving from ICMPv6 socket, ignoring: %m");
-                        return 0;
-                }
+        log_radv(ra, "Sent solicited Router Advertisement to %s.", IN6_ADDR_TO_STRING(&src));
+        return 0;
+}
 
-        if ((size_t) buflen < sizeof(struct nd_router_solicit)) {
-                log_radv(ra, "Too short packet received, ignoring");
-                return 0;
-        }
+static int radv_recv(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
+        _cleanup_(icmp6_packet_unrefp) ICMP6Packet *packet = NULL;
+        sd_radv *ra = ASSERT_PTR(userdata);
+        int r;
 
-        /* TODO: if the sender address is null, check that the message does not have the source link-layer
-         * address option. See RFC 4861 Section 6.1.1. */
+        assert(fd >= 0);
 
-        const char *addr = IN6_ADDR_TO_STRING(&src);
-        r = radv_send_router(ra, &src, ra->lifetime_usec);
-        if (r < 0)
-                log_radv_errno(ra, r, "Unable to send solicited Router Advertisement to %s, ignoring: %m", addr);
-        else
-                log_radv(ra, "Sent solicited Router Advertisement to %s.", addr);
+        r = icmp6_packet_receive(fd, &packet);
+        if (r < 0) {
+                log_radv_errno(ra, r, "Failed to receive ICMPv6 packet, ignoring: %m");
+                return 0;
+        }
 
+        (void) radv_process_packet(ra, packet);
         return 0;
 }
 
index 4b5ad70cf15302d7f54f17d0bc116d429779362b..6ed7260a72bec8cd0496379e3b3a5466aa6f0e4a 100644 (file)
@@ -1,11 +1,12 @@
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 
+/* Make sure the net/if.h header is included before any linux/ one */
+#include <net/if.h>
 #include <errno.h>
+#include <linux/veth.h>
 #include <stdlib.h>
 #include <unistd.h>
 
-#include <linux/veth.h>
-#include <net/if.h>
 
 #include "sd-event.h"
 #include "sd-ipv4acd.h"
index ecf3f095c39ec268fcfd98c26766cdebcf86c1a3..37d5a0cf6c739bc62d8eb8ceb807eaf796a2016b 100644 (file)
@@ -59,8 +59,7 @@
 
 static const struct in6_addr local_address =
         { { { 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, } } };
-static const struct in6_addr mcast_address =
-        IN6ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS_INIT;
+static const struct in6_addr mcast_address = IN6_ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS;
 static const struct in6_addr ia_na_address1 = { { { IA_NA_ADDRESS1_BYTES } } };
 static const struct in6_addr ia_na_address2 = { { { IA_NA_ADDRESS2_BYTES } } };
 static const struct in6_addr ia_pd_prefix1 = { { { IA_PD_PREFIX1_BYTES } } };
@@ -1028,7 +1027,7 @@ static void test_client_callback(sd_dhcp6_client *client, int event, void *userd
         }
 }
 
-int dhcp6_network_send_udp_socket(int s, struct in6_addr *a, const void *packet, size_t len) {
+int dhcp6_network_send_udp_socket(int s, const struct in6_addr *a, const void *packet, size_t len) {
         log_debug("/* %s(count=%u) */", __func__, test_client_sent_message_count);
 
         assert_se(a);
@@ -1072,7 +1071,7 @@ int dhcp6_network_send_udp_socket(int s, struct in6_addr *a, const void *packet,
         return len;
 }
 
-int dhcp6_network_bind_udp_socket(int ifindex, struct in6_addr *a) {
+int dhcp6_network_bind_udp_socket(int ifindex, const struct in6_addr *a) {
         assert_se(ifindex == test_ifindex);
         assert_se(a);
         assert_se(in6_addr_equal(a, &local_address));
index 5dc6b1034761d52f4bb0ee40238baf344e8b2eca..b4e6d2428fe03e3badef49967840516c6995811a 100644 (file)
@@ -1,10 +1,11 @@
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 
-#include <errno.h>
+/* Make sure the net/if.h header is included before any linux/ one */
 #include <net/if.h>
+#include <errno.h>
+#include <linux/veth.h>
 #include <stdlib.h>
 #include <unistd.h>
-#include <linux/veth.h>
 
 #include "sd-event.h"
 #include "sd-ipv4ll.h"
index 5bf3e84db99ee31873b13bd1c367195019cc74fa..5ad2c92b49c1dd6281e326f133fa343157e98d4a 100644 (file)
@@ -195,7 +195,7 @@ static int send_ra(uint8_t flags) {
         return 0;
 }
 
-static void test_callback(sd_ndisc *nd, sd_ndisc_event_t event, void *message, void *userdata) {
+static void test_callback_ra(sd_ndisc *nd, sd_ndisc_event_t event, void *message, void *userdata) {
         sd_event *e = userdata;
         static unsigned idx = 0;
         uint64_t flags_array[] = {
@@ -253,7 +253,7 @@ TEST(rs) {
 
         assert_se(sd_ndisc_set_ifindex(nd, 42) >= 0);
         assert_se(sd_ndisc_set_mac(nd, &mac_addr) >= 0);
-        assert_se(sd_ndisc_set_callback(nd, test_callback, e) >= 0);
+        assert_se(sd_ndisc_set_callback(nd, test_callback_ra, e) >= 0);
 
         assert_se(sd_event_add_time_relative(e, NULL, CLOCK_BOOTTIME,
                                              30 * USEC_PER_SEC, 0,
@@ -342,7 +342,7 @@ TEST(invalid_domain) {
 
         assert_se(sd_ndisc_set_ifindex(nd, 42) >= 0);
         assert_se(sd_ndisc_set_mac(nd, &mac_addr) >= 0);
-        assert_se(sd_ndisc_set_callback(nd, test_callback, e) >= 0);
+        assert_se(sd_ndisc_set_callback(nd, test_callback_ra, e) >= 0);
 
         assert_se(sd_event_add_time_relative(e, NULL, CLOCK_BOOTTIME,
                                              30 * USEC_PER_SEC, 0,
@@ -358,6 +358,120 @@ TEST(invalid_domain) {
         test_fd[1] = -EBADF;
 }
 
+static void neighbor_dump(sd_ndisc_neighbor *na) {
+        struct in6_addr addr;
+        uint32_t flags;
+
+        assert_se(na);
+
+        log_info("--");
+        assert_se(sd_ndisc_neighbor_get_sender_address(na, &addr) >= 0);
+        log_info("Sender: %s", IN6_ADDR_TO_STRING(&addr));
+
+        assert_se(sd_ndisc_neighbor_get_flags(na, &flags) >= 0);
+        log_info("Flags: Router:%s, Solicited:%s, Override: %s",
+                 yes_no(flags & ND_NA_FLAG_ROUTER),
+                 yes_no(flags & ND_NA_FLAG_SOLICITED),
+                 yes_no(flags & ND_NA_FLAG_OVERRIDE));
+
+        assert_se(sd_ndisc_neighbor_is_router(na) == FLAGS_SET(flags, ND_NA_FLAG_ROUTER));
+        assert_se(sd_ndisc_neighbor_is_solicited(na) == FLAGS_SET(flags, ND_NA_FLAG_SOLICITED));
+        assert_se(sd_ndisc_neighbor_is_override(na) == FLAGS_SET(flags, ND_NA_FLAG_OVERRIDE));
+}
+
+static int send_na(uint32_t flags) {
+        uint8_t advertisement[] = {
+                /* struct nd_neighbor_advert */
+                0x88, 0x00, 0xde, 0x83, 0x00, 0x00, 0x00, 0x00,
+                0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+                /* type = 0x02 (SD_NDISC_OPTION_TARGET_LL_ADDRESS), length = 8 */
+                0x01, 0x01, 'A', 'B', 'C', '1', '2', '3',
+        };
+
+        ((struct nd_neighbor_advert*) advertisement)->nd_na_flags_reserved = flags;
+
+        assert_se(write(test_fd[1], advertisement, sizeof(advertisement)) == sizeof(advertisement));
+        if (verbose)
+                printf("  sent NA with flag 0x%02x\n", flags);
+
+        return 0;
+}
+
+static void test_callback_na(sd_ndisc *nd, sd_ndisc_event_t event, void *message, void *userdata) {
+        sd_event *e = userdata;
+        static unsigned idx = 0;
+        uint32_t flags_array[] = {
+                0,
+                0,
+                ND_NA_FLAG_ROUTER,
+                ND_NA_FLAG_SOLICITED,
+                ND_NA_FLAG_SOLICITED | ND_NA_FLAG_OVERRIDE,
+        };
+        uint32_t flags;
+
+        assert_se(nd);
+
+        if (event != SD_NDISC_EVENT_NEIGHBOR)
+                return;
+
+        sd_ndisc_neighbor *rt = ASSERT_PTR(message);
+
+        neighbor_dump(rt);
+
+        assert_se(sd_ndisc_neighbor_get_flags(rt, &flags) >= 0);
+        assert_se(flags == flags_array[idx]);
+        idx++;
+
+        if (verbose)
+                printf("  got event 0x%02" PRIx32 "\n", flags);
+
+        if (idx < ELEMENTSOF(flags_array)) {
+                send_na(flags_array[idx]);
+                return;
+        }
+
+        idx = 0;
+        sd_event_exit(e, 0);
+}
+
+static int on_recv_rs_na(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
+        _cleanup_(icmp6_packet_unrefp) ICMP6Packet *packet = NULL;
+        assert_se(icmp6_packet_receive(fd, &packet) >= 0);
+
+        return send_na(0);
+}
+
+TEST(na) {
+        _cleanup_(sd_event_unrefp) sd_event *e = NULL;
+        _cleanup_(sd_event_source_unrefp) sd_event_source *s = NULL;
+        _cleanup_(sd_ndisc_unrefp) sd_ndisc *nd = NULL;
+
+        assert_se(sd_event_new(&e) >= 0);
+
+        assert_se(sd_ndisc_new(&nd) >= 0);
+        assert_se(nd);
+
+        assert_se(sd_ndisc_attach_event(nd, e, 0) >= 0);
+
+        assert_se(sd_ndisc_set_ifindex(nd, 42) >= 0);
+        assert_se(sd_ndisc_set_mac(nd, &mac_addr) >= 0);
+        assert_se(sd_ndisc_set_callback(nd, test_callback_na, e) >= 0);
+
+        assert_se(sd_event_add_time_relative(e, NULL, CLOCK_BOOTTIME,
+                                             30 * USEC_PER_SEC, 0,
+                                             NULL, INT_TO_PTR(-ETIMEDOUT)) >= 0);
+
+        assert_se(sd_ndisc_start(nd) >= 0);
+
+        assert_se(sd_event_add_io(e, &s, test_fd[1], EPOLLIN, on_recv_rs_na, nd) >= 0);
+        assert_se(sd_event_source_set_io_fd_own(s, true) >= 0);
+
+        assert_se(sd_event_loop(e) >= 0);
+
+        test_fd[1] = -EBADF;
+}
+
 static int on_recv_rs_timeout(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
         _cleanup_(icmp6_packet_unrefp) ICMP6Packet *packet = NULL;
         sd_ndisc *nd = ASSERT_PTR(userdata);
diff --git a/src/libsystemd-network/test-ndisc-send.c b/src/libsystemd-network/test-ndisc-send.c
new file mode 100644 (file)
index 0000000..1b1b27d
--- /dev/null
@@ -0,0 +1,449 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <getopt.h>
+
+#include "build.h"
+#include "ether-addr-util.h"
+#include "fd-util.h"
+#include "hexdecoct.h"
+#include "icmp6-util.h"
+#include "in-addr-util.h"
+#include "main-func.h"
+#include "ndisc-option.h"
+#include "netlink-util.h"
+#include "network-common.h"
+#include "parse-util.h"
+#include "socket-util.h"
+#include "strv.h"
+#include "time-util.h"
+
+static int arg_ifindex = 0;
+static int arg_icmp6_type = 0;
+static union in_addr_union arg_dest = IN_ADDR_NULL;
+static uint8_t arg_hop_limit = 0;
+static uint8_t arg_ra_flags = 0;
+static uint8_t arg_preference = false;
+static usec_t arg_lifetime = 0;
+static usec_t arg_reachable = 0;
+static usec_t arg_retransmit = 0;
+static uint32_t arg_na_flags = 0;
+static union in_addr_union arg_target_address = IN_ADDR_NULL;
+static union in_addr_union arg_redirect_destination = IN_ADDR_NULL;
+static bool arg_set_source_mac = false;
+static struct ether_addr arg_source_mac = {};
+static bool arg_set_target_mac = false;
+static struct ether_addr arg_target_mac = {};
+static struct ip6_hdr *arg_redirected_header = NULL;
+static bool arg_set_mtu = false;
+static uint32_t arg_mtu = 0;
+
+STATIC_DESTRUCTOR_REGISTER(arg_redirected_header, freep);
+
+static int parse_icmp6_type(const char *str) {
+        if (STR_IN_SET(str, "router-solicit", "rs", "RS"))
+                return ND_ROUTER_SOLICIT;
+        if (STR_IN_SET(str, "router-advertisement", "ra", "RA"))
+                return ND_ROUTER_ADVERT;
+        if (STR_IN_SET(str, "neighbor-solicit", "ns", "NS"))
+                return ND_NEIGHBOR_SOLICIT;
+        if (STR_IN_SET(str, "neighbor-advertisement", "na", "NA"))
+                return ND_NEIGHBOR_ADVERT;
+        if (STR_IN_SET(str, "redirect", "rd", "RD"))
+                return ND_REDIRECT;
+        return -EINVAL;
+}
+
+static int parse_preference(const char *str) {
+        if (streq(str, "low"))
+                return SD_NDISC_PREFERENCE_LOW;
+        if (streq(str, "medium"))
+                return SD_NDISC_PREFERENCE_MEDIUM;
+        if (streq(str, "high"))
+                return SD_NDISC_PREFERENCE_HIGH;
+        if (streq(str, "reserved"))
+                return SD_NDISC_PREFERENCE_RESERVED;
+        return -EINVAL;
+}
+
+static int parse_argv(int argc, char *argv[]) {
+        enum {
+                ARG_VERSION = 0x100,
+                ARG_RA_HOP_LIMIT,
+                ARG_RA_MANAGED,
+                ARG_RA_OTHER,
+                ARG_RA_HOME_AGENT,
+                ARG_RA_PREFERENCE,
+                ARG_RA_LIFETIME,
+                ARG_RA_REACHABLE,
+                ARG_RA_RETRANSMIT,
+                ARG_NA_ROUTER,
+                ARG_NA_SOLICITED,
+                ARG_NA_OVERRIDE,
+                ARG_TARGET_ADDRESS,
+                ARG_REDIRECT_DESTINATION,
+                ARG_OPTION_SOURCE_LL,
+                ARG_OPTION_TARGET_LL,
+                ARG_OPTION_REDIRECTED_HEADER,
+                ARG_OPTION_MTU,
+        };
+
+        static const struct option options[] = {
+                { "version",              no_argument,       NULL, ARG_VERSION                  },
+                { "interface",            required_argument, NULL, 'i'                          },
+                { "type",                 required_argument, NULL, 't'                          },
+                { "dest",                 required_argument, NULL, 'd'                          },
+                /* For Router Advertisement */
+                { "hop-limit",            required_argument, NULL, ARG_RA_HOP_LIMIT             },
+                { "managed",              required_argument, NULL, ARG_RA_MANAGED               },
+                { "other",                required_argument, NULL, ARG_RA_OTHER                 },
+                { "home-agent",           required_argument, NULL, ARG_RA_HOME_AGENT            },
+                { "preference",           required_argument, NULL, ARG_RA_PREFERENCE            },
+                { "lifetime",             required_argument, NULL, ARG_RA_LIFETIME              },
+                { "reachable-time",       required_argument, NULL, ARG_RA_REACHABLE             },
+                { "retransmit-timer",     required_argument, NULL, ARG_RA_RETRANSMIT            },
+                /* For Neighbor Advertisement */
+                { "is-router",            required_argument, NULL, ARG_NA_ROUTER                },
+                { "is-solicited",         required_argument, NULL, ARG_NA_SOLICITED             },
+                { "is-override",          required_argument, NULL, ARG_NA_OVERRIDE              },
+                /* For Neighbor Solicit, Neighbor Advertisement, and Redirect */
+                { "target-address",       required_argument, NULL, ARG_TARGET_ADDRESS           },
+                /* For Redirect */
+                { "redirect-destination", required_argument, NULL, ARG_REDIRECT_DESTINATION     },
+                /* Options */
+                { "source-ll-address",    required_argument, NULL, ARG_OPTION_SOURCE_LL         },
+                { "target-ll-address",    required_argument, NULL, ARG_OPTION_TARGET_LL         },
+                { "redirected-header",    required_argument, NULL, ARG_OPTION_REDIRECTED_HEADER },
+                { "mtu",                  required_argument, NULL, ARG_OPTION_MTU               },
+                {}
+        };
+
+        _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
+        int c, r;
+
+        assert(argc >= 0);
+        assert(argv);
+
+        while ((c = getopt_long(argc, argv, "i:t:d:", options, NULL)) >= 0) {
+
+                switch (c) {
+
+                case ARG_VERSION:
+                        return version();
+
+                case 'i':
+                        r = rtnl_resolve_interface_or_warn(&rtnl, optarg);
+                        if (r < 0)
+                                return r;
+                        arg_ifindex = r;
+                        break;
+
+                case 't':
+                        r = parse_icmp6_type(optarg);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to parse message type: %m");
+                        arg_icmp6_type = r;
+                        break;
+
+                case 'd':
+                        r = in_addr_from_string(AF_INET6, optarg, &arg_dest);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to parse destination address: %m");
+                        if (!in6_addr_is_link_local(&arg_dest.in6))
+                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+                                                       "The destination address %s is not a link-local address.", optarg);
+                        break;
+
+                case ARG_RA_HOP_LIMIT:
+                        r = safe_atou8(optarg, &arg_hop_limit);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to parse hop limit: %m");
+                        break;
+
+                case ARG_RA_MANAGED:
+                        r = parse_boolean(optarg);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to parse managed flag: %m");
+                        SET_FLAG(arg_ra_flags, ND_RA_FLAG_MANAGED, r);
+                        break;
+
+                case ARG_RA_OTHER:
+                        r = parse_boolean(optarg);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to parse other flag: %m");
+                        SET_FLAG(arg_ra_flags, ND_RA_FLAG_OTHER, r);
+                        break;
+
+                case ARG_RA_HOME_AGENT:
+                        r = parse_boolean(optarg);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to parse home-agent flag: %m");
+                        SET_FLAG(arg_ra_flags, ND_RA_FLAG_HOME_AGENT, r);
+                        break;
+
+                case ARG_RA_PREFERENCE:
+                        r = parse_preference(optarg);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to parse preference: %m");
+                        arg_preference = r;
+                        break;
+
+                case ARG_RA_LIFETIME:
+                        r = parse_sec(optarg, &arg_lifetime);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to parse lifetime: %m");
+                        break;
+
+                case ARG_RA_REACHABLE:
+                        r = parse_sec(optarg, &arg_reachable);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to parse reachable time: %m");
+                        break;
+
+                case ARG_RA_RETRANSMIT:
+                        r = parse_sec(optarg, &arg_retransmit);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to parse retransmit timer: %m");
+                        break;
+
+                case ARG_NA_ROUTER:
+                        r = parse_boolean(optarg);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to parse is-router flag: %m");
+                        SET_FLAG(arg_na_flags, ND_NA_FLAG_ROUTER, r);
+                        break;
+
+                case ARG_NA_SOLICITED:
+                        r = parse_boolean(optarg);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to parse is-solicited flag: %m");
+                        SET_FLAG(arg_na_flags, ND_NA_FLAG_SOLICITED, r);
+                        break;
+
+                case ARG_NA_OVERRIDE:
+                        r = parse_boolean(optarg);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to parse is-override flag: %m");
+                        SET_FLAG(arg_na_flags, ND_NA_FLAG_OVERRIDE, r);
+                        break;
+
+                case ARG_TARGET_ADDRESS:
+                        r = in_addr_from_string(AF_INET6, optarg, &arg_target_address);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to parse target address: %m");
+                        break;
+
+                case ARG_REDIRECT_DESTINATION:
+                        r = in_addr_from_string(AF_INET6, optarg, &arg_redirect_destination);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to parse destination address: %m");
+                        break;
+
+                case ARG_OPTION_SOURCE_LL:
+                        r = parse_boolean(optarg);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to parse source LL address option: %m");
+                        arg_set_source_mac = r;
+                        break;
+
+                case ARG_OPTION_TARGET_LL:
+                        r = parse_ether_addr(optarg, &arg_target_mac);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to parse target LL address option: %m");
+                        arg_set_target_mac = true;
+                        break;
+
+                case ARG_OPTION_REDIRECTED_HEADER: {
+                        _cleanup_free_ void *p = NULL;
+                        size_t len;
+
+                        r = unbase64mem(optarg, &p, &len);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to parse redirected header: %m");
+
+                        if (len < sizeof(struct ip6_hdr))
+                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid redirected header.");
+
+                        arg_redirected_header = TAKE_PTR(p);
+                        break;
+                }
+                case ARG_OPTION_MTU:
+                        r = safe_atou32(optarg, &arg_mtu);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to parse MTU: %m");
+                        arg_set_mtu = true;
+                        break;
+
+                case '?':
+                        return -EINVAL;
+
+                default:
+                        assert_not_reached();
+                }
+        }
+
+        if (arg_ifindex <= 0)
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "--interface/-i option is mandatory.");
+
+        if (arg_icmp6_type <= 0)
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "--type/-t option is mandatory.");
+
+        if (in6_addr_is_null(&arg_dest.in6)) {
+                if (IN_SET(arg_icmp6_type, ND_ROUTER_ADVERT, ND_NEIGHBOR_ADVERT, ND_REDIRECT))
+                        arg_dest.in6 = IN6_ADDR_ALL_NODES_MULTICAST;
+                else
+                        arg_dest.in6 = IN6_ADDR_ALL_ROUTERS_MULTICAST;
+        }
+
+        if (arg_set_source_mac) {
+                struct hw_addr_data hw_addr;
+
+                r = rtnl_get_link_info(&rtnl, arg_ifindex,
+                                       /* ret_iftype = */ NULL,
+                                       /* ret_flags = */ NULL,
+                                       /* ret_kind = */ NULL,
+                                       &hw_addr,
+                                       /* ret_permanent_hw_addr = */ NULL);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to get the source link-layer address: %m");
+
+                if (hw_addr.length != sizeof(struct ether_addr))
+                        return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
+                                               "Unsupported hardware address length %zu: %m",
+                                               hw_addr.length);
+
+                arg_source_mac = hw_addr.ether;
+        }
+
+        return 1;
+}
+
+static int send_icmp6(int fd, const struct icmp6_hdr *hdr) {
+        _cleanup_set_free_ Set *options = NULL;
+        int r;
+
+        assert(fd >= 0);
+        assert(hdr);
+
+        if (arg_set_source_mac) {
+                r = ndisc_option_add_link_layer_address(&options, 0, SD_NDISC_OPTION_SOURCE_LL_ADDRESS, &arg_source_mac);
+                if (r < 0)
+                        return r;
+        }
+
+        if (arg_set_target_mac) {
+                r = ndisc_option_add_link_layer_address(&options, 0, SD_NDISC_OPTION_TARGET_LL_ADDRESS, &arg_target_mac);
+                if (r < 0)
+                        return r;
+        }
+
+        if (arg_redirected_header) {
+                r = ndisc_option_add_redirected_header(&options, 0, arg_redirected_header);
+                if (r < 0)
+                        return r;
+        }
+
+        if (arg_set_mtu) {
+                r = ndisc_option_add_mtu(&options, 0, arg_mtu);
+                if (r < 0)
+                        return r;
+        }
+
+        return ndisc_send(fd, &arg_dest.in6, hdr, options, now(CLOCK_BOOTTIME));
+}
+
+static int send_router_solicit(int fd) {
+        struct nd_router_solicit hdr = {
+                .nd_rs_type = ND_ROUTER_SOLICIT,
+        };
+
+        assert(fd >= 0);
+
+        return send_icmp6(fd, &hdr.nd_rs_hdr);
+}
+
+static int send_router_advertisement(int fd) {
+        struct nd_router_advert hdr = {
+                .nd_ra_type = ND_ROUTER_ADVERT,
+                .nd_ra_router_lifetime = usec_to_be16_sec(arg_lifetime),
+                .nd_ra_reachable = usec_to_be32_msec(arg_reachable),
+                .nd_ra_retransmit = usec_to_be32_msec(arg_retransmit),
+        };
+
+        assert(fd >= 0);
+
+        /* The nd_ra_curhoplimit and nd_ra_flags_reserved fields cannot specified with nd_ra_router_lifetime
+         * simultaneously in the structured initializer in the above. */
+        hdr.nd_ra_curhoplimit = arg_hop_limit;
+        hdr.nd_ra_flags_reserved = arg_ra_flags;
+
+        return send_icmp6(fd, &hdr.nd_ra_hdr);
+}
+
+static int send_neighbor_solicit(int fd) {
+        struct nd_neighbor_solicit hdr = {
+                .nd_ns_type = ND_NEIGHBOR_SOLICIT,
+                .nd_ns_target = arg_target_address.in6,
+        };
+
+        assert(fd >= 0);
+
+        return send_icmp6(fd, &hdr.nd_ns_hdr);
+}
+
+static int send_neighbor_advertisement(int fd) {
+        struct nd_neighbor_advert hdr = {
+                .nd_na_type = ND_NEIGHBOR_ADVERT,
+                .nd_na_flags_reserved = arg_na_flags,
+                .nd_na_target = arg_target_address.in6,
+        };
+
+        assert(fd >= 0);
+
+        return send_icmp6(fd, &hdr.nd_na_hdr);
+}
+
+static int send_redirect(int fd) {
+        struct nd_redirect hdr = {
+                .nd_rd_type = ND_REDIRECT,
+                .nd_rd_target = arg_target_address.in6,
+                .nd_rd_dst = arg_redirect_destination.in6,
+        };
+
+        assert(fd >= 0);
+
+        return send_icmp6(fd, &hdr.nd_rd_hdr);
+}
+
+static int run(int argc, char *argv[]) {
+        _cleanup_close_ int fd = -EBADF;
+        int r;
+
+        log_setup();
+
+        r = parse_argv(argc, argv);
+        if (r <= 0)
+                return r;
+
+        fd = icmp6_bind(arg_ifindex, /* is_router = */ false);
+        if (fd < 0)
+                return log_error_errno(fd, "Failed to bind socket to interface: %m");
+
+        switch (arg_icmp6_type) {
+        case ND_ROUTER_SOLICIT:
+                return send_router_solicit(fd);
+        case ND_ROUTER_ADVERT:
+                return send_router_advertisement(fd);
+        case ND_NEIGHBOR_SOLICIT:
+                return send_neighbor_solicit(fd);
+        case ND_NEIGHBOR_ADVERT:
+                return send_neighbor_advertisement(fd);
+        case ND_REDIRECT:
+                return send_redirect(fd);
+        default:
+                assert_not_reached();
+        }
+
+        return 0;
+}
+
+DEFINE_MAIN_FUNCTION(run);
index 77b2e1a0fded7537d2a355f50d4a2c63c826df37..f415797700ef38e98ede8ef2ce7deb36c44672cc 100644 (file)
@@ -277,14 +277,16 @@ _public_ int sd_bus_error_setf(sd_bus_error *e, const char *name, const char *fo
 
                 va_start(ap, format);
                 r = sd_bus_error_setfv(e, name, format, ap);
-                assert(!name || r < 0);
+                if (name)
+                        assert(r < 0);
                 va_end(ap);
 
                 return r;
         }
 
         r = sd_bus_error_set(e, name, NULL);
-        assert(!name || r < 0);
+        if (name)
+                assert(r < 0);
         return r;
 }
 
index 018a68d1dbbe490345962889aa6ab6794004f7ca..a7ef03abac262001bf16de97a3352c91ba63f1d1 100644 (file)
@@ -473,7 +473,7 @@ static bool check_sender_uid(sd_device_monitor *m, uid_t uid) {
                 return true;
 
         if (!m->mapped_userns_uid_range) {
-                r = uid_range_load_userns(&m->mapped_userns_uid_range, NULL);
+                r = uid_range_load_userns(/* path = */ NULL, UID_RANGE_USERNS_INSIDE, &m->mapped_userns_uid_range);
                 if (r < 0)
                         log_monitor_errno(m, r, "Failed to load UID ranges mapped to the current user namespace, ignoring: %m");
         }
index da20b503d4ffd94f26549d5936c1908fcf6910e2..298d21ed29d5f225fdd97eb38fef5003120067d5 100644 (file)
@@ -9,6 +9,8 @@
 #include "hexdecoct.h"
 #include "id128-util.h"
 #include "io-util.h"
+#include "namespace-util.h"
+#include "process-util.h"
 #include "sha256.h"
 #include "stdio-util.h"
 #include "string-util.h"
@@ -268,3 +270,64 @@ sd_id128_t id128_digest(const void *data, size_t size) {
 
         return id128_make_v4_uuid(id);
 }
+
+int id128_get_boot_for_machine(const char *machine, sd_id128_t *ret) {
+        _cleanup_close_ int pidnsfd = -EBADF, mntnsfd = -EBADF, rootfd = -EBADF;
+        _cleanup_close_pair_ int pair[2] = EBADF_PAIR;
+        pid_t pid, child;
+        sd_id128_t id;
+        ssize_t k;
+        int r;
+
+        assert(ret);
+
+        if (isempty(machine))
+                return sd_id128_get_boot(ret);
+
+        r = container_get_leader(machine, &pid);
+        if (r < 0)
+                return r;
+
+        r = namespace_open(pid, &pidnsfd, &mntnsfd, /* ret_netns_fd = */ NULL, /* ret_userns_fd = */ NULL, &rootfd);
+        if (r < 0)
+                return r;
+
+        if (socketpair(AF_UNIX, SOCK_DGRAM, 0, pair) < 0)
+                return -errno;
+
+        r = namespace_fork("(sd-bootidns)", "(sd-bootid)", NULL, 0, FORK_RESET_SIGNALS|FORK_DEATHSIG_SIGKILL,
+                           pidnsfd, mntnsfd, -1, -1, rootfd, &child);
+        if (r < 0)
+                return r;
+        if (r == 0) {
+                pair[0] = safe_close(pair[0]);
+
+                r = id128_get_boot(&id);
+                if (r < 0)
+                        _exit(EXIT_FAILURE);
+
+                k = send(pair[1], &id, sizeof(id), MSG_NOSIGNAL);
+                if (k != sizeof(id))
+                        _exit(EXIT_FAILURE);
+
+                _exit(EXIT_SUCCESS);
+        }
+
+        pair[1] = safe_close(pair[1]);
+
+        r = wait_for_terminate_and_check("(sd-bootidns)", child, 0);
+        if (r < 0)
+                return r;
+        if (r != EXIT_SUCCESS)
+                return -EIO;
+
+        k = recv(pair[0], &id, sizeof(id), 0);
+        if (k != sizeof(id))
+                return -EIO;
+
+        if (sd_id128_is_null(id))
+                return -EIO;
+
+        *ret = id;
+        return 0;
+}
index 53ba50a8ace5d42554b05edad0fec0462978fc7e..458d43077198f26713f2c1afdf6df471e538e768 100644 (file)
@@ -49,6 +49,9 @@ int id128_get_product(sd_id128_t *ret);
 
 sd_id128_t id128_digest(const void *data, size_t size);
 
+int id128_get_boot(sd_id128_t *ret);
+int id128_get_boot_for_machine(const char *machine, sd_id128_t *ret);
+
 /* A helper to check for the three relevant cases of "machine ID not initialized" */
 #define ERRNO_IS_NEG_MACHINE_ID_UNSET(r)        \
         IN_SET(r,                               \
index 9fda79ae266cf716634d36f0f4359c79963c429d..4336d3f1b709c2a5b43fb7a6e385d453f7feb3aa 100644 (file)
@@ -170,14 +170,24 @@ int id128_get_machine(const char *root, sd_id128_t *ret) {
         return id128_read_fd(fd, ID128_FORMAT_PLAIN | ID128_REFUSE_NULL, ret);
 }
 
+int id128_get_boot(sd_id128_t *ret) {
+        int r;
+
+        assert(ret);
+
+        r = id128_read("/proc/sys/kernel/random/boot_id", ID128_FORMAT_UUID | ID128_REFUSE_NULL, ret);
+        if (r == -ENOENT && proc_mounted() == 0)
+                return -ENOSYS;
+
+        return r;
+}
+
 _public_ int sd_id128_get_boot(sd_id128_t *ret) {
         static thread_local sd_id128_t saved_boot_id = {};
         int r;
 
         if (sd_id128_is_null(saved_boot_id)) {
-                r = id128_read("/proc/sys/kernel/random/boot_id", ID128_FORMAT_UUID | ID128_REFUSE_NULL, &saved_boot_id);
-                if (r == -ENOENT && proc_mounted() == 0)
-                        return -ENOSYS;
+                r = id128_get_boot(&saved_boot_id);
                 if (r < 0)
                         return r;
         }
index e86be6af3539f24c052750dfcd1b1c2251d23744..85632b06d3a2815b9608ff551ee2aaa7c75909ff 100644 (file)
@@ -3,21 +3,6 @@
  * fsprg v0.1  -  (seekable) forward-secure pseudorandom generator
  * Copyright © 2012 B. Poettering
  * Contact: fsprg@point-at-infinity.org
- *
- * This library 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.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- * 02110-1301  USA
  */
 
 /*
@@ -50,11 +35,11 @@ static void mpi_export(void *buf, size_t buflen, const gcry_mpi_t x) {
         unsigned len;
         size_t nwritten;
 
-        assert(gcry_mpi_cmp_ui(x, 0) >= 0);
-        len = (gcry_mpi_get_nbits(x) + 7) / 8;
+        assert(sym_gcry_mpi_cmp_ui(x, 0) >= 0);
+        len = (sym_gcry_mpi_get_nbits(x) + 7) / 8;
         assert(len <= buflen);
         memzero(buf, buflen);
-        gcry_mpi_print(GCRYMPI_FMT_USG, buf + (buflen - len), len, &nwritten, x);
+        sym_gcry_mpi_print(GCRYMPI_FMT_USG, buf + (buflen - len), len, &nwritten, x);
         assert(nwritten == len);
 }
 
@@ -62,10 +47,10 @@ static gcry_mpi_t mpi_import(const void *buf, size_t buflen) {
         gcry_mpi_t h;
         _unused_ unsigned len;
 
-        assert_se(gcry_mpi_scan(&h, GCRYMPI_FMT_USG, buf, buflen, NULL) == 0);
-        len = (gcry_mpi_get_nbits(h) + 7) / 8;
+        assert_se(sym_gcry_mpi_scan(&h, GCRYMPI_FMT_USG, buf, buflen, NULL) == 0);
+        len = (sym_gcry_mpi_get_nbits(h) + 7) / 8;
         assert(len <= buflen);
-        assert(gcry_mpi_cmp_ui(h, 0) >= 0);
+        assert(sym_gcry_mpi_cmp_ui(h, 0) >= 0);
 
         return h;
 }
@@ -102,30 +87,30 @@ static void det_randomize(void *buf, size_t buflen, const void *seed, size_t see
         gcry_error_t err;
         uint32_t ctr;
 
-        olen = gcry_md_get_algo_dlen(RND_HASH);
-        err = gcry_md_open(&hd, RND_HASH, 0);
+        olen = sym_gcry_md_get_algo_dlen(RND_HASH);
+        err = sym_gcry_md_open(&hd, RND_HASH, 0);
         assert_se(gcry_err_code(err) == GPG_ERR_NO_ERROR); /* This shouldn't happen */
-        gcry_md_write(hd, seed, seedlen);
-        gcry_md_putc(hd, (idx >> 24) & 0xff);
-        gcry_md_putc(hd, (idx >> 16) & 0xff);
-        gcry_md_putc(hd, (idx >>  8) & 0xff);
-        gcry_md_putc(hd, (idx >>  0) & 0xff);
+        sym_gcry_md_write(hd, seed, seedlen);
+        sym_gcry_md_putc(hd, (idx >> 24) & 0xff);
+        sym_gcry_md_putc(hd, (idx >> 16) & 0xff);
+        sym_gcry_md_putc(hd, (idx >>  8) & 0xff);
+        sym_gcry_md_putc(hd, (idx >>  0) & 0xff);
 
         for (ctr = 0; buflen; ctr++) {
-                err = gcry_md_copy(&hd2, hd);
+                err = sym_gcry_md_copy(&hd2, hd);
                 assert_se(gcry_err_code(err) == GPG_ERR_NO_ERROR); /* This shouldn't happen */
-                gcry_md_putc(hd2, (ctr >> 24) & 0xff);
-                gcry_md_putc(hd2, (ctr >> 16) & 0xff);
-                gcry_md_putc(hd2, (ctr >>  8) & 0xff);
-                gcry_md_putc(hd2, (ctr >>  0) & 0xff);
-                gcry_md_final(hd2);
+                sym_gcry_md_putc(hd2, (ctr >> 24) & 0xff);
+                sym_gcry_md_putc(hd2, (ctr >> 16) & 0xff);
+                sym_gcry_md_putc(hd2, (ctr >>  8) & 0xff);
+                sym_gcry_md_putc(hd2, (ctr >>  0) & 0xff);
+                sym_gcry_md_ctl(hd2, GCRYCTL_FINALIZE, NULL, 0);
                 cpylen = (buflen < olen) ? buflen : olen;
-                memcpy(buf, gcry_md_read(hd2, RND_HASH), cpylen);
-                gcry_md_close(hd2);
+                memcpy(buf, sym_gcry_md_read(hd2, RND_HASH), cpylen);
+                sym_gcry_md_close(hd2);
                 buf += cpylen;
                 buflen -= cpylen;
         }
-        gcry_md_close(hd);
+        sym_gcry_md_close(hd);
 }
 
 /* deterministically generate from seed/idx a prime of length `bits' that is 3 (mod 4) */
@@ -142,8 +127,8 @@ static gcry_mpi_t genprime3mod4(int bits, const void *seed, size_t seedlen, uint
         buf[buflen - 1] |= 0x03; /* set lower two bits, to have result 3 (mod 4) */
 
         p = mpi_import(buf, buflen);
-        while (gcry_prime_check(p, 0))
-                gcry_mpi_add_ui(p, p, 4);
+        while (sym_gcry_prime_check(p, 0))
+                sym_gcry_mpi_add_ui(p, p, 4);
 
         return p;
 }
@@ -157,8 +142,8 @@ static gcry_mpi_t gensquare(const gcry_mpi_t n, const void *seed, size_t seedlen
         det_randomize(buf, buflen, seed, seedlen, idx);
         buf[0] &= 0x7f; /* clear upper bit, so that we have x < n */
         x = mpi_import(buf, buflen);
-        assert(gcry_mpi_cmp(x, n) < 0);
-        gcry_mpi_mulm(x, x, x, n);
+        assert(sym_gcry_mpi_cmp(x, n) < 0);
+        sym_gcry_mpi_mulm(x, x, x, n);
         return x;
 }
 
@@ -167,51 +152,51 @@ static gcry_mpi_t twopowmodphi(uint64_t m, const gcry_mpi_t p) {
         gcry_mpi_t phi, r;
         int n;
 
-        phi = gcry_mpi_new(0);
-        gcry_mpi_sub_ui(phi, p, 1);
+        phi = sym_gcry_mpi_new(0);
+        sym_gcry_mpi_sub_ui(phi, p, 1);
 
         /* count number of used bits in m */
         for (n = 0; (1ULL << n) <= m; n++)
                 ;
 
-        r = gcry_mpi_new(0);
-        gcry_mpi_set_ui(r, 1);
+        r = sym_gcry_mpi_new(0);
+        sym_gcry_mpi_set_ui(r, 1);
         while (n) { /* square and multiply algorithm for fast exponentiation */
                 n--;
-                gcry_mpi_mulm(r, r, r, phi);
+                sym_gcry_mpi_mulm(r, r, r, phi);
                 if (m & ((uint64_t)1 << n)) {
-                        gcry_mpi_add(r, r, r);
-                        if (gcry_mpi_cmp(r, phi) >= 0)
-                                gcry_mpi_sub(r, r, phi);
+                        sym_gcry_mpi_add(r, r, r);
+                        if (sym_gcry_mpi_cmp(r, phi) >= 0)
+                                sym_gcry_mpi_sub(r, r, phi);
                 }
         }
 
-        gcry_mpi_release(phi);
+        sym_gcry_mpi_release(phi);
         return r;
 }
 
 /* Decompose $x \in Z_n$ into $(xp,xq) \in Z_p \times Z_q$ using Chinese Remainder Theorem */
 static void CRT_decompose(gcry_mpi_t *xp, gcry_mpi_t *xq, const gcry_mpi_t x, const gcry_mpi_t p, const gcry_mpi_t q) {
-        *xp = gcry_mpi_new(0);
-        *xq = gcry_mpi_new(0);
-        gcry_mpi_mod(*xp, x, p);
-        gcry_mpi_mod(*xq, x, q);
+        *xp = sym_gcry_mpi_new(0);
+        *xq = sym_gcry_mpi_new(0);
+        sym_gcry_mpi_mod(*xp, x, p);
+        sym_gcry_mpi_mod(*xq, x, q);
 }
 
 /* Compose $(xp,xq) \in Z_p \times Z_q$ into $x \in Z_n$ using Chinese Remainder Theorem */
 static void CRT_compose(gcry_mpi_t *x, const gcry_mpi_t xp, const gcry_mpi_t xq, const gcry_mpi_t p, const gcry_mpi_t q) {
         gcry_mpi_t a, u;
 
-        a = gcry_mpi_new(0);
-        u = gcry_mpi_new(0);
-        *x = gcry_mpi_new(0);
-        gcry_mpi_subm(a, xq, xp, q);
-        gcry_mpi_invm(u, p, q);
-        gcry_mpi_mulm(a, a, u, q); /* a = (xq - xp) / p  (mod q) */
-        gcry_mpi_mul(*x, p, a);
-        gcry_mpi_add(*x, *x, xp); /* x = p * ((xq - xp) / p mod q) + xp */
-        gcry_mpi_release(a);
-        gcry_mpi_release(u);
+        a = sym_gcry_mpi_new(0);
+        u = sym_gcry_mpi_new(0);
+        *x = sym_gcry_mpi_new(0);
+        sym_gcry_mpi_subm(a, xq, xp, q);
+        sym_gcry_mpi_invm(u, p, q);
+        sym_gcry_mpi_mulm(a, a, u, q); /* a = (xq - xp) / p  (mod q) */
+        sym_gcry_mpi_mul(*x, p, a);
+        sym_gcry_mpi_add(*x, *x, xp); /* x = p * ((xq - xp) / p mod q) + xp */
+        sym_gcry_mpi_release(a);
+        sym_gcry_mpi_release(u);
 }
 
 /******************************************************************************/
@@ -245,18 +230,21 @@ static uint16_t read_secpar(const void *buf) {
         return 16 * (secpar + 1);
 }
 
-void FSPRG_GenMK(void *msk, void *mpk, const void *seed, size_t seedlen, unsigned _secpar) {
+int FSPRG_GenMK(void *msk, void *mpk, const void *seed, size_t seedlen, unsigned _secpar) {
         uint8_t iseed[FSPRG_RECOMMENDED_SEEDLEN];
         gcry_mpi_t n, p, q;
         uint16_t secpar;
+        int r;
 
         VALIDATE_SECPAR(_secpar);
         secpar = _secpar;
 
-        initialize_libgcrypt(false);
+        r = initialize_libgcrypt(false);
+        if (r < 0)
+                return r;
 
         if (!seed) {
-                gcry_randomize(iseed, FSPRG_RECOMMENDED_SEEDLEN, GCRY_STRONG_RANDOM);
+                sym_gcry_randomize(iseed, FSPRG_RECOMMENDED_SEEDLEN, GCRY_STRONG_RANDOM);
                 seed = iseed;
                 seedlen = FSPRG_RECOMMENDED_SEEDLEN;
         }
@@ -271,25 +259,30 @@ void FSPRG_GenMK(void *msk, void *mpk, const void *seed, size_t seedlen, unsigne
         }
 
         if (mpk) {
-                n = gcry_mpi_new(0);
-                gcry_mpi_mul(n, p, q);
-                assert(gcry_mpi_get_nbits(n) == secpar);
+                n = sym_gcry_mpi_new(0);
+                sym_gcry_mpi_mul(n, p, q);
+                assert(sym_gcry_mpi_get_nbits(n) == secpar);
 
                 store_secpar(mpk + 0, secpar);
                 mpi_export(mpk + 2, secpar / 8, n);
 
-                gcry_mpi_release(n);
+                sym_gcry_mpi_release(n);
         }
 
-        gcry_mpi_release(p);
-        gcry_mpi_release(q);
+        sym_gcry_mpi_release(p);
+        sym_gcry_mpi_release(q);
+
+        return 0;
 }
 
-void FSPRG_GenState0(void *state, const void *mpk, const void *seed, size_t seedlen) {
+int FSPRG_GenState0(void *state, const void *mpk, const void *seed, size_t seedlen) {
         gcry_mpi_t n, x;
         uint16_t secpar;
+        int r;
 
-        initialize_libgcrypt(false);
+        r = initialize_libgcrypt(false);
+        if (r < 0)
+                return r;
 
         secpar = read_secpar(mpk + 0);
         n = mpi_import(mpk + 2, secpar / 8);
@@ -299,30 +292,37 @@ void FSPRG_GenState0(void *state, const void *mpk, const void *seed, size_t seed
         mpi_export(state + 2 + 1 * secpar / 8, secpar / 8, x);
         memzero(state + 2 + 2 * secpar / 8, 8);
 
-        gcry_mpi_release(n);
-        gcry_mpi_release(x);
+        sym_gcry_mpi_release(n);
+        sym_gcry_mpi_release(x);
+
+        return 0;
 }
 
-void FSPRG_Evolve(void *state) {
+int FSPRG_Evolve(void *state) {
         gcry_mpi_t n, x;
         uint16_t secpar;
         uint64_t epoch;
+        int r;
 
-        initialize_libgcrypt(false);
+        r = initialize_libgcrypt(false);
+        if (r < 0)
+                return r;
 
         secpar = read_secpar(state + 0);
         n = mpi_import(state + 2 + 0 * secpar / 8, secpar / 8);
         x = mpi_import(state + 2 + 1 * secpar / 8, secpar / 8);
         epoch = uint64_import(state + 2 + 2 * secpar / 8, 8);
 
-        gcry_mpi_mulm(x, x, x, n);
+        sym_gcry_mpi_mulm(x, x, x, n);
         epoch++;
 
         mpi_export(state + 2 + 1 * secpar / 8, secpar / 8, x);
         uint64_export(state + 2 + 2 * secpar / 8, 8, epoch);
 
-        gcry_mpi_release(n);
-        gcry_mpi_release(x);
+        sym_gcry_mpi_release(n);
+        sym_gcry_mpi_release(x);
+
+        return 0;
 }
 
 uint64_t FSPRG_GetEpoch(const void *state) {
@@ -331,18 +331,21 @@ uint64_t FSPRG_GetEpoch(const void *state) {
         return uint64_import(state + 2 + 2 * secpar / 8, 8);
 }
 
-void FSPRG_Seek(void *state, uint64_t epoch, const void *msk, const void *seed, size_t seedlen) {
+int FSPRG_Seek(void *state, uint64_t epoch, const void *msk, const void *seed, size_t seedlen) {
         gcry_mpi_t p, q, n, x, xp, xq, kp, kq, xm;
         uint16_t secpar;
+        int r;
 
-        initialize_libgcrypt(false);
+        r = initialize_libgcrypt(false);
+        if (r < 0)
+                return r;
 
         secpar = read_secpar(msk + 0);
         p  = mpi_import(msk + 2 + 0 * (secpar / 2) / 8, (secpar / 2) / 8);
         q  = mpi_import(msk + 2 + 1 * (secpar / 2) / 8, (secpar / 2) / 8);
 
-        n = gcry_mpi_new(0);
-        gcry_mpi_mul(n, p, q);
+        n = sym_gcry_mpi_new(0);
+        sym_gcry_mpi_mul(n, p, q);
 
         x = gensquare(n, seed, seedlen, RND_GEN_X, secpar);
         CRT_decompose(&xp, &xq, x, p, q); /* split (mod n) into (mod p) and (mod q) using CRT */
@@ -350,8 +353,8 @@ void FSPRG_Seek(void *state, uint64_t epoch, const void *msk, const void *seed,
         kp = twopowmodphi(epoch, p); /* compute 2^epoch (mod phi(p)) */
         kq = twopowmodphi(epoch, q); /* compute 2^epoch (mod phi(q)) */
 
-        gcry_mpi_powm(xp, xp, kp, p); /* compute x^(2^epoch) (mod p) */
-        gcry_mpi_powm(xq, xq, kq, q); /* compute x^(2^epoch) (mod q) */
+        sym_gcry_mpi_powm(xp, xp, kp, p); /* compute x^(2^epoch) (mod p) */
+        sym_gcry_mpi_powm(xq, xq, kq, q); /* compute x^(2^epoch) (mod q) */
 
         CRT_compose(&xm, xp, xq, p, q); /* combine (mod p) and (mod q) to (mod n) using CRT */
 
@@ -360,22 +363,29 @@ void FSPRG_Seek(void *state, uint64_t epoch, const void *msk, const void *seed,
         mpi_export(state + 2 + 1 * secpar / 8, secpar / 8, xm);
         uint64_export(state + 2 + 2 * secpar / 8, 8, epoch);
 
-        gcry_mpi_release(p);
-        gcry_mpi_release(q);
-        gcry_mpi_release(n);
-        gcry_mpi_release(x);
-        gcry_mpi_release(xp);
-        gcry_mpi_release(xq);
-        gcry_mpi_release(kp);
-        gcry_mpi_release(kq);
-        gcry_mpi_release(xm);
+        sym_gcry_mpi_release(p);
+        sym_gcry_mpi_release(q);
+        sym_gcry_mpi_release(n);
+        sym_gcry_mpi_release(x);
+        sym_gcry_mpi_release(xp);
+        sym_gcry_mpi_release(xq);
+        sym_gcry_mpi_release(kp);
+        sym_gcry_mpi_release(kq);
+        sym_gcry_mpi_release(xm);
+
+        return 0;
 }
 
-void FSPRG_GetKey(const void *state, void *key, size_t keylen, uint32_t idx) {
+int FSPRG_GetKey(const void *state, void *key, size_t keylen, uint32_t idx) {
         uint16_t secpar;
+        int r;
 
-        initialize_libgcrypt(false);
+        r = initialize_libgcrypt(false);
+        if (r < 0)
+                return r;
 
         secpar = read_secpar(state + 0);
         det_randomize(key, keylen, state + 2, 2 * secpar / 8 + 8, idx);
+
+        return 0;
 }
index d3d88aab31a05b127218a73e9ed54c4269596c8a..a0594cd4382273e41ce979eed876be1ded90e930 100644 (file)
@@ -5,21 +5,6 @@
  * fsprg v0.1  -  (seekable) forward-secure pseudorandom generator
  * Copyright © 2012 B. Poettering
  * Contact: fsprg@point-at-infinity.org
- *
- * This library 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.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- * 02110-1301  USA
  */
 
 #include <inttypes.h>
@@ -39,22 +24,22 @@ size_t FSPRG_mpkinbytes(unsigned secpar) _const_;
 size_t FSPRG_stateinbytes(unsigned secpar) _const_;
 
 /* Setup msk and mpk. Providing seed != NULL makes this algorithm deterministic. */
-void FSPRG_GenMK(void *msk, void *mpk, const void *seed, size_t seedlen, unsigned secpar);
+int FSPRG_GenMK(void *msk, void *mpk, const void *seed, size_t seedlen, unsigned secpar);
 
 /* Initialize state deterministically in dependence on seed. */
 /* Note: in case one wants to run only one GenState0 per GenMK it is safe to use
    the same seed for both GenMK and GenState0.
 */
-void FSPRG_GenState0(void *state, const void *mpk, const void *seed, size_t seedlen);
+int FSPRG_GenState0(void *state, const void *mpk, const void *seed, size_t seedlen);
 
-void FSPRG_Evolve(void *state);
+int FSPRG_Evolve(void *state);
 
 uint64_t FSPRG_GetEpoch(const void *state) _pure_;
 
 /* Seek to any arbitrary state (by providing msk together with seed from GenState0). */
-void FSPRG_Seek(void *state, uint64_t epoch, const void *msk, const void *seed, size_t seedlen);
+int FSPRG_Seek(void *state, uint64_t epoch, const void *msk, const void *seed, size_t seedlen);
 
-void FSPRG_GetKey(const void *state, void *key, size_t keylen, uint32_t idx);
+int FSPRG_GetKey(const void *state, void *key, size_t keylen, uint32_t idx);
 
 #ifdef __cplusplus
 }
index 8e7533eee3d852207a8632305fa03bf89371d1d9..64f5739ba8a14b45cdcd938830755fb7978ecd04 100644 (file)
@@ -71,7 +71,7 @@ int journal_file_append_tag(JournalFile *f) {
                 return r;
 
         /* Get the HMAC tag and store it in the object */
-        memcpy(o->tag.tag, gcry_md_read(f->hmac, 0), TAG_LENGTH);
+        memcpy(o->tag.tag, sym_gcry_md_read(f->hmac, 0), TAG_LENGTH);
         f->hmac_running = false;
 
         return 0;
@@ -80,6 +80,7 @@ int journal_file_append_tag(JournalFile *f) {
 int journal_file_hmac_start(JournalFile *f) {
         uint8_t key[256 / 8]; /* Let's pass 256 bit from FSPRG to HMAC */
         gcry_error_t err;
+        int r;
 
         assert(f);
 
@@ -90,13 +91,17 @@ int journal_file_hmac_start(JournalFile *f) {
                 return 0;
 
         /* Prepare HMAC for next cycle */
-        gcry_md_reset(f->hmac);
-        FSPRG_GetKey(f->fsprg_state, key, sizeof(key), 0);
-        err = gcry_md_setkey(f->hmac, key, sizeof(key));
+        sym_gcry_md_reset(f->hmac);
+
+        r = FSPRG_GetKey(f->fsprg_state, key, sizeof(key), 0);
+        if (r < 0)
+                return r;
+
+        err = sym_gcry_md_setkey(f->hmac, key, sizeof(key));
         if (gcry_err_code(err) != GPG_ERR_NO_ERROR)
                 return log_debug_errno(SYNTHETIC_ERRNO(EIO),
-                                       "gcry_md_setkey() failed with error code: %s",
-                                       gcry_strerror(err));
+                                       "sym_gcry_md_setkey() failed with error code: %s",
+                                       sym_gcry_strerror(err));
 
         f->hmac_running = true;
 
@@ -167,7 +172,10 @@ int journal_file_fsprg_evolve(JournalFile *f, uint64_t realtime) {
                 if (epoch == goal)
                         return 0;
 
-                FSPRG_Evolve(f->fsprg_state);
+                r = FSPRG_Evolve(f->fsprg_state);
+                if (r < 0)
+                        return r;
+
                 epoch = FSPRG_GetEpoch(f->fsprg_state);
                 if (epoch < goal) {
                         r = journal_file_append_tag(f);
@@ -180,6 +188,7 @@ int journal_file_fsprg_evolve(JournalFile *f, uint64_t realtime) {
 int journal_file_fsprg_seek(JournalFile *f, uint64_t goal) {
         void *msk;
         uint64_t epoch;
+        int r;
 
         assert(f);
 
@@ -195,10 +204,8 @@ int journal_file_fsprg_seek(JournalFile *f, uint64_t goal) {
                 if (goal == epoch)
                         return 0;
 
-                if (goal == epoch + 1) {
-                        FSPRG_Evolve(f->fsprg_state);
-                        return 0;
-                }
+                if (goal == epoch + 1)
+                        return FSPRG_Evolve(f->fsprg_state);
         } else {
                 f->fsprg_state_size = FSPRG_stateinbytes(FSPRG_RECOMMENDED_SECPAR);
                 f->fsprg_state = malloc(f->fsprg_state_size);
@@ -209,10 +216,12 @@ int journal_file_fsprg_seek(JournalFile *f, uint64_t goal) {
         log_debug("Seeking FSPRG key to %"PRIu64".", goal);
 
         msk = alloca_safe(FSPRG_mskinbytes(FSPRG_RECOMMENDED_SECPAR));
-        FSPRG_GenMK(msk, NULL, f->fsprg_seed, f->fsprg_seed_size, FSPRG_RECOMMENDED_SECPAR);
-        FSPRG_Seek(f->fsprg_state, goal, msk, f->fsprg_seed, f->fsprg_seed_size);
 
-        return 0;
+        r = FSPRG_GenMK(msk, NULL, f->fsprg_seed, f->fsprg_seed_size, FSPRG_RECOMMENDED_SECPAR);
+        if (r < 0)
+                return r;
+
+        return FSPRG_Seek(f->fsprg_state, goal, msk, f->fsprg_seed, f->fsprg_seed_size);
 }
 
 int journal_file_maybe_append_tag(JournalFile *f, uint64_t realtime) {
@@ -260,25 +269,25 @@ int journal_file_hmac_put_object(JournalFile *f, ObjectType type, Object *o, uin
         } else if (type > OBJECT_UNUSED && o->object.type != type)
                 return -EBADMSG;
 
-        gcry_md_write(f->hmac, o, offsetof(ObjectHeader, payload));
+        sym_gcry_md_write(f->hmac, o, offsetof(ObjectHeader, payload));
 
         switch (o->object.type) {
 
         case OBJECT_DATA:
                 /* All but hash and payload are mutable */
-                gcry_md_write(f->hmac, &o->data.hash, sizeof(o->data.hash));
-                gcry_md_write(f->hmac, journal_file_data_payload_field(f, o), le64toh(o->object.size) - journal_file_data_payload_offset(f));
+                sym_gcry_md_write(f->hmac, &o->data.hash, sizeof(o->data.hash));
+                sym_gcry_md_write(f->hmac, journal_file_data_payload_field(f, o), le64toh(o->object.size) - journal_file_data_payload_offset(f));
                 break;
 
         case OBJECT_FIELD:
                 /* Same here */
-                gcry_md_write(f->hmac, &o->field.hash, sizeof(o->field.hash));
-                gcry_md_write(f->hmac, o->field.payload, le64toh(o->object.size) - offsetof(Object, field.payload));
+                sym_gcry_md_write(f->hmac, &o->field.hash, sizeof(o->field.hash));
+                sym_gcry_md_write(f->hmac, o->field.payload, le64toh(o->object.size) - offsetof(Object, field.payload));
                 break;
 
         case OBJECT_ENTRY:
                 /* All */
-                gcry_md_write(f->hmac, &o->entry.seqnum, le64toh(o->object.size) - offsetof(Object, entry.seqnum));
+                sym_gcry_md_write(f->hmac, &o->entry.seqnum, le64toh(o->object.size) - offsetof(Object, entry.seqnum));
                 break;
 
         case OBJECT_FIELD_HASH_TABLE:
@@ -289,8 +298,8 @@ int journal_file_hmac_put_object(JournalFile *f, ObjectType type, Object *o, uin
 
         case OBJECT_TAG:
                 /* All but the tag itself */
-                gcry_md_write(f->hmac, &o->tag.seqnum, sizeof(o->tag.seqnum));
-                gcry_md_write(f->hmac, &o->tag.epoch, sizeof(o->tag.epoch));
+                sym_gcry_md_write(f->hmac, &o->tag.seqnum, sizeof(o->tag.seqnum));
+                sym_gcry_md_write(f->hmac, &o->tag.epoch, sizeof(o->tag.epoch));
                 break;
         default:
                 return -EINVAL;
@@ -318,10 +327,10 @@ int journal_file_hmac_put_header(JournalFile *f) {
          * tail_entry_monotonic, n_data, n_fields, n_tags,
          * n_entry_arrays. */
 
-        gcry_md_write(f->hmac, f->header->signature, offsetof(Header, state) - offsetof(Header, signature));
-        gcry_md_write(f->hmac, &f->header->file_id, offsetof(Header, tail_entry_boot_id) - offsetof(Header, file_id));
-        gcry_md_write(f->hmac, &f->header->seqnum_id, offsetof(Header, arena_size) - offsetof(Header, seqnum_id));
-        gcry_md_write(f->hmac, &f->header->data_hash_table_offset, offsetof(Header, tail_object_offset) - offsetof(Header, data_hash_table_offset));
+        sym_gcry_md_write(f->hmac, f->header->signature, offsetof(Header, state) - offsetof(Header, signature));
+        sym_gcry_md_write(f->hmac, &f->header->file_id, offsetof(Header, tail_entry_boot_id) - offsetof(Header, file_id));
+        sym_gcry_md_write(f->hmac, &f->header->seqnum_id, offsetof(Header, arena_size) - offsetof(Header, seqnum_id));
+        sym_gcry_md_write(f->hmac, &f->header->data_hash_table_offset, offsetof(Header, tail_object_offset) - offsetof(Header, data_hash_table_offset));
 
         return 0;
 }
@@ -406,13 +415,16 @@ int journal_file_fss_load(JournalFile *f) {
 
 int journal_file_hmac_setup(JournalFile *f) {
         gcry_error_t e;
+        int r;
 
         if (!JOURNAL_HEADER_SEALED(f->header))
                 return 0;
 
-        initialize_libgcrypt(true);
+        r = initialize_libgcrypt(true);
+        if (r < 0)
+                return r;
 
-        e = gcry_md_open(&f->hmac, GCRY_MD_SHA256, GCRY_MD_FLAG_HMAC);
+        e = sym_gcry_md_open(&f->hmac, GCRY_MD_SHA256, GCRY_MD_FLAG_HMAC);
         if (e != 0)
                 return -EOPNOTSUPP;
 
index 6f29b47ff2f9df7ba4a9b563603f324c76a141e7..c6652f2359563eeaff6395c37bf38f4a35e5447e 100644 (file)
@@ -20,6 +20,7 @@
 #include "fd-util.h"
 #include "format-util.h"
 #include "fs-util.h"
+#include "gcrypt-util.h"
 #include "id128-util.h"
 #include "journal-authenticate.h"
 #include "journal-def.h"
@@ -307,7 +308,7 @@ JournalFile* journal_file_close(JournalFile *f) {
         free(f->fsprg_seed);
 
         if (f->hmac)
-                gcry_md_close(f->hmac);
+                sym_gcry_md_close(f->hmac);
 #endif
 
         return mfree(f);
@@ -637,7 +638,7 @@ static int journal_file_verify_header(JournalFile *f) {
                                 return -ENODATA;
                         if (!VALID_REALTIME(le64toh(f->header->tail_entry_realtime)))
                                 return -ENODATA;
-                        if (!VALID_MONOTONIC(le64toh(f->header->tail_entry_realtime)))
+                        if (!VALID_MONOTONIC(le64toh(f->header->tail_entry_monotonic)))
                                 return -ENODATA;
                 } else {
                         /* Otherwise, the fields must be zero. */
@@ -648,7 +649,7 @@ static int journal_file_verify_header(JournalFile *f) {
                                 return -ENODATA;
                         if (f->header->tail_entry_realtime != 0)
                                 return -ENODATA;
-                        if (f->header->tail_entry_realtime != 0)
+                        if (f->header->tail_entry_monotonic != 0)
                                 return -ENODATA;
                 }
         }
@@ -2530,7 +2531,7 @@ int journal_file_append_entry(
                                                ts->realtime);
                 if (!VALID_MONOTONIC(ts->monotonic))
                         return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
-                                               "Invalid monotomic timestamp %" PRIu64 ", refusing entry.",
+                                               "Invalid monotonic timestamp %" PRIu64 ", refusing entry.",
                                                ts->monotonic);
         } else {
                 dual_timestamp_now(&_ts);
index bdaa01d66faa5b162d81a36324dae1c22e11950e..2e09e715745a5e42240a0e375d53dacc158bb58a 100644 (file)
@@ -10,6 +10,7 @@
 #include "fd-util.h"
 #include "fileio.h"
 #include "fs-util.h"
+#include "gcrypt-util.h"
 #include "journal-authenticate.h"
 #include "journal-def.h"
 #include "journal-file.h"
@@ -1224,7 +1225,7 @@ int journal_file_verify(
                                 if (r < 0)
                                         goto fail;
 
-                                if (memcmp(o->tag.tag, gcry_md_read(f->hmac, 0), TAG_LENGTH) != 0) {
+                                if (memcmp(o->tag.tag, sym_gcry_md_read(f->hmac, 0), TAG_LENGTH) != 0) {
                                         error(p, "Tag failed verification");
                                         r = -EBADMSG;
                                         goto fail;
index 383f3e5d572fcc53ec116ed27494204637f7a7ed..a4ed3804d8661aac82d03c3c04fb71cacaeae1e6 100644 (file)
@@ -233,7 +233,11 @@ _public_ int sd_journal_add_match(sd_journal *j, const void *data, size_t size)
         assert_return(!journal_origin_changed(j), -ECHILD);
         assert_return(data, -EINVAL);
 
-        if (size == 0)
+        /* If the size is unspecified, assume it's a string. Note: 0 is the public value we document for
+         * this, for historical reasons. Internally, we pretty widely started using SIZE_MAX for this in
+         * similar cases however, hence accept that too. And internally we actually prefer it, to make things
+         * less surprising. */
+        if (IN_SET(size, 0, SIZE_MAX))
                 size = strlen(data);
 
         if (!match_is_valid(data, size))
@@ -336,7 +340,7 @@ int journal_add_match_pair(sd_journal *j, const char *field, const char *value)
         if (!s)
                 return -ENOMEM;
 
-        return sd_journal_add_match(j, s, 0);
+        return sd_journal_add_match(j, s, SIZE_MAX);
 }
 
 int journal_add_matchf(sd_journal *j, const char *format, ...) {
@@ -353,7 +357,7 @@ int journal_add_matchf(sd_journal *j, const char *format, ...) {
         if (r < 0)
                 return -ENOMEM;
 
-        return sd_journal_add_match(j, s, 0);
+        return sd_journal_add_match(j, s, SIZE_MAX);
 }
 
 _public_ int sd_journal_add_conjunction(sd_journal *j) {
index 93d4b8f3c74f07bd62569175ee010230ecd1b69b..d101fe76f214e95f27aa843d1ef178c35a957bb8 100644 (file)
@@ -17,8 +17,8 @@ int main(int argc, char *argv[]) {
 
         assert_se(sd_journal_open(&j, SD_JOURNAL_LOCAL_ONLY | SD_JOURNAL_ASSUME_IMMUTABLE) >= 0);
 
-        assert_se(sd_journal_add_match(j, "_TRANSPORT=syslog", 0) >= 0);
-        assert_se(sd_journal_add_match(j, "_UID=0", 0) >= 0);
+        assert_se(sd_journal_add_match(j, "_TRANSPORT=syslog", SIZE_MAX) >= 0);
+        assert_se(sd_journal_add_match(j, "_UID=0", SIZE_MAX) >= 0);
 
         SD_JOURNAL_FOREACH_BACKWARDS(j) {
                 const void *d;
index e56b27dde3f6b195eb60a5f852406634d5495036..c2c345fb07fee81910bdc0757c8bdafc7dced675 100644 (file)
@@ -18,38 +18,38 @@ int main(int argc, char *argv[]) {
 
         assert_se(sd_journal_open(&j, SD_JOURNAL_ASSUME_IMMUTABLE) >= 0);
 
-        assert_se(sd_journal_add_match(j, "foobar", 0) < 0);
-        assert_se(sd_journal_add_match(j, "foobar=waldo", 0) < 0);
-        assert_se(sd_journal_add_match(j, "", 0) < 0);
-        assert_se(sd_journal_add_match(j, "=", 0) < 0);
-        assert_se(sd_journal_add_match(j, "=xxxxx", 0) < 0);
+        assert_se(sd_journal_add_match(j, "foobar", SIZE_MAX) < 0);
+        assert_se(sd_journal_add_match(j, "foobar=waldo", SIZE_MAX) < 0);
+        assert_se(sd_journal_add_match(j, "", SIZE_MAX) < 0);
+        assert_se(sd_journal_add_match(j, "=", SIZE_MAX) < 0);
+        assert_se(sd_journal_add_match(j, "=xxxxx", SIZE_MAX) < 0);
         assert_se(sd_journal_add_match(j, (uint8_t[4]){'A', '=', '\1', '\2'}, 4) >= 0);
         assert_se(sd_journal_add_match(j, (uint8_t[5]){'B', '=', 'C', '\0', 'D'}, 5) >= 0);
-        assert_se(sd_journal_add_match(j, "HALLO=WALDO", 0) >= 0);
-        assert_se(sd_journal_add_match(j, "QUUX=mmmm", 0) >= 0);
-        assert_se(sd_journal_add_match(j, "QUUX=xxxxx", 0) >= 0);
-        assert_se(sd_journal_add_match(j, "HALLO=", 0) >= 0);
-        assert_se(sd_journal_add_match(j, "QUUX=xxxxx", 0) >= 0);
-        assert_se(sd_journal_add_match(j, "QUUX=yyyyy", 0) >= 0);
-        assert_se(sd_journal_add_match(j, "PIFF=paff", 0) >= 0);
+        assert_se(sd_journal_add_match(j, "HALLO=WALDO", SIZE_MAX) >= 0);
+        assert_se(sd_journal_add_match(j, "QUUX=mmmm", SIZE_MAX) >= 0);
+        assert_se(sd_journal_add_match(j, "QUUX=xxxxx", SIZE_MAX) >= 0);
+        assert_se(sd_journal_add_match(j, "HALLO=", SIZE_MAX) >= 0);
+        assert_se(sd_journal_add_match(j, "QUUX=xxxxx", SIZE_MAX) >= 0);
+        assert_se(sd_journal_add_match(j, "QUUX=yyyyy", SIZE_MAX) >= 0);
+        assert_se(sd_journal_add_match(j, "PIFF=paff", SIZE_MAX) >= 0);
 
         assert_se(sd_journal_add_disjunction(j) >= 0);
 
-        assert_se(sd_journal_add_match(j, "ONE=one", 0) >= 0);
-        assert_se(sd_journal_add_match(j, "ONE=two", 0) >= 0);
-        assert_se(sd_journal_add_match(j, "TWO=two", 0) >= 0);
+        assert_se(sd_journal_add_match(j, "ONE=one", SIZE_MAX) >= 0);
+        assert_se(sd_journal_add_match(j, "ONE=two", SIZE_MAX) >= 0);
+        assert_se(sd_journal_add_match(j, "TWO=two", SIZE_MAX) >= 0);
 
         assert_se(sd_journal_add_conjunction(j) >= 0);
 
-        assert_se(sd_journal_add_match(j, "L4_1=yes", 0) >= 0);
-        assert_se(sd_journal_add_match(j, "L4_1=ok", 0) >= 0);
-        assert_se(sd_journal_add_match(j, "L4_2=yes", 0) >= 0);
-        assert_se(sd_journal_add_match(j, "L4_2=ok", 0) >= 0);
+        assert_se(sd_journal_add_match(j, "L4_1=yes", SIZE_MAX) >= 0);
+        assert_se(sd_journal_add_match(j, "L4_1=ok", SIZE_MAX) >= 0);
+        assert_se(sd_journal_add_match(j, "L4_2=yes", SIZE_MAX) >= 0);
+        assert_se(sd_journal_add_match(j, "L4_2=ok", SIZE_MAX) >= 0);
 
         assert_se(sd_journal_add_disjunction(j) >= 0);
 
-        assert_se(sd_journal_add_match(j, "L3=yes", 0) >= 0);
-        assert_se(sd_journal_add_match(j, "L3=ok", 0) >= 0);
+        assert_se(sd_journal_add_match(j, "L3=yes", SIZE_MAX) >= 0);
+        assert_se(sd_journal_add_match(j, "L3=ok", SIZE_MAX) >= 0);
 
         assert_se(t = journal_make_match_string(j));
 
index cb245b2fbd4f9707cfa007d4af7bec0236ba96d5..00c0435fd4c9408704c001157a0f267679bad714 100644 (file)
@@ -121,7 +121,7 @@ static void run_test(void) {
 
         assert_se(sd_journal_open_directory(&j, t, SD_JOURNAL_ASSUME_IMMUTABLE) >= 0);
 
-        assert_se(sd_journal_add_match(j, "MAGIC=quux", 0) >= 0);
+        assert_se(sd_journal_add_match(j, "MAGIC=quux", SIZE_MAX) >= 0);
         SD_JOURNAL_FOREACH_BACKWARDS(j) {
                 _cleanup_free_ char *c;
 
@@ -147,7 +147,7 @@ static void run_test(void) {
         verify_contents(j, 1);
 
         printf("NEXT TEST\n");
-        assert_se(sd_journal_add_match(j, "MAGIC=quux", 0) >= 0);
+        assert_se(sd_journal_add_match(j, "MAGIC=quux", SIZE_MAX) >= 0);
 
         assert_se(z = journal_make_match_string(j));
         printf("resulting match expression is: %s\n", z);
@@ -157,10 +157,10 @@ static void run_test(void) {
 
         printf("NEXT TEST\n");
         sd_journal_flush_matches(j);
-        assert_se(sd_journal_add_match(j, "MAGIC=waldo", 0) >= 0);
-        assert_se(sd_journal_add_match(j, "NUMBER=10", 0) >= 0);
-        assert_se(sd_journal_add_match(j, "NUMBER=11", 0) >= 0);
-        assert_se(sd_journal_add_match(j, "NUMBER=12", 0) >= 0);
+        assert_se(sd_journal_add_match(j, "MAGIC=waldo", SIZE_MAX) >= 0);
+        assert_se(sd_journal_add_match(j, "NUMBER=10", SIZE_MAX) >= 0);
+        assert_se(sd_journal_add_match(j, "NUMBER=11", SIZE_MAX) >= 0);
+        assert_se(sd_journal_add_match(j, "NUMBER=12", SIZE_MAX) >= 0);
 
         assert_se(z = journal_make_match_string(j));
         printf("resulting match expression is: %s\n", z);
index fb3150f1310ef4c22100e29e896e735248681db8..cf19c48213965369be92365adf6261617dd255cf 100644 (file)
@@ -1,5 +1,6 @@
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 
+/* Make sure the net/if.h header is included before any linux/ one */
 #include <net/if.h>
 #include <netinet/ether.h>
 #include <netinet/in.h>
index 7b9f54c976bf6caf4b73477c48e912f176049c58..50e2459a06365df607e7dee8bad504f92142a8e6 100644 (file)
@@ -1,11 +1,12 @@
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 
+/* Make sure the net/if.h header is included before any linux/ one */
+#include <net/if.h>
 #include <ctype.h>
 #include <dirent.h>
 #include <errno.h>
 #include <fcntl.h>
 #include <linux/sockios.h>
-#include <net/if.h>
 #include <stdbool.h>
 #include <stddef.h>
 #include <stdio.h>
index 5d27491af3a5fbec9dd0265e93912a8f1427a829..eb19577e79bcc4ed947a4de21369794ac69865a0 100644 (file)
@@ -2088,10 +2088,10 @@ static int method_do_shutdown_or_sleep(
                                                 "Both reboot via kexec and soft reboot selected, which is not supported");
 
                 if (action != HANDLE_REBOOT) {
-                        if (flags & SD_LOGIND_REBOOT_VIA_KEXEC)
+                        if (FLAGS_SET(flags, SD_LOGIND_REBOOT_VIA_KEXEC))
                                 return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS,
                                                         "Reboot via kexec option is only applicable with reboot operations");
-                        if ((flags & SD_LOGIND_SOFT_REBOOT) || (flags & SD_LOGIND_SOFT_REBOOT_IF_NEXTROOT_SET_UP))
+                        if (flags & (SD_LOGIND_SOFT_REBOOT|SD_LOGIND_SOFT_REBOOT_IF_NEXTROOT_SET_UP))
                                 return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS,
                                                         "Soft reboot option is only applicable with reboot operations");
                 }
@@ -2110,10 +2110,10 @@ static int method_do_shutdown_or_sleep(
 
         const HandleActionData *a = NULL;
 
-        if ((flags & SD_LOGIND_SOFT_REBOOT) ||
-            ((flags & SD_LOGIND_SOFT_REBOOT_IF_NEXTROOT_SET_UP) && path_is_os_tree("/run/nextroot") > 0))
+        if (FLAGS_SET(flags, SD_LOGIND_SOFT_REBOOT) ||
+            (FLAGS_SET(flags, SD_LOGIND_SOFT_REBOOT_IF_NEXTROOT_SET_UP) && path_is_os_tree("/run/nextroot") > 0))
                 a = handle_action_lookup(HANDLE_SOFT_REBOOT);
-        else if ((flags & SD_LOGIND_REBOOT_VIA_KEXEC) && kexec_loaded())
+        else if (FLAGS_SET(flags, SD_LOGIND_REBOOT_VIA_KEXEC) && kexec_loaded())
                 a = handle_action_lookup(HANDLE_KEXEC);
 
         if (action == HANDLE_SLEEP) {
@@ -2154,7 +2154,7 @@ static int method_do_shutdown_or_sleep(
 
                         case SLEEP_RESUME_NOT_SUPPORTED:
                                 return sd_bus_error_set(error, BUS_ERROR_SLEEP_VERB_NOT_SUPPORTED,
-                                                        "Not running on EFI and resume= is not set. No available method to resume from hibernation");
+                                                        "Not running on EFI and resume= is not set, or noresume is set. No available method to resume from hibernation");
 
                         case SLEEP_NOT_ENOUGH_SWAP_SPACE:
                                 return sd_bus_error_set(error, BUS_ERROR_SLEEP_VERB_NOT_SUPPORTED,
@@ -4241,6 +4241,7 @@ int manager_start_scope(
                 char **ret_job) {
 
         _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL;
+        _cleanup_(sd_bus_error_free) sd_bus_error e = SD_BUS_ERROR_NULL;
         int r;
 
         assert(manager);
@@ -4330,12 +4331,12 @@ int manager_start_scope(
         if (r < 0)
                 return r;
 
-        r = sd_bus_call(manager->bus, m, 0, error, &reply);
+        r = sd_bus_call(manager->bus, m, 0, &e, &reply);
         if (r < 0) {
                 /* If this failed with a property we couldn't write, this is quite likely because the server
                  * doesn't support PIDFDs yet, let's try without. */
                 if (allow_pidfd &&
-                    sd_bus_error_has_names(error, SD_BUS_ERROR_UNKNOWN_PROPERTY, SD_BUS_ERROR_PROPERTY_READ_ONLY))
+                    sd_bus_error_has_names(&e, SD_BUS_ERROR_UNKNOWN_PROPERTY, SD_BUS_ERROR_PROPERTY_READ_ONLY))
                         return manager_start_scope(
                                         manager,
                                         scope,
@@ -4350,7 +4351,7 @@ int manager_start_scope(
                                         error,
                                         ret_job);
 
-                return r;
+                return sd_bus_error_move(error, &e);
         }
 
         return strdup_job(reply, ret_job);
index 4d3d0270effa718a3e78b4f247b830a610852fc8..ac4b8602c4f9b11816595a8148d02278ff268e97 100644 (file)
@@ -194,8 +194,11 @@ static int manager_enumerate_devices(Manager *m) {
 
         r = 0;
 
-        FOREACH_DEVICE(e, d)
+        FOREACH_DEVICE(e, d) {
+                if (device_is_processed(d) <= 0)
+                        continue;
                 RET_GATHER(r, manager_process_seat_device(m, d));
+        }
 
         return r;
 }
@@ -225,8 +228,11 @@ static int manager_enumerate_buttons(Manager *m) {
 
         r = 0;
 
-        FOREACH_DEVICE(e, d)
+        FOREACH_DEVICE(e, d) {
+                if (device_is_processed(d) <= 0)
+                        continue;
                 RET_GATHER(r, manager_process_button_device(m, d));
+        }
 
         return r;
 }
index 2f1decc8b775a43cfe55a2b6724911e6955e6b45..d6375a63a32594a2b0ef7252050fd84091e9a8b2 100644 (file)
@@ -5,7 +5,7 @@ executables += [
                 'name' : 'systemd-modules-load',
                 'conditions' : ['HAVE_KMOD'],
                 'sources' : files('modules-load.c'),
-                'dependencies' : libkmod,
+                'dependencies' : libkmod_cflags,
         },
 ]
 
index da7e3d890055550b370dfea691852d356a02457b..c1f818320ffb7048ec0c68dca3f53ceb49d65641 100644 (file)
@@ -23,14 +23,6 @@ static const char conf_file_dirs[] = CONF_PATHS_NULSTR("modules-load.d");
 
 STATIC_DESTRUCTOR_REGISTER(arg_proc_cmdline_modules, strv_freep);
 
-static void systemd_kmod_log(void *data, int priority, const char *file, int line,
-                             const char *fn, const char *format, va_list args) {
-
-        DISABLE_WARNING_FORMAT_NONLITERAL;
-        log_internalv(priority, 0, file, line, fn, format, args);
-        REENABLE_WARNING;
-}
-
 static int add_modules(const char *p) {
         _cleanup_strv_free_ char **k = NULL;
 
@@ -156,7 +148,7 @@ static int parse_argv(int argc, char *argv[]) {
 }
 
 static int run(int argc, char *argv[]) {
-        _cleanup_(kmod_unrefp) struct kmod_ctx *ctx = NULL;
+        _cleanup_(sym_kmod_unrefp) struct kmod_ctx *ctx = NULL;
         int r, k;
 
         r = parse_argv(argc, argv);
@@ -171,12 +163,9 @@ static int run(int argc, char *argv[]) {
         if (r < 0)
                 log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m");
 
-        ctx = kmod_new(NULL, NULL);
-        if (!ctx)
-                return log_oom();
-
-        kmod_load_resources(ctx);
-        kmod_set_log_fn(ctx, systemd_kmod_log, NULL);
+        r = module_setup_context(&ctx);
+        if (r < 0)
+                return log_error_errno(r, "Failed to initialize libkmod context: %m");
 
         r = 0;
 
diff --git a/src/mountfsd/io.systemd.mount-file-system.policy b/src/mountfsd/io.systemd.mount-file-system.policy
new file mode 100644 (file)
index 0000000..6a151eb
--- /dev/null
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="UTF-8"?> <!--*-nxml-*-->
+<!DOCTYPE policyconfig PUBLIC "-//freedesktop//DTD PolicyKit Policy Configuration 1.0//EN"
+        "https://www.freedesktop.org/standards/PolicyKit/1/policyconfig.dtd">
+
+<!--
+  SPDX-License-Identifier: LGPL-2.1-or-later
+
+  This file is part of systemd.
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+-->
+
+<policyconfig>
+
+        <vendor>The systemd Project</vendor>
+        <vendor_url>https://systemd.io</vendor_url>
+
+        <!-- Allow mounting DDIs into the host user namespace -->
+        <action id="io.systemd.mount-file-system.mount-image">
+                <!-- This action is generally checked first: we'll first try to mount the image with
+                     signature checks on. If that fails, we'll retry with the untrusted action below. -->
+                <description gettext-domain="systemd">Allow mounting of file system image</description>
+                <message gettext-domain="systemd">Authentication is required for an application to mount a file system image.</message>
+                <defaults>
+                        <allow_any>auth_admin_keep</allow_any>
+                        <allow_inactive>auth_admin_keep</allow_inactive>
+                        <allow_active>yes</allow_active>
+                </defaults>
+        </action>
+
+        <action id="io.systemd.mount-file-system.mount-untrusted-image">
+                <!-- If the image cannot be mounted via the regular action because it is not signed by a
+                     recognized key, we'll try this action. -->
+                <description gettext-domain="systemd">Allow mounting of untrusted file system image</description>
+                <message gettext-domain="systemd">Authentication is required for an application to mount a cryptographically unsigned file system image or an image whose cryptographic signature is not recognized.</message>
+                <defaults>
+                        <allow_any>auth_admin</allow_any>
+                        <allow_inactive>auth_admin</allow_inactive>
+                        <allow_active>auth_admin</allow_active>
+                </defaults>
+
+                <annotate key="org.freedesktop.policykit.imply">io.systemd.mount-file-system.mount-image</annotate>
+        </action>
+
+        <!-- Allow mounting DDIs into a private user namespace -->
+        <action id="io.systemd.mount-file-system.mount-image-privately">
+                <description gettext-domain="systemd">Allow private mounting of trusted file system image</description>
+                <message gettext-domain="systemd">Authentication is required for an application to privately mount a file system image or an image whose cryptographic signature is recognized.</message>
+                <defaults>
+                        <allow_any>yes</allow_any>
+                        <allow_inactive>yes</allow_inactive>
+                        <allow_active>yes</allow_active>
+                </defaults>
+        </action>
+
+        <action id="io.systemd.mount-file-system.mount-untrusted-image-privately">
+                <description gettext-domain="systemd">Allow private mounting of untrusted file system image</description>
+                <message gettext-domain="systemd">Authentication is required for an application to privately mount a cryptographically unsigned file system image or an image whose cryptographic signature is not recognized.</message>
+                <defaults>
+                        <allow_any>auth_admin</allow_any>
+                        <allow_inactive>auth_admin</allow_inactive>
+                        <allow_active>auth_admin</allow_active>
+                </defaults>
+
+                <annotate key="org.freedesktop.policykit.imply">io.systemd.mount-file-system.mount-image-privately</annotate>
+        </action>
+</policyconfig>
diff --git a/src/mountfsd/meson.build b/src/mountfsd/meson.build
new file mode 100644 (file)
index 0000000..3689d2a
--- /dev/null
@@ -0,0 +1,28 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+systemd_mountwork_sources = files(
+        'mountwork.c',
+)
+
+systemd_mountfsd_sources = files(
+        'mountfsd.c',
+        'mountfsd-manager.c',
+)
+
+executables += [
+        libexec_template + {
+                'name' : 'systemd-mountfsd',
+                'conditions' : ['ENABLE_MOUNTFSD'],
+                'sources' : systemd_mountfsd_sources,
+        },
+        libexec_template + {
+                'name' : 'systemd-mountwork',
+                'conditions' : ['ENABLE_MOUNTFSD'],
+                'sources' : systemd_mountwork_sources,
+                'link_with' : common_libs,
+                'dependencies' : common_deps,
+        },
+]
+
+install_data('io.systemd.mount-file-system.policy',
+             install_dir : polkitpolicydir)
diff --git a/src/mountfsd/mountfsd-manager.c b/src/mountfsd/mountfsd-manager.c
new file mode 100644 (file)
index 0000000..b05c6e8
--- /dev/null
@@ -0,0 +1,277 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#include <sys/wait.h>
+
+#include "sd-daemon.h"
+
+#include "build-path.h"
+#include "common-signal.h"
+#include "env-util.h"
+#include "fd-util.h"
+#include "fs-util.h"
+#include "mkdir.h"
+#include "mountfsd-manager.h"
+#include "process-util.h"
+#include "set.h"
+#include "signal-util.h"
+#include "socket-util.h"
+#include "stdio-util.h"
+#include "umask-util.h"
+
+#define LISTEN_TIMEOUT_USEC (25 * USEC_PER_SEC)
+
+static int start_workers(Manager *m, bool explicit_request);
+
+static size_t manager_current_workers(Manager *m) {
+        assert(m);
+
+        return set_size(m->workers_fixed) + set_size(m->workers_dynamic);
+}
+
+static int on_worker_exit(sd_event_source *s, const siginfo_t *si, void *userdata) {
+        Manager *m = ASSERT_PTR(userdata);
+
+        assert(s);
+
+        assert_se(!set_remove(m->workers_dynamic, s) != !set_remove(m->workers_fixed, s));
+        sd_event_source_disable_unref(s);
+
+        if (si->si_code == CLD_EXITED) {
+                if (si->si_status == EXIT_SUCCESS)
+                        log_debug("Worker " PID_FMT " exited successfully.", si->si_pid);
+                else
+                        log_warning("Worker " PID_FMT " died with a failure exit status %i, ignoring.", si->si_pid, si->si_status);
+        } else if (si->si_code == CLD_KILLED)
+                log_warning("Worker " PID_FMT " was killed by signal %s, ignoring.", si->si_pid, signal_to_string(si->si_status));
+        else if (si->si_code == CLD_DUMPED)
+                log_warning("Worker " PID_FMT " dumped core by signal %s, ignoring.", si->si_pid, signal_to_string(si->si_status));
+        else
+                log_warning("Got unexpected exit code via SIGCHLD, ignoring.");
+
+        (void) start_workers(m, /* explicit_request= */ false); /* Fill up workers again if we fell below the low watermark */
+        return 0;
+}
+
+static int on_sigusr2(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) {
+        Manager *m = ASSERT_PTR(userdata);
+
+        (void) start_workers(m, /* explicit_request= */ true); /* Workers told us there's more work, let's add one more worker as long as we are below the high watermark */
+        return 0;
+}
+
+DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(
+                event_source_hash_ops,
+                sd_event_source,
+                (void (*)(const sd_event_source*, struct siphash*)) trivial_hash_func,
+                (int (*)(const sd_event_source*, const sd_event_source*)) trivial_compare_func,
+                sd_event_source_disable_unref);
+
+int manager_new(Manager **ret) {
+        _cleanup_(manager_freep) Manager *m = NULL;
+        int r;
+
+        m = new(Manager, 1);
+        if (!m)
+                return -ENOMEM;
+
+        *m = (Manager) {
+                .listen_fd = -EBADF,
+                .worker_ratelimit = {
+                        .interval = 5 * USEC_PER_SEC,
+                        .burst = 50,
+                },
+        };
+
+        r = sd_event_new(&m->event);
+        if (r < 0)
+                return r;
+
+        r = sd_event_set_signal_exit(m->event, true);
+        if (r < 0)
+                return r;
+
+        r = sd_event_add_signal(m->event, NULL, (SIGRTMIN+18)|SD_EVENT_SIGNAL_PROCMASK, sigrtmin18_handler, NULL);
+        if (r < 0)
+                return r;
+
+        r = sd_event_add_memory_pressure(m->event, NULL, NULL, NULL);
+        if (r < 0)
+                log_debug_errno(r, "Failed allocate memory pressure event source, ignoring: %m");
+
+        r = sd_event_set_watchdog(m->event, true);
+        if (r < 0)
+                log_debug_errno(r, "Failed to enable watchdog handling, ignoring: %m");
+
+        r = sd_event_add_signal(m->event, NULL, SIGUSR2|SD_EVENT_SIGNAL_PROCMASK, on_sigusr2, m);
+        if (r < 0)
+                return r;
+
+        *ret = TAKE_PTR(m);
+        return 0;
+}
+
+Manager* manager_free(Manager *m) {
+        if (!m)
+                return NULL;
+
+        set_free(m->workers_fixed);
+        set_free(m->workers_dynamic);
+
+        /* Note: we rely on PR_DEATHSIG to kill the workers for us */
+
+        sd_event_unref(m->event);
+
+        return mfree(m);
+}
+
+static int start_one_worker(Manager *m) {
+        _cleanup_(sd_event_source_disable_unrefp) sd_event_source *source = NULL;
+        bool fixed;
+        pid_t pid;
+        int r;
+
+        assert(m);
+
+        fixed = set_size(m->workers_fixed) < MOUNTFS_WORKERS_MIN;
+
+        r = safe_fork_full(
+                        "(sd-worker)",
+                        /* stdio_fds= */ NULL,
+                        &m->listen_fd, 1,
+                        FORK_RESET_SIGNALS|FORK_DEATHSIG_SIGTERM|FORK_REOPEN_LOG|FORK_LOG|FORK_CLOSE_ALL_FDS,
+                        &pid);
+        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) {
+                        r = fd_cloexec(3, false);
+                        if (r < 0) {
+                                log_error_errno(r, "Failed to turn off O_CLOEXEC for fd 3: %m");
+                                _exit(EXIT_FAILURE);
+                        }
+                } else {
+                        if (dup2(m->listen_fd, 3) < 0) { /* dup2() creates with O_CLOEXEC off */
+                                log_error_errno(errno, "Failed to move listen fd to 3: %m");
+                                _exit(EXIT_FAILURE);
+                        }
+
+                        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");
+                        _exit(EXIT_FAILURE);
+                }
+
+                if (setenv("LISTEN_FDS", "1", 1) < 0) {
+                        log_error_errno(errno, "Failed to set $LISTEN_FDS: %m");
+                        _exit(EXIT_FAILURE);
+                }
+
+                if (setenv("MOUNTFS_FIXED_WORKER", one_zero(fixed), 1) < 0) {
+                        log_error_errno(errno, "Failed to set $MOUNTFS_FIXED_WORKER: %m");
+                        _exit(EXIT_FAILURE);
+                }
+
+                r = setenv_systemd_log_level();
+                if (r < 0) {
+                        log_error_errno(r, "Failed to set $SYSTEMD_LOG_LEVEL: %m");
+                        _exit(EXIT_FAILURE);
+                }
+
+                r = invoke_callout_binary(SYSTEMD_MOUNTWORK_PATH, STRV_MAKE("systemd-mountwork", "xxxxxxxxxxxxxxxx")); /* With some extra space rename_process() can make use of */
+                log_error_errno(r, "Failed start worker process: %m");
+                _exit(EXIT_FAILURE);
+        }
+
+        r = sd_event_add_child(m->event, &source, pid, WEXITED, on_worker_exit, m);
+        if (r < 0)
+                return log_error_errno(r, "Failed to watch child " PID_FMT ": %m", pid);
+
+        r = set_ensure_put(
+                        fixed ? &m->workers_fixed : &m->workers_dynamic,
+                        &event_source_hash_ops,
+                        source);
+        if (r < 0)
+                return log_error_errno(r, "Failed to add child process to set: %m");
+
+        TAKE_PTR(source);
+
+        return 0;
+}
+
+static int start_workers(Manager *m, bool explicit_request) {
+        int r;
+
+        assert(m);
+
+        for (;;)  {
+                size_t n;
+
+                n = manager_current_workers(m);
+
+                log_debug("%zu workers running.", n);
+
+                if (n >= MOUNTFS_WORKERS_MIN && (!explicit_request || n >= MOUNTFS_WORKERS_MAX))
+                        break;
+
+                if (!ratelimit_below(&m->worker_ratelimit)) {
+                        /* If we keep starting workers too often, let's fail the whole daemon, something is wrong */
+                        sd_event_exit(m->event, EXIT_FAILURE);
+
+                        return log_error_errno(SYNTHETIC_ERRNO(EUCLEAN), "Worker threads requested too frequently, something is wrong.");
+                }
+
+                r = start_one_worker(m);
+                if (r < 0)
+                        return r;
+
+                explicit_request = false;
+        }
+
+        return 0;
+}
+
+int manager_startup(Manager *m) {
+        int n;
+
+        assert(m);
+        assert(m->listen_fd < 0);
+
+        n = sd_listen_fds(false);
+        if (n < 0)
+                return log_error_errno(n, "Failed to determine number of passed file descriptors: %m");
+        if (n > 1)
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Expected one listening fd, got %i.", n);
+        if (n == 1)
+                m->listen_fd = SD_LISTEN_FDS_START;
+        else {
+                static const union sockaddr_union sockaddr = {
+                        .un.sun_family = AF_UNIX,
+                        .un.sun_path = "/run/systemd/io.systemd.MountFileSystem",
+                };
+
+                m->listen_fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0);
+                if (m->listen_fd < 0)
+                        return log_error_errno(errno, "Failed to bind on socket: %m");
+
+                (void) sockaddr_un_unlink(&sockaddr.un);
+
+                WITH_UMASK(0000)
+                        if (bind(m->listen_fd, &sockaddr.sa, SOCKADDR_UN_LEN(sockaddr.un)) < 0)
+                                return log_error_errno(errno, "Failed to bind socket: %m");
+
+                if (listen(m->listen_fd, SOMAXCONN) < 0)
+                        return log_error_errno(errno, "Failed to listen on socket: %m");
+        }
+
+        /* Let's make sure every accept() call on this socket times out after 25s. This allows workers to be
+         * GC'ed on idle */
+        if (setsockopt(m->listen_fd, SOL_SOCKET, SO_RCVTIMEO, TIMEVAL_STORE(LISTEN_TIMEOUT_USEC), sizeof(struct timeval)) < 0)
+                return log_error_errno(errno, "Failed to se SO_RCVTIMEO: %m");
+
+        return start_workers(m, /* explicit_request= */ false);
+}
diff --git a/src/mountfsd/mountfsd-manager.h b/src/mountfsd/mountfsd-manager.h
new file mode 100644 (file)
index 0000000..6bfbddc
--- /dev/null
@@ -0,0 +1,30 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include "sd-bus.h"
+#include "sd-event.h"
+
+typedef struct Manager Manager;
+
+#include "hashmap.h"
+#include "ratelimit.h"
+
+#define MOUNTFS_WORKERS_MIN 3
+#define MOUNTFS_WORKERS_MAX 4096
+
+struct Manager {
+        sd_event *event;
+
+        Set *workers_fixed;    /* Workers 0…MOUNTFS_WORKERS_MIN */
+        Set *workers_dynamic;  /* Workers MOUNTFS_WORKERS_MIN+1…MOUNTFS_WORKERS_MAX */
+
+        int listen_fd;
+
+        RateLimit worker_ratelimit;
+};
+
+int manager_new(Manager **ret);
+Manager* manager_free(Manager *m);
+DEFINE_TRIVIAL_CLEANUP_FUNC(Manager*, manager_free);
+
+int manager_startup(Manager *m);
diff --git a/src/mountfsd/mountfsd.c b/src/mountfsd/mountfsd.c
new file mode 100644 (file)
index 0000000..6073bd5
--- /dev/null
@@ -0,0 +1,43 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include "daemon-util.h"
+#include "log.h"
+#include "main-func.h"
+#include "mountfsd-manager.h"
+#include "signal-util.h"
+
+static int run(int argc, char *argv[]) {
+        _unused_ _cleanup_(notify_on_cleanup) const char *notify_stop = NULL;
+        _cleanup_(manager_freep) Manager *m = NULL;
+        int r;
+
+        log_setup();
+
+        umask(0022);
+
+        if (argc != 1)
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "This program takes no arguments.");
+
+        assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGCHLD) >= 0);
+
+        r = manager_new(&m);
+        if (r < 0)
+                return log_error_errno(r, "Could not create manager: %m");
+
+        r = manager_startup(m);
+        if (r < 0)
+                return log_error_errno(r, "Failed to start up daemon: %m");
+
+        notify_stop = notify_start(NOTIFY_READY, NOTIFY_STOPPING);
+
+        r = sd_event_loop(m->event);
+        if (r < 0)
+                return log_error_errno(r, "Event loop failed: %m");
+
+        return 0;
+}
+
+DEFINE_MAIN_FUNCTION(run);
diff --git a/src/mountfsd/mountwork.c b/src/mountfsd/mountwork.c
new file mode 100644 (file)
index 0000000..1d218a6
--- /dev/null
@@ -0,0 +1,703 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "sd-daemon.h"
+
+#include "argv-util.h"
+#include "bus-polkit.h"
+#include "chase.h"
+#include "discover-image.h"
+#include "dissect-image.h"
+#include "env-util.h"
+#include "errno-util.h"
+#include "fd-util.h"
+#include "io-util.h"
+#include "main-func.h"
+#include "missing_loop.h"
+#include "namespace-util.h"
+#include "nsresource.h"
+#include "nulstr-util.h"
+#include "os-util.h"
+#include "process-util.h"
+#include "stat-util.h"
+#include "user-util.h"
+#include "varlink.h"
+#include "varlink-io.systemd.MountFileSystem.h"
+
+#define ITERATIONS_MAX 64U
+#define RUNTIME_MAX_USEC (5 * USEC_PER_MINUTE)
+#define PRESSURE_SLEEP_TIME_USEC (50 * USEC_PER_MSEC)
+#define LISTEN_IDLE_USEC (90 * USEC_PER_SEC)
+
+static const ImagePolicy image_policy_untrusted = {
+        .n_policies = 2,
+        .policies = {
+                { PARTITION_ROOT,     PARTITION_POLICY_SIGNED|PARTITION_POLICY_ABSENT },
+                { PARTITION_USR,      PARTITION_POLICY_SIGNED|PARTITION_POLICY_ABSENT },
+        },
+        .default_flags = PARTITION_POLICY_IGNORE,
+};
+
+static int json_dispatch_image_policy(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
+        _cleanup_(image_policy_freep) ImagePolicy *q = NULL;
+        ImagePolicy **p = ASSERT_PTR(userdata);
+        int r;
+
+        assert(p);
+
+        if (json_variant_is_null(variant)) {
+                *p = image_policy_free(*p);
+                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));
+
+        r = image_policy_from_string(json_variant_string(variant), &q);
+        if (r < 0)
+                return json_log(variant, flags, r, "JSON field '%s' is not a valid image policy.", strna(name));
+
+        image_policy_free(*p);
+        *p = TAKE_PTR(q);
+        return 0;
+}
+
+typedef struct MountImageParameters {
+        unsigned image_fd_idx;
+        unsigned userns_fd_idx;
+        int read_only;
+        int growfs;
+        char *password;
+        ImagePolicy *image_policy;
+} MountImageParameters;
+
+static void mount_image_parameters_done(MountImageParameters *p) {
+        assert(p);
+
+        p->password = erase_and_free(p->password);
+        p->image_policy = image_policy_free(p->image_policy);
+}
+
+static int validate_image_fd(int fd, MountImageParameters *p) {
+        int r, fl;
+
+        assert(fd >= 0);
+        assert(p);
+
+        r = fd_verify_regular(fd);
+        if (r < 0)
+                return r;
+
+        fl = fd_verify_safe_flags(fd);
+        if (fl < 0)
+                return log_debug_errno(fl, "Image file descriptor has unsafe flags set: %m");
+
+        switch (fl & O_ACCMODE) {
+
+        case O_RDONLY:
+                p->read_only = true;
+                break;
+
+        case O_RDWR:
+                break;
+
+        default:
+                return -EBADF;
+        }
+
+        return 0;
+}
+
+static int verify_trusted_image_fd_by_path(int fd) {
+        _cleanup_free_ char *p = NULL;
+        struct stat sta;
+        int r;
+
+        assert(fd >= 0);
+
+        r = secure_getenv_bool("SYSTEMD_MOUNTFSD_TRUSTED_DIRECTORIES");
+        if (r == -ENXIO)  {
+                if (!DEFAULT_MOUNTFSD_TRUSTED_DIRECTORIES) {
+                        log_debug("Trusted directory mechanism disabled at compile time.");
+                        return false;
+                }
+        } else if (r < 0) {
+                log_debug_errno(r, "Failed to parse $SYSTEMD_MOUNTFSD_TRUSTED_DIRECTORIES environment variable, not trusting any image.");
+                return false;
+        } else if (!r) {
+                log_debug("Trusted directory mechanism disabled via $SYSTEMD_MOUNTFSD_TRUSTED_DIRECTORIES environment variable.");
+                return false;
+        }
+
+        r = fd_get_path(fd, &p);
+        if (r < 0)
+                return log_debug_errno(r, "Failed to get path of passed image file descriptor: %m");
+        if (fstat(fd, &sta) < 0)
+                return log_debug_errno(errno, "Failed to stat() passed image file descriptor: %m");
+
+        log_debug("Checking if image '%s' is in trusted directories.", p);
+
+        for (ImageClass c = 0; c < _IMAGE_CLASS_MAX; c++)
+                NULSTR_FOREACH(s, image_search_path[c]) {
+                        _cleanup_close_ int dir_fd = -EBADF, inode_fd = -EBADF;
+                        _cleanup_free_ char *q = NULL;
+                        struct stat stb;
+                        const char *e;
+
+                        r = chase(s, NULL, CHASE_SAFE, &q, &dir_fd);
+                        if (r == -ENOENT)
+                                continue;
+                        if (r < 0) {
+                                log_warning_errno(r, "Failed to resolve search path '%s', ignoring: %m", s);
+                                continue;
+                        }
+
+                        /* Check that the inode refers to a file immediately inside the image directory,
+                         * i.e. not the image directory itself, and nothing further down the tree */
+                        e = path_startswith(p, q);
+                        if (isempty(e))
+                                continue;
+
+                        e += strspn(e, "/");
+                        if (!filename_is_valid(e))
+                                continue;
+
+                        r = chaseat(dir_fd, e, CHASE_SAFE, NULL, &inode_fd);
+                        if (r < 0)
+                                return log_error_errno(r, "Couldn't verify that specified image '%s' is in search path '%s': %m", p, s);
+
+                        if (fstat(inode_fd, &stb) < 0)
+                                return log_error_errno(errno, "Failed to stat image file '%s/%s': %m", q, e);
+
+                        if (stat_inode_same(&sta, &stb)) {
+                                log_debug("Image '%s' is *in* trusted directories.", p);
+                                return true; /* Yay */
+                        }
+                }
+
+        log_debug("Image '%s' is *not* in trusted directories.", p);
+        return false;
+}
+
+static int determine_image_policy(
+                int image_fd,
+                bool trusted,
+                ImagePolicy *client_policy,
+                ImagePolicy **ret) {
+
+        _cleanup_(image_policy_freep) ImagePolicy *envvar_policy = NULL;
+        const ImagePolicy *default_policy;
+        const char *envvar, *e;
+        int r;
+
+        assert(image_fd >= 0);
+        assert(ret);
+
+        if (trusted) {
+                envvar = "SYSTEMD_MOUNTFSD_IMAGE_POLICY_TRUSTED";
+                default_policy = &image_policy_allow;
+        } else {
+                envvar = "SYSTEMD_MOUNTFSD_IMAGE_POLICY_UNTRUSTED";
+                default_policy = &image_policy_untrusted;
+        }
+
+        e = secure_getenv(envvar);
+        if (e) {
+                r = image_policy_from_string(e, &envvar_policy);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to parse image policy supplied via $%s: %m", envvar);
+
+                default_policy = envvar_policy;
+        }
+
+        return image_policy_intersect(default_policy, client_policy, ret);
+}
+
+static int validate_userns(Varlink *link, int *userns_fd) {
+        int r;
+
+        assert(link);
+        assert(userns_fd);
+
+        if (*userns_fd < 0)
+                return 0;
+
+        r = fd_verify_safe_flags(*userns_fd);
+        if (r < 0)
+                return log_debug_errno(r, "User namespace file descriptor has unsafe flags set: %m");
+
+        r = fd_is_ns(*userns_fd, CLONE_NEWUSER);
+        if (r < 0)
+                return r;
+        if (r == 0)
+                return varlink_error_invalid_parameter_name(link, "userNamespaceFileDescriptor");
+
+        /* Our own host user namespace? Then close the fd, and handle it as if none was specified. */
+        r = is_our_namespace(*userns_fd, NAMESPACE_USER);
+        if (r < 0)
+                return log_debug_errno(r, "Failed to determine if user namespace provided by client is our own.");
+        if (r > 0) {
+                log_debug("User namespace provided by client is our own.");
+                *userns_fd = safe_close(*userns_fd);
+        }
+
+        return 0;
+}
+
+static int vl_method_mount_image(
+                Varlink *link,
+                JsonVariant *parameters,
+                VarlinkMethodFlags flags,
+                void *userdata) {
+
+        static const JsonDispatch dispatch_table[] = {
+                { "imageFileDescriptor",         JSON_VARIANT_UNSIGNED, json_dispatch_uint,         offsetof(MountImageParameters, image_fd_idx),  JSON_MANDATORY },
+                { "userNamespaceFileDescriptor", JSON_VARIANT_UNSIGNED, json_dispatch_uint,         offsetof(MountImageParameters, userns_fd_idx), 0 },
+                { "readOnly",                    JSON_VARIANT_BOOLEAN,  json_dispatch_tristate,     offsetof(MountImageParameters, read_only),     0 },
+                { "growFileSystems",             JSON_VARIANT_BOOLEAN,  json_dispatch_tristate,     offsetof(MountImageParameters, growfs),        0 },
+                { "password",                    JSON_VARIANT_STRING,   json_dispatch_string,       offsetof(MountImageParameters, password),      0 },
+                { "imagePolicy",                 JSON_VARIANT_STRING,   json_dispatch_image_policy, offsetof(MountImageParameters, image_policy),  0 },
+                VARLINK_DISPATCH_POLKIT_FIELD,
+                {}
+        };
+
+        _cleanup_(verity_settings_done) VeritySettings verity = VERITY_SETTINGS_DEFAULT;
+        _cleanup_(mount_image_parameters_done) MountImageParameters p = {
+                .image_fd_idx = UINT_MAX,
+                .userns_fd_idx = UINT_MAX,
+                .read_only = -1,
+                .growfs = -1,
+        };
+        _cleanup_(dissected_image_unrefp) DissectedImage *di = NULL;
+        _cleanup_(loop_device_unrefp) LoopDevice *loop = NULL;
+        _cleanup_(json_variant_unrefp) JsonVariant *aj = NULL;
+        _cleanup_close_ int image_fd = -EBADF, userns_fd = -EBADF;
+        _cleanup_(image_policy_freep) ImagePolicy *use_policy = NULL;
+        Hashmap **polkit_registry = ASSERT_PTR(userdata);
+        _cleanup_free_ char *ps = NULL;
+        bool image_is_trusted = false;
+        uid_t peer_uid;
+        int r;
+
+        assert(link);
+        assert(parameters);
+
+        json_variant_sensitive(parameters); /* might contain passwords */
+
+        r = varlink_get_peer_uid(link, &peer_uid);
+        if (r < 0)
+                return log_debug_errno(r, "Failed to get client UID: %m");
+
+        r = varlink_dispatch(link, parameters, dispatch_table, &p);
+        if (r != 0)
+                return r;
+
+        if (p.image_fd_idx != UINT_MAX) {
+                image_fd = varlink_peek_dup_fd(link, p.image_fd_idx);
+                if (image_fd < 0)
+                        return log_debug_errno(image_fd, "Failed to peek image fd from client: %m");
+        }
+
+        if (p.userns_fd_idx != UINT_MAX) {
+                userns_fd = varlink_peek_dup_fd(link, p.userns_fd_idx);
+                if (userns_fd < 0)
+                        return log_debug_errno(userns_fd, "Failed to peek user namespace fd from client: %m");
+        }
+
+        r = validate_image_fd(image_fd, &p);
+        if (r < 0)
+                return r;
+
+        r = validate_userns(link, &userns_fd);
+        if (r != 0)
+                return r;
+
+        r = verify_trusted_image_fd_by_path(image_fd);
+        if (r < 0)
+                return r;
+        image_is_trusted = r;
+
+        const char *polkit_details[] = {
+                "read_only", one_zero(p.read_only > 0),
+                NULL,
+        };
+
+        const char *polkit_action, *polkit_untrusted_action;
+        PolkitFlags polkit_flags;
+        if (userns_fd < 0) {
+                /* Mount into the host user namespace */
+                polkit_action = "io.systemd.mount-file-system.mount-image";
+                polkit_untrusted_action = "io.systemd.mount-file-system.mount-untrusted-image";
+                polkit_flags = 0;
+        } else {
+                /* Mount into a private user namespace */
+                polkit_action = "io.systemd.mount-file-system.mount-image-privately";
+                polkit_untrusted_action = "io.systemd.mount-file-system.mount-untrusted-image-privately";
+
+                /* If polkit is not around, let's allow mounting authenticated images by default */
+                polkit_flags = POLKIT_DEFAULT_ALLOW;
+        }
+
+        /* Let's definitely acquire the regular action privilege, for mounting properly signed images */
+        r = varlink_verify_polkit_async_full(
+                        link,
+                        /* bus= */ NULL,
+                        polkit_action,
+                        polkit_details,
+                        /* good_user= */ UID_INVALID,
+                        polkit_flags,
+                        polkit_registry);
+        if (r <= 0)
+                return r;
+
+        /* Generate the common dissection directory here. We are not going to use it, but the clients might,
+         * and they likely are unprivileged, hence cannot create it themselves. Hence let's just create it
+         * here, if it is missing. */
+        r = get_common_dissect_directory(NULL);
+        if (r < 0)
+                return r;
+
+        r = loop_device_make(
+                        image_fd,
+                        p.read_only == 0 ? O_RDONLY : O_RDWR,
+                        0,
+                        UINT64_MAX,
+                        UINT32_MAX,
+                        LO_FLAGS_PARTSCAN,
+                        LOCK_EX,
+                        &loop);
+        if (r < 0)
+                return r;
+
+        DissectImageFlags dissect_flags =
+                (p.read_only == 0 ? DISSECT_IMAGE_READ_ONLY : 0) |
+                (p.growfs != 0 ? DISSECT_IMAGE_GROWFS : 0) |
+                DISSECT_IMAGE_DISCARD_ANY |
+                DISSECT_IMAGE_FSCK |
+                DISSECT_IMAGE_ADD_PARTITION_DEVICES |
+                DISSECT_IMAGE_PIN_PARTITION_DEVICES |
+                DISSECT_IMAGE_ALLOW_USERSPACE_VERITY;
+
+        /* Let's see if we have acquired the privilege to mount untrusted images already */
+        bool polkit_have_untrusted_action =
+                varlink_has_polkit_action(link, polkit_untrusted_action, polkit_details, polkit_registry);
+
+        for (;;) {
+                use_policy = image_policy_free(use_policy);
+                ps = mfree(ps);
+
+                /* We use the image policy for trusted images if either the path is below a trusted
+                 * directory, or if we have already acquired a PK authentication that tells us that untrusted
+                 * images are OK */
+                bool use_trusted_policy =
+                        image_is_trusted ||
+                        polkit_have_untrusted_action;
+
+                r = determine_image_policy(
+                                image_fd,
+                                use_trusted_policy,
+                                p.image_policy,
+                                &use_policy);
+                if (r < 0)
+                        return r;
+
+                r = image_policy_to_string(use_policy, /* simplify= */ true, &ps);
+                if (r < 0)
+                        return r;
+
+                log_debug("Using image policy: %s", ps);
+
+                r = dissect_loop_device(
+                                loop,
+                                &verity,
+                                /* mount_options= */ NULL,
+                                use_policy,
+                                dissect_flags,
+                                &di);
+                if (r == -ENOPKG)
+                        return varlink_error(link, "io.systemd.MountFileSystem.IncompatibleImage", NULL);
+                if (r == -ENOTUNIQ)
+                        return varlink_error(link, "io.systemd.MountFileSystem.MultipleRootPartitionsFound", NULL);
+                if (r == -ENXIO)
+                        return varlink_error(link, "io.systemd.MountFileSystem.RootPartitionNotFound", NULL);
+                if (r == -ERFKILL) {
+                        /* The image policy refused this, let's retry after trying to get PolicyKit */
+
+                        if (!polkit_have_untrusted_action) {
+                                log_debug("Denied by image policy. Trying a stronger polkit authentication before continuing.");
+                                r = varlink_verify_polkit_async_full(
+                                                link,
+                                                /* bus= */ NULL,
+                                                polkit_untrusted_action,
+                                                polkit_details,
+                                                /* good_user= */ UID_INVALID,
+                                                /* flags= */ 0,                   /* NB: the image cannot be authenticated, hence unless PK is around to allow this anyway, fail! */
+                                                polkit_registry);
+                                if (r <= 0 && !ERRNO_IS_NEG_PRIVILEGE(r))
+                                        return r;
+                                if (r > 0) {
+                                        /* Try again, now that we know the client has enough privileges. */
+                                        log_debug("Denied by image policy, retrying after polkit authentication.");
+                                        polkit_have_untrusted_action = true;
+                                        continue;
+                                }
+                        }
+
+                        return varlink_error(link, "io.systemd.MountFileSystem.DeniedByImagePolicy", NULL);
+                }
+                if (r < 0)
+                        return r;
+
+                /* Success */
+                break;
+        }
+
+        r = dissected_image_load_verity_sig_partition(
+                        di,
+                        loop->fd,
+                        &verity);
+        if (r < 0)
+                return r;
+
+        r = dissected_image_decrypt(
+                        di,
+                        p.password,
+                        &verity,
+                        dissect_flags);
+        if (r == -ENOKEY) /* new dm-verity userspace returns ENOKEY if the dm-verity signature key is not in
+                           * key chain. That's great. */
+                return varlink_error(link, "io.systemd.MountFileSystem.KeyNotFound", NULL);
+        if (r == -EBUSY) /* DM kernel subsystem is shit with returning useful errors hence we keep retrying
+                          * under the assumption that some errors are transitional. Which the errors might
+                          * not actually be. After all retries failed we return EBUSY. Let's turn that into a
+                          * generic Verity error. It's not very helpful, could mean anything, but at least it
+                          * gives client a clear idea that this has to do with Verity. */
+                return varlink_error(link, "io.systemd.MountFileSystem.VerityFailure", NULL);
+        if (r < 0)
+                return r;
+
+        r = dissected_image_mount(
+                        di,
+                        /* where= */ NULL,
+                        /* uid_shift= */ UID_INVALID,
+                        /* uid_range= */ UID_INVALID,
+                        userns_fd,
+                        dissect_flags);
+        if (r < 0)
+                return r;
+
+        for (PartitionDesignator d = 0; d < _PARTITION_DESIGNATOR_MAX; d++) {
+                _cleanup_(json_variant_unrefp) JsonVariant *pj = NULL;
+                DissectedPartition *pp = di->partitions + d;
+                int fd_idx;
+
+                if (!pp->found)
+                        continue;
+
+                if (pp->fsmount_fd < 0)
+                        continue;
+
+                if (userns_fd >= 0) {
+                        r = nsresource_add_mount(userns_fd, pp->fsmount_fd);
+                        if (r < 0)
+                                return r;
+                }
+
+                fd_idx = varlink_push_fd(link, pp->fsmount_fd);
+                if (fd_idx < 0)
+                        return fd_idx;
+
+                TAKE_FD(pp->fsmount_fd);
+
+                r = json_build(&pj,
+                               JSON_BUILD_OBJECT(
+                                               JSON_BUILD_PAIR("designator", JSON_BUILD_STRING(partition_designator_to_string(d))),
+                                               JSON_BUILD_PAIR("writable", JSON_BUILD_BOOLEAN(pp->rw)),
+                                               JSON_BUILD_PAIR("growFileSystem", JSON_BUILD_BOOLEAN(pp->growfs)),
+                                               JSON_BUILD_PAIR_CONDITION(pp->partno > 0, "partitionNumber", JSON_BUILD_INTEGER(pp->partno)),
+                                               JSON_BUILD_PAIR_CONDITION(pp->architecture > 0, "architecture", JSON_BUILD_STRING(architecture_to_string(pp->architecture))),
+                                               JSON_BUILD_PAIR_CONDITION(!sd_id128_is_null(pp->uuid), "partitionUuid", JSON_BUILD_UUID(pp->uuid)),
+                                               JSON_BUILD_PAIR("fileSystemType", JSON_BUILD_STRING(dissected_partition_fstype(pp))),
+                                               JSON_BUILD_PAIR_CONDITION(pp->label, "partitionLabel", JSON_BUILD_STRING(pp->label)),
+                                               JSON_BUILD_PAIR("size", JSON_BUILD_INTEGER(pp->size)),
+                                               JSON_BUILD_PAIR("offset", JSON_BUILD_INTEGER(pp->offset)),
+                                               JSON_BUILD_PAIR("mountFileDescriptor", JSON_BUILD_INTEGER(fd_idx))));
+                if (r < 0)
+                        return r;
+
+                r = json_variant_append_array(&aj, pj);
+                if (r < 0)
+                        return r;
+        }
+
+        loop_device_relinquish(loop);
+
+        r = varlink_replyb(link, JSON_BUILD_OBJECT(
+                                           JSON_BUILD_PAIR("partitions", JSON_BUILD_VARIANT(aj)),
+                                           JSON_BUILD_PAIR("imagePolicy", JSON_BUILD_STRING(ps)),
+                                           JSON_BUILD_PAIR("imageSize", JSON_BUILD_INTEGER(di->image_size)),
+                                           JSON_BUILD_PAIR("sectorSize", JSON_BUILD_INTEGER(di->sector_size)),
+                                           JSON_BUILD_PAIR_CONDITION(!sd_id128_is_null(di->image_uuid), "imageUuid", JSON_BUILD_UUID(di->image_uuid))));
+        if (r < 0)
+                return r;
+
+        return r;
+}
+
+static int process_connection(VarlinkServer *server, int _fd) {
+        _cleanup_close_ int fd = TAKE_FD(_fd); /* always take possession */
+        _cleanup_(varlink_close_unrefp) Varlink *vl = NULL;
+        _cleanup_(sd_event_unrefp) sd_event *event = NULL;
+        int r;
+
+        r = sd_event_new(&event);
+        if (r < 0)
+                return r;
+
+        r = varlink_server_attach_event(server, event, 0);
+        if (r < 0)
+                return log_error_errno(r, "Failed to attach Varlink server to event loop: %m");
+
+        r = varlink_server_add_connection(server, fd, &vl);
+        if (r < 0)
+                return log_error_errno(r, "Failed to add connection: %m");
+
+        TAKE_FD(fd);
+        vl = varlink_ref(vl);
+
+        r = varlink_set_allow_fd_passing_input(vl, true);
+        if (r < 0)
+                return log_error_errno(r, "Failed to enable fd passing for read: %m");
+
+        r = varlink_set_allow_fd_passing_output(vl, true);
+        if (r < 0)
+                return log_error_errno(r, "Failed to enable fd passing for write: %m");
+
+        r = sd_event_loop(event);
+        if (r < 0)
+                return log_error_errno(r, "Failed to run event loop: %m");
+
+        r = varlink_server_detach_event(server);
+        if (r < 0)
+                return log_error_errno(r, "Failed to detach Varlink server from event loop: %m");
+
+        return 0;
+}
+
+static int run(int argc, char *argv[]) {
+        usec_t start_time, listen_idle_usec, last_busy_usec = USEC_INFINITY;
+        _cleanup_(varlink_server_unrefp) VarlinkServer *server = NULL;
+        _cleanup_(hashmap_freep) Hashmap *polkit_registry = NULL;
+        _cleanup_(pidref_done) PidRef parent = PIDREF_NULL;
+        unsigned n_iterations = 0;
+        int m, listen_fd, r;
+
+        log_setup();
+
+        m = sd_listen_fds(false);
+        if (m < 0)
+                return log_error_errno(m, "Failed to determine number of listening fds: %m");
+        if (m == 0)
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "No socket to listen on received.");
+        if (m > 1)
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Worker can only listen on a single socket at a time.");
+
+        listen_fd = SD_LISTEN_FDS_START;
+
+        r = fd_nonblock(listen_fd, false);
+        if (r < 0)
+                return log_error_errno(r, "Failed to turn off non-blocking mode for listening socket: %m");
+
+        r = varlink_server_new(&server, VARLINK_SERVER_INHERIT_USERDATA);
+        if (r < 0)
+                return log_error_errno(r, "Failed to allocate server: %m");
+
+        r = varlink_server_add_interface(server, &vl_interface_io_systemd_MountFileSystem);
+        if (r < 0)
+                return log_error_errno(r, "Failed to add MountFileSystem interface to varlink server: %m");
+
+        r = varlink_server_bind_method_many(
+                        server,
+                        "io.systemd.MountFileSystem.MountImage", vl_method_mount_image);
+        if (r < 0)
+                return log_error_errno(r, "Failed to bind methods: %m");
+
+        varlink_server_set_userdata(server, &polkit_registry);
+
+        r = varlink_server_set_exit_on_idle(server, true);
+        if (r < 0)
+                return log_error_errno(r, "Failed to enable exit-on-idle mode: %m");
+
+        r = getenv_bool("MOUNTFS_FIXED_WORKER");
+        if (r < 0)
+                return log_error_errno(r, "Failed to parse MOUNTFSD_FIXED_WORKER: %m");
+        listen_idle_usec = r ? USEC_INFINITY : LISTEN_IDLE_USEC;
+
+        r = pidref_set_parent(&parent);
+        if (r < 0)
+                return log_error_errno(r, "Failed to acquire pidfd of parent process: %m");
+
+        start_time = now(CLOCK_MONOTONIC);
+
+        for (;;) {
+                _cleanup_close_ int fd = -EBADF;
+                usec_t n;
+
+                /* Exit the worker in regular intervals, to flush out all memory use */
+                if (n_iterations++ > ITERATIONS_MAX) {
+                        log_debug("Exiting worker, processed %u iterations, that's enough.", n_iterations);
+                        break;
+                }
+
+                n = now(CLOCK_MONOTONIC);
+                if (n >= usec_add(start_time, RUNTIME_MAX_USEC)) {
+                        log_debug("Exiting worker, ran for %s, that's enough.",
+                                  FORMAT_TIMESPAN(usec_sub_unsigned(n, start_time), 0));
+                        break;
+                }
+
+                if (last_busy_usec == USEC_INFINITY)
+                        last_busy_usec = n;
+                else if (listen_idle_usec != USEC_INFINITY && n >= usec_add(last_busy_usec, listen_idle_usec)) {
+                        log_debug("Exiting worker, been idle for %s.",
+                                  FORMAT_TIMESPAN(usec_sub_unsigned(n, last_busy_usec), 0));
+                        break;
+                }
+
+                (void) rename_process("systemd-mountwork: waiting...");
+                fd = RET_NERRNO(accept4(listen_fd, NULL, NULL, SOCK_NONBLOCK|SOCK_CLOEXEC));
+                (void) rename_process("systemd-mountwork: processing...");
+
+                if (fd == -EAGAIN)
+                        continue; /* The listening socket has SO_RECVTIMEO set, hence a timeout is expected
+                                   * after a while, let's check if it's time to exit though. */
+                if (fd == -EINTR)
+                        continue; /* Might be that somebody attached via strace, let's just continue in that
+                                   * case */
+                if (fd < 0)
+                        return log_error_errno(fd, "Failed to accept() from listening socket: %m");
+
+                if (now(CLOCK_MONOTONIC) <= usec_add(n, PRESSURE_SLEEP_TIME_USEC)) {
+                        /* We only slept a very short time? If so, let's see if there are more sockets
+                         * pending, and if so, let's ask our parent for more workers */
+
+                        r = fd_wait_for_event(listen_fd, POLLIN, 0);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to test for POLLIN on listening socket: %m");
+
+                        if (FLAGS_SET(r, POLLIN)) {
+                                r = pidref_kill(&parent, SIGUSR2);
+                                if (r == -ESRCH)
+                                        return log_error_errno(r, "Parent already died?");
+                                if (r < 0)
+                                        return log_error_errno(r, "Failed to send SIGUSR2 signal to parent. %m");
+                        }
+                }
+
+                (void) process_connection(server, TAKE_FD(fd));
+                last_busy_usec = USEC_INFINITY;
+        }
+
+        return 0;
+}
+
+DEFINE_MAIN_FUNCTION(run);
index 4a3ccd6a99239648e39123cda8c17752448b86b6..0911656e889c01057f165f229ca85dbc7c9e1175 100644 (file)
@@ -3,7 +3,6 @@
 #include <getopt.h>
 
 #include "build.h"
-#include "copy.h"
 #include "creds-util.h"
 #include "fd-util.h"
 #include "fs-util.h"
@@ -14,9 +13,8 @@
 #include "network-generator.h"
 #include "path-util.h"
 #include "proc-cmdline.h"
-#include "recurse-dir.h"
 
-#define NETWORKD_UNIT_DIRECTORY "/run/systemd/network"
+#define NETWORK_UNIT_DIRECTORY "/run/systemd/network/"
 
 static const char *arg_root = NULL;
 
@@ -48,7 +46,7 @@ static int network_save(Network *network, const char *dest_dir) {
 
         r = conservative_rename(temp_path, p);
         if (r < 0)
-                return r;
+                return log_error_errno(r, "Failed to rename '%s' to '%s': %m", temp_path, p);
 
         temp_path = mfree(temp_path);
         return 0;
@@ -79,7 +77,7 @@ static int netdev_save(NetDev *netdev, const char *dest_dir) {
 
         r = conservative_rename(temp_path, p);
         if (r < 0)
-                return r;
+                return log_error_errno(r, "Failed to rename '%s' to '%s': %m", temp_path, p);
 
         temp_path = mfree(temp_path);
         return 0;
@@ -113,7 +111,7 @@ static int link_save(Link *link, const char *dest_dir) {
 
         r = conservative_rename(temp_path, p);
         if (r < 0)
-                return r;
+                return log_error_errno(r, "Failed to rename '%s' to '%s': %m", temp_path, p);
 
         temp_path = mfree(temp_path);
         return 0;
@@ -125,11 +123,11 @@ static int context_save(Context *context) {
         Link *link;
         int r;
 
-        const char *p = prefix_roota(arg_root, NETWORKD_UNIT_DIRECTORY);
+        const char *p = prefix_roota(arg_root, NETWORK_UNIT_DIRECTORY);
 
         r = mkdir_p(p, 0755);
         if (r < 0)
-                return log_error_errno(r, "Failed to create directory " NETWORKD_UNIT_DIRECTORY ": %m");
+                return log_error_errno(r, "Failed to create directory " NETWORK_UNIT_DIRECTORY ": %m");
 
         HASHMAP_FOREACH(network, context->networks_by_name)
                 RET_GATHER(r, network_save(network, p));
@@ -143,76 +141,6 @@ 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"
@@ -304,7 +232,14 @@ static int run(int argc, char *argv[]) {
                 return log_warning_errno(r, "Failed to merge multiple command line options: %m");
 
         RET_GATHER(ret, context_save(&context));
-        RET_GATHER(ret, pick_up_credentials());
+
+        static const PickUpCredential table[] = {
+                { "network.conf.",    "/run/systemd/networkd.conf.d/", ".conf"    },
+                { "network.link.",    NETWORK_UNIT_DIRECTORY,          ".link"    },
+                { "network.netdev.",  NETWORK_UNIT_DIRECTORY,          ".netdev"  },
+                { "network.network.", NETWORK_UNIT_DIRECTORY,          ".network" },
+        };
+        RET_GATHER(ret, pick_up_credentials(table, ELEMENTSOF(table)));
 
         return ret;
 }
index 106e10a5752543c4e26b19ed13478343b3f23fb1..a5458e0a5f5400ccfa9c53c92897f6e125a93815 100644 (file)
@@ -373,13 +373,13 @@ static int network_set_dhcp_type(Context *context, const char *ifname, const cha
 
         t = dracut_dhcp_type_from_string(dhcp_type);
         if (t < 0)
-                return t;
+                return log_debug_errno(t, "Invalid DHCP type '%s'", dhcp_type);
 
         network = network_get(context, ifname);
         if (!network) {
                 r = network_new(context, ifname, &network);
                 if (r < 0)
-                        return r;
+                        return log_debug_errno(r, "Failed to create network for '%s': %m", ifname);
         }
 
         network->dhcp_type = t;
@@ -394,13 +394,14 @@ static int network_set_hostname(Context *context, const char *ifname, const char
 
         network = network_get(context, ifname);
         if (!network)
-                return -ENODEV;
+                return log_debug_errno(SYNTHETIC_ERRNO(ENODEV), "No network found for '%s'", ifname);
 
         return free_and_strdup(&network->hostname, hostname);
 }
 
 static int network_set_mtu(Context *context, const char *ifname, const char *mtu) {
         Network *network;
+        int r;
 
         assert(context);
         assert(ifname);
@@ -410,13 +411,18 @@ static int network_set_mtu(Context *context, const char *ifname, const char *mtu
 
         network = network_get(context, ifname);
         if (!network)
-                return -ENODEV;
+                return log_debug_errno(SYNTHETIC_ERRNO(ENODEV), "No network found for '%s'", ifname);
 
-        return parse_mtu(AF_UNSPEC, mtu, &network->mtu);
+        r = parse_mtu(AF_UNSPEC, mtu, &network->mtu);
+        if (r < 0)
+                return log_debug_errno(r, "Invalid MTU '%s' for '%s': %m", mtu, ifname);
+
+        return r;
 }
 
 static int network_set_mac_address(Context *context, const char *ifname, const char *mac) {
         Network *network;
+        int r;
 
         assert(context);
         assert(ifname);
@@ -424,9 +430,13 @@ static int network_set_mac_address(Context *context, const char *ifname, const c
 
         network = network_get(context, ifname);
         if (!network)
-                return -ENODEV;
+                return log_debug_errno(SYNTHETIC_ERRNO(ENODEV), "No network found for '%s'", ifname);
 
-        return parse_ether_addr(mac, &network->mac);
+        r = parse_ether_addr(mac, &network->mac);
+        if (r < 0)
+                return log_debug_errno(r, "Invalid MAC address '%s' for '%s'", mac, ifname);
+
+        return r;
 }
 
 static int network_set_address(Context *context, const char *ifname, int family, unsigned char prefixlen,
@@ -443,7 +453,7 @@ static int network_set_address(Context *context, const char *ifname, int family,
 
         network = network_get(context, ifname);
         if (!network)
-                return -ENODEV;
+                return log_debug_errno(SYNTHETIC_ERRNO(ENODEV), "No network found for '%s'", ifname);
 
         return address_new(network, family, prefixlen, addr, peer, NULL);
 }
@@ -465,7 +475,7 @@ static int network_set_route(Context *context, const char *ifname, int family, u
         if (!network) {
                 r = network_new(context, ifname, &network);
                 if (r < 0)
-                        return r;
+                        return log_debug_errno(r, "Failed to create network for '%s': %m", ifname);
         }
 
         return route_new(network, family, prefixlen, dest, gateway, NULL);
@@ -486,13 +496,13 @@ static int network_set_dns(Context *context, const char *ifname, int family, con
         else
                 r = in_addr_from_string(family, dns, &a);
         if (r < 0)
-                return r;
+                return log_debug_errno(r, "Invalid DNS address '%s' for '%s'", dns, ifname);
 
         network = network_get(context, ifname);
         if (!network) {
                 r = network_new(context, ifname, &network);
                 if (r < 0)
-                        return r;
+                        return log_debug_errno(r, "Failed to create network for '%s': %m", ifname);
         }
 
         return strv_extend(&network->dns, dns);
@@ -509,7 +519,7 @@ static int network_set_dhcp_use_dns(Context *context, const char *ifname, bool v
         if (!network) {
                 r = network_new(context, ifname, &network);
                 if (r < 0)
-                        return r;
+                        return log_debug_errno(r, "Failed to create network for '%s': %m", ifname);
         }
 
         network->dhcp_use_dns = value;
@@ -528,7 +538,7 @@ static int network_set_vlan(Context *context, const char *ifname, const char *va
         if (!network) {
                 r = network_new(context, ifname, &network);
                 if (r < 0)
-                        return r;
+                        return log_debug_errno(r, "Failed to create network for '%s': %m", ifname);
         }
 
         return free_and_strdup(&network->vlan, value);
@@ -545,7 +555,7 @@ static int network_set_bridge(Context *context, const char *ifname, const char *
         if (!network) {
                 r = network_new(context, ifname, &network);
                 if (r < 0)
-                        return r;
+                        return log_debug_errno(r, "Failed to create network for '%s': %m", ifname);
         }
 
         return free_and_strdup(&network->bridge, value);
@@ -562,7 +572,7 @@ static int network_set_bond(Context *context, const char *ifname, const char *va
         if (!network) {
                 r = network_new(context, ifname, &network);
                 if (r < 0)
-                        return r;
+                        return log_debug_errno(r, "Failed to create network for '%s': %m", ifname);
         }
 
         return free_and_strdup(&network->bond, value);
@@ -615,21 +625,21 @@ static int parse_ip_address_one(int family, const char **value, union in_addr_un
 
         if (family == AF_INET6) {
                 if (p[0] != '[')
-                        return -EINVAL;
+                        return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid IPv6 address '%s'", p);
 
                 q = strchr(p + 1, ']');
                 if (!q)
-                        return -EINVAL;
+                        return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid IPv6 address '%s'", p);
 
                 if (q[1] != ':')
-                        return -EINVAL;
+                        return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid IPv6 address '%s'", p);
 
                 buf = strndupa_safe(p + 1, q - p - 1);
                 p = q + 2;
         } else {
                 q = strchr(p, ':');
                 if (!q)
-                        return -EINVAL;
+                        log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid IPv4 address '%s'", p);
 
                 buf = strndupa_safe(p, q - p);
                 p = q + 1;
@@ -637,7 +647,7 @@ static int parse_ip_address_one(int family, const char **value, union in_addr_un
 
         r = in_addr_from_string(family, buf, ret);
         if (r < 0)
-                return r;
+                return log_debug_errno(r, "Invalid IP address '%s': %m", buf);
 
         *value = p;
         return 1;
@@ -657,7 +667,7 @@ static int parse_netmask_or_prefixlen(int family, const char **value, unsigned c
         if (r > 0) {
                 if (family == AF_INET6)
                         /* TODO: Not supported yet. */
-                        return -EINVAL;
+                        return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "IPv6 prefix length is not supported yet");
 
                 *ret = in4_addr_netmask_to_prefixlen(&netmask.in);
         } else if (r == 0)
@@ -665,12 +675,12 @@ static int parse_netmask_or_prefixlen(int family, const char **value, unsigned c
         else {
                 p = strchr(*value, ':');
                 if (!p)
-                        return -EINVAL;
+                        return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid netmask or prefix length '%s'", *value);
 
                 q = strndupa_safe(*value, p - *value);
                 r = safe_atou8(q, ret);
                 if (r < 0)
-                        return r;
+                        return log_debug_errno(r, "Invalid netmask or prefix length '%s': %m", q);
 
                 *value = p + 1;
         }
@@ -693,10 +703,8 @@ static int parse_ip_dns_address_one(Context *context, const char *ifname, const
 
         if (p[0] == '[') {
                 q = strchr(p + 1, ']');
-                if (!q)
-                        return -EINVAL;
-                if (!IN_SET(q[1], ':', '\0'))
-                        return -EINVAL;
+                if (!q || !IN_SET(q[1], ':', '\0'))
+                        return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid IP DNS address '%s'", p);
 
                 buf = strndupa_safe(p + 1, q - p - 1);
                 p = q + 1;
@@ -749,12 +757,12 @@ static int parse_cmdline_ip_address(Context *context, int family, const char *va
         /* hostname */
         p = strchr(value, ':');
         if (!p)
-                return -EINVAL;
+                return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid IP address '%s'", value);
 
         if (p != value) {
                 hostname = strndupa_safe(value, p - value);
                 if (!hostname_is_valid(hostname, 0))
-                        return -EINVAL;
+                        return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid hostname '%s'", hostname);
         }
 
         value = p + 1;
@@ -762,7 +770,7 @@ static int parse_cmdline_ip_address(Context *context, int family, const char *va
         /* ifname */
         p = strchr(value, ':');
         if (!p)
-                return -EINVAL;
+                return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid IP address '%s'", value);
 
         ifname = strndupa_safe(value, p - value);
 
@@ -813,7 +821,7 @@ static int parse_cmdline_ip_address(Context *context, int family, const char *va
 
         /* refuse unexpected trailing strings */
         if (!isempty(value))
-                return -EINVAL;
+                return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid IP address '%s'", value);
 
         return 0;
 }
@@ -829,7 +837,7 @@ static int parse_cmdline_ip_interface(Context *context, const char *value) {
 
         p = strchr(value, ':');
         if (!p)
-                return -EINVAL;
+                return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid IP address '%s'", value);
 
         ifname = strndupa_safe(value, p - value);
 
@@ -858,7 +866,7 @@ static int parse_cmdline_ip(Context *context, const char *key, const char *value
         assert(key);
 
         if (proc_cmdline_value_missing(key, value))
-                return -EINVAL;
+                return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Missing value for '%s'", key);
 
         p = strchr(value, ':');
         if (!p)
@@ -887,15 +895,15 @@ static int parse_cmdline_rd_route(Context *context, const char *key, const char
         /* rd.route=<net>/<netmask>:<gateway>[:<interface>] */
 
         if (proc_cmdline_value_missing(key, value))
-                return -EINVAL;
+                return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Missing value for '%s'", key);
 
         if (value[0] == '[') {
                 p = strchr(value, ']');
                 if (!p)
-                        return -EINVAL;
+                        return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid IPv6 address '%s'", value);
 
                 if (p[1] != ':')
-                        return -EINVAL;
+                        return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid IPv6 address '%s'", value);
 
                 buf = strndupa_safe(value + 1, p - value - 1);
                 value = p + 2;
@@ -903,7 +911,7 @@ static int parse_cmdline_rd_route(Context *context, const char *key, const char
         } else {
                 p = strchr(value, ':');
                 if (!p)
-                        return -EINVAL;
+                        return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid IPv4 address '%s'", value);
 
                 buf = strndupa_safe(value, p - value);
                 value = p + 1;
@@ -912,7 +920,7 @@ static int parse_cmdline_rd_route(Context *context, const char *key, const char
 
         r = in_addr_prefix_from_string(buf, family, &addr, &prefixlen);
         if (r < 0)
-                return r;
+                return log_debug_errno(r, "Invalid IP address '%s': %m", buf);
 
         p = strchr(value, ':');
         if (!p)
@@ -930,7 +938,7 @@ static int parse_cmdline_nameserver(Context *context, const char *key, const cha
         assert(key);
 
         if (proc_cmdline_value_missing(key, value))
-                return -EINVAL;
+                return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Missing value for '%s'", key);
 
         return network_set_dns(context, "", AF_UNSPEC, value);
 }
@@ -946,7 +954,7 @@ static int parse_cmdline_rd_peerdns(Context *context, const char *key, const cha
 
         r = parse_boolean(value);
         if (r < 0)
-                return r;
+                return log_debug_errno(r, "Invalid boolean value '%s'", value);
 
         return network_set_dhcp_use_dns(context, "", r);
 }
@@ -960,11 +968,11 @@ static int parse_cmdline_vlan(Context *context, const char *key, const char *val
         assert(key);
 
         if (proc_cmdline_value_missing(key, value))
-                return -EINVAL;
+                return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Missing value for '%s'", key);
 
         p = strchr(value, ':');
         if (!p)
-                return -EINVAL;
+                return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid VLAN value '%s'", value);
 
         name = strndupa_safe(value, p - value);
 
@@ -972,7 +980,7 @@ static int parse_cmdline_vlan(Context *context, const char *key, const char *val
         if (!netdev) {
                 r = netdev_new(context, "vlan", name, &netdev);
                 if (r < 0)
-                        return r;
+                        return log_debug_errno(r, "Failed to create VLAN device for '%s': %m", name);
         }
 
         return network_set_vlan(context, p + 1, name);
@@ -987,11 +995,11 @@ static int parse_cmdline_bridge(Context *context, const char *key, const char *v
         assert(key);
 
         if (proc_cmdline_value_missing(key, value))
-                return -EINVAL;
+                return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Missing value for '%s'", key);
 
         p = strchr(value, ':');
         if (!p)
-                return -EINVAL;
+                return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid bridge value '%s'", value);
 
         name = strndupa_safe(value, p - value);
 
@@ -999,19 +1007,21 @@ static int parse_cmdline_bridge(Context *context, const char *key, const char *v
         if (!netdev) {
                 r = netdev_new(context, "bridge", name, &netdev);
                 if (r < 0)
-                        return r;
+                        return log_debug_errno(r, "Failed to create bridge device for '%s': %m", name);
         }
 
         p++;
         if (isempty(p))
-                return -EINVAL;
+                return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Missing slave interfaces for bridge '%s'", name);
 
         for (;;) {
                 _cleanup_free_ char *word = NULL;
 
                 r = extract_first_word(&p, &word, ",", 0);
-                if (r <= 0)
-                        return r;
+                if (r < 0)
+                        return log_debug_errno(r, "Failed to parse slave interfaces for bridge '%s'", name);
+                if (r == 0)
+                        return 0;
 
                 r = network_set_bridge(context, word, name);
                 if (r < 0)
@@ -1028,11 +1038,11 @@ static int parse_cmdline_bond(Context *context, const char *key, const char *val
         assert(key);
 
         if (proc_cmdline_value_missing(key, value))
-                return -EINVAL;
+                return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Missing value for '%s'", key);
 
         p = strchr(value, ':');
         if (!p)
-                return -EINVAL;
+                return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid bond value '%s'", value);
 
         name = strndupa_safe(value, p - value);
 
@@ -1040,7 +1050,7 @@ static int parse_cmdline_bond(Context *context, const char *key, const char *val
         if (!netdev) {
                 r = netdev_new(context, "bond", name, &netdev);
                 if (r < 0)
-                        return r;
+                        return log_debug_errno(r, "Failed to create bond device for '%s': %m", name);
         }
 
         value = p + 1;
@@ -1051,7 +1061,7 @@ static int parse_cmdline_bond(Context *context, const char *key, const char *val
                 slaves = strndupa_safe(value, p - value);
 
         if (isempty(slaves))
-                return -EINVAL;
+                return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Missing slave interfaces for bond '%s'", name);
 
         for (const char *q = slaves; ; ) {
                 _cleanup_free_ char *word = NULL;
@@ -1060,7 +1070,7 @@ static int parse_cmdline_bond(Context *context, const char *key, const char *val
                 if (r == 0)
                         break;
                 if (r < 0)
-                        return r;
+                        return log_debug_errno(r, "Failed to parse slave interfaces for bond '%s'", name);
 
                 r = network_set_bond(context, word, name);
                 if (r < 0)
@@ -1090,19 +1100,23 @@ static int parse_cmdline_ifname(Context *context, const char *key, const char *v
         /* ifname=<interface>:<MAC> */
 
         if (proc_cmdline_value_missing(key, value))
-                return -EINVAL;
+                return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Missing value for '%s'", key);
 
         p = strchr(value, ':');
         if (!p)
-                return -EINVAL;
+                return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid ifname value '%s'", value);
 
         name = strndupa_safe(value, p - value);
 
         r = parse_hw_addr(p + 1, &mac);
         if (r < 0)
-                return r;
+                return log_debug_errno(r, "Invalid MAC address '%s' for '%s'", p + 1, name);
 
-        return link_new(context, name, &mac, NULL);
+        r = link_new(context, name, &mac, NULL);
+        if (r < 0)
+                return log_debug_errno(r, "Failed to create link for '%s': %m", name);
+
+        return 0;
 }
 
 static int parse_cmdline_ifname_policy(Context *context, const char *key, const char *value) {
@@ -1117,7 +1131,7 @@ static int parse_cmdline_ifname_policy(Context *context, const char *key, const
         /* net.ifname_policy=policy1[,policy2,...][,<MAC>] */
 
         if (proc_cmdline_value_missing(key, value))
-                return -EINVAL;
+                return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Missing value for '%s'", key);
 
         for (const char *q = value; ; ) {
                 _cleanup_free_ char *word = NULL;
@@ -1127,19 +1141,19 @@ static int parse_cmdline_ifname_policy(Context *context, const char *key, const
                 if (r == 0)
                         break;
                 if (r < 0)
-                        return r;
+                        return log_debug_errno(r, "Failed to parse ifname policy '%s'", value);
 
                 p = name_policy_from_string(word);
                 if (p < 0) {
                         r = parse_hw_addr(word, &mac);
                         if (r < 0)
-                                return r;
+                                return log_debug_errno(r, "Invalid MAC address '%s'", word);
 
                         if (hw_addr_is_null(&mac))
-                                return -EINVAL;
+                                return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "MAC address is not set");
 
                         if (!isempty(q))
-                                return -EINVAL;
+                                return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Unexpected trailing string '%s' in ifname policy '%s'", q, value);
 
                         break;
                 }
@@ -1147,20 +1161,20 @@ static int parse_cmdline_ifname_policy(Context *context, const char *key, const
                 if (alternative_names_policy_from_string(word) >= 0) {
                         r = strv_extend(&alt_policies, word);
                         if (r < 0)
-                                return r;
+                                return log_oom_debug();
                 }
 
                 r = strv_consume(&policies, TAKE_PTR(word));
                 if (r < 0)
-                        return r;
+                        return log_oom_debug();
         }
 
         if (strv_isempty(policies))
-                return -EINVAL;
+                return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "No ifname policy specified");
 
         r = link_new(context, NULL, &mac, &link);
         if (r < 0)
-                return r;
+                return log_debug_errno(r, "Failed to create link: %m");
 
         link->policies = TAKE_PTR(policies);
         link->alt_policies = TAKE_PTR(alt_policies);
@@ -1220,12 +1234,12 @@ int context_merge_networks(Context *context) {
 
                 r = strv_extend_strv(&network->dns, all->dns, false);
                 if (r < 0)
-                        return r;
+                        return log_oom_debug();
 
                 LIST_FOREACH(routes, route, all->routes) {
                         r = route_new(network, route->family, route->prefixlen, &route->dest, &route->gateway, NULL);
                         if (r < 0)
-                                return r;
+                                return log_debug_errno(r, "Failed to copy route: %m");
                 }
         }
 
@@ -1392,7 +1406,7 @@ int network_format(Network *network, char **ret) {
 
         f = memstream_init(&m);
         if (!f)
-                return -ENOMEM;
+                return log_oom_debug();
 
         network_dump(network, f);
 
@@ -1408,7 +1422,7 @@ int netdev_format(NetDev *netdev, char **ret) {
 
         f = memstream_init(&m);
         if (!f)
-                return -ENOMEM;
+                return log_oom_debug();
 
         netdev_dump(netdev, f);
 
@@ -1424,7 +1438,7 @@ int link_format(Link *link, char **ret) {
 
         f = memstream_init(&m);
         if (!f)
-                return -ENOMEM;
+                return log_oom_debug();
 
         link_dump(link, f);
 
index 2708b3a5bf4aa39974fe0e72fea3970aa399d515..14810ddb75f0d182c85aa70e215068da726d9e03 100644 (file)
@@ -147,8 +147,7 @@ if get_option('link-networkd-shared')
         networkd_link_with = [libshared]
 else
         networkd_link_with = [libsystemd_static,
-                              libshared_static,
-                              libbasic_gcrypt]
+                              libshared_static]
 endif
 
 network_includes = [libsystemd_network_includes, include_directories(['.', 'netdev', 'tc'])]
index 3e394edadfd331e8e9a75e8db4cdd74eada2da9e..d426c0c5019f03e4118cadc05feb8e1bc4e2b518 100644 (file)
@@ -1,9 +1,10 @@
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 
+/* Make sure the net/if.h header is included before any linux/ one */
 #include <net/if.h>
-#include <netinet/in.h>
 #include <linux/if_arp.h>
 #include <linux/if_bridge.h>
+#include <netinet/in.h>
 
 #include "bridge.h"
 #include "netlink-util.h"
index 3bf41a8bf2979f988d922f03e8150b81a2659317..bddee5e98cde0bb560bf0438a016ba3829817165 100644 (file)
@@ -1,7 +1,8 @@
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 
-#include <linux/fou.h>
+/* Make sure the net/if.h header is included before any linux/ one */
 #include <net/if.h>
+#include <linux/fou.h>
 #include <netinet/in.h>
 #include <linux/ip.h>
 
index bc655ec7ffd9960a38ad8905421a385aaaefb8fb..22c2b00e1b700634719b02ba5bf2da8c8885b630 100644 (file)
@@ -1,8 +1,9 @@
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 
+/* Make sure the net/if.h header is included before any linux/ one */
 #include <net/if.h>
-#include <netinet/in.h>
 #include <linux/if_arp.h>
+#include <netinet/in.h>
 
 #include "alloc-util.h"
 #include "conf-parser.h"
index 05d5d010f65c7b74db53359d60a31c8ccdd07422..51ae64341db2c9c1d984b03143ad6fb0a9079873 100644 (file)
@@ -1,5 +1,6 @@
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 
+/* Make sure the net/if.h header is included before any linux/ one */
 #include <net/if.h>
 #include <netinet/in.h>
 #include <linux/if_arp.h>
index 85011e11de40a3da85a8c3c972b44042e84ef237..21933d3970a13e24170a22e4e2eea1d68f5db756 100644 (file)
@@ -1,5 +1,6 @@
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 
+/* Make sure the net/if.h header is included before any linux/ one */
 #include <net/if.h>
 #include <netinet/in.h>
 #include <linux/if_arp.h>
index 894cec44cbb574923d9d4a3cf64041ec4604b280..229520dfb2b667bd0a8710a74ce1e90bed9c10e1 100644 (file)
@@ -1,5 +1,6 @@
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 
+/* Make sure the net/if.h header is included before any linux/ one */
 #include <net/if.h>
 #include <netinet/in.h>
 #include <linux/if_arp.h>
index 97b4b928e768af45380a50d3f0660b73c56e40ec..f5be31ed9438e09ca06028609cd8a0cf72136029 100644 (file)
@@ -1,13 +1,14 @@
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 
+/* Make sure the net/if.h header is included before any linux/ one */
+#include <net/if.h>
 #include <errno.h>
 #include <fcntl.h>
-#include <net/if.h>
+#include <linux/if_tun.h>
 #include <netinet/if_ether.h>
 #include <sys/ioctl.h>
 #include <sys/stat.h>
 #include <sys/types.h>
-#include <linux/if_tun.h>
 
 #include "alloc-util.h"
 #include "daemon-util.h"
index e0f5b4ebb1a7c61a2046a981c4cbdb5d42a5ee07..78555286d1a4cc86a4765e8908b5ef8c080caeb6 100644 (file)
@@ -1,10 +1,11 @@
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 
-#include <errno.h>
+/* Make sure the net/if.h header is included before any linux/ one */
 #include <net/if.h>
-#include <netinet/in.h>
+#include <errno.h>
 #include <linux/if_arp.h>
 #include <linux/veth.h>
+#include <netinet/in.h>
 
 #include "netlink-util.h"
 #include "veth.h"
index adbba14a6e84555db343f7831e6ebc02c79c922c..60e49a5b8a9aec2ee03a345496f8f5080ccd6dfc 100644 (file)
@@ -1,7 +1,8 @@
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 
-#include <errno.h>
+/* Make sure the net/if.h header is included before any linux/ one */
 #include <net/if.h>
+#include <errno.h>
 #include <linux/if_arp.h>
 #include <linux/if_vlan.h>
 
index b75ec2bcc686263956698155687ca5f01448265c..24079a7203c89063a9c2c4bd3a501d2b57c40873 100644 (file)
@@ -1,8 +1,9 @@
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 
+/* Make sure the net/if.h header is included before any linux/ one */
 #include <net/if.h>
-#include <netinet/in.h>
 #include <linux/if_arp.h>
+#include <netinet/in.h>
 
 #include "vrf.h"
 
index f333abc6c04f6aba61f052af2cb6f49b90a97a77..37f65967a6bd5b352b8065939c10364feecd82bc 100644 (file)
@@ -1,5 +1,6 @@
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 
+/* Make sure the net/if.h header is included before any linux/ one */
 #include <net/if.h>
 #include <netinet/in.h>
 #include <linux/if_arp.h>
index 95c9c8c330093d5e20f666b49d9184c9f937b5b8..fed1be8d1104613d37408b18e8f186b0df44f0b4 100644 (file)
@@ -3,11 +3,12 @@
   Copyright © 2015-2017 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
 ***/
 
-#include <sys/ioctl.h>
+/* Make sure the net/if.h header is included before any linux/ one */
 #include <net/if.h>
-#include <netinet/in.h>
 #include <linux/if_arp.h>
 #include <linux/ipv6_route.h>
+#include <netinet/in.h>
+#include <sys/ioctl.h>
 
 #include "sd-resolve.h"
 
index 670f1c2fd7b6cd250569ad23adb78431c3500f51..216e9d49543ffe45791ac7cb2a8cd37466bc9fc8 100644 (file)
@@ -476,12 +476,16 @@ int verb_edit(int argc, char *argv[], void *userdata) {
 
 int verb_cat(int argc, char *argv[], void *userdata) {
         _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
+        char **args = strv_skip(argv, 1);
         int r, ret = 0;
 
         pager_open(arg_pager_flags);
 
+        if (strv_isempty(args))
+                return conf_files_cat(NULL, "systemd/networkd.conf", CAT_FORMAT_HAS_SECTIONS);
+
         bool first = true;
-        STRV_FOREACH(name, strv_skip(argv, 1)) {
+        STRV_FOREACH(name, args) {
                 _cleanup_strv_free_ char **dropins = NULL;
                 _cleanup_free_ char *path = NULL;
                 const char *link_config;
index fab9ff465bae3d655548bb91d7e23ff4b9457856..46f7d5036927ba0d7e0043c02e2f047e6ccc6db6 100644 (file)
@@ -1,9 +1,10 @@
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 
+/* Make sure the net/if.h header is included before any linux/ one */
+#include <net/if.h>
 #include <arpa/inet.h>
 #include <getopt.h>
 #include <linux/if_addrlabel.h>
-#include <net/if.h>
 #include <stdbool.h>
 #include <sys/stat.h>
 #include <sys/types.h>
@@ -2981,7 +2982,7 @@ static int help(void) {
                "  reconfigure DEVICES... Reconfigure interfaces\n"
                "  reload                 Reload .network and .netdev files\n"
                "  edit FILES|DEVICES...  Edit network configuration files\n"
-               "  cat FILES|DEVICES...   Show network configuration files\n"
+               "  cat [FILES|DEVICES...] Show network configuration files\n"
                "  mask FILES...          Mask network configuration files\n"
                "  unmask FILES...        Unmask network configuration files\n"
                "  persistent-storage BOOL\n"
@@ -3141,7 +3142,7 @@ static int networkctl_main(int argc, char *argv[]) {
                 { "reconfigure",        2,        VERB_ANY, VERB_ONLINE_ONLY,              verb_reconfigure        },
                 { "reload",             1,        1,        VERB_ONLINE_ONLY,              verb_reload             },
                 { "edit",               2,        VERB_ANY, 0,                             verb_edit               },
-                { "cat",                2,        VERB_ANY, 0,                             verb_cat                },
+                { "cat",                1,        VERB_ANY, 0,                             verb_cat                },
                 { "mask",               2,        VERB_ANY, 0,                             verb_mask               },
                 { "unmask",             2,        VERB_ANY, 0,                             verb_unmask             },
                 { "persistent-storage", 2,        2,        0,                             verb_persistent_storage },
index 9a5e2c217393ddfbf03534133ad8a3843298f8fd..816cdf713ac5d650376efd2207c8066365ad4320 100644 (file)
@@ -34,11 +34,125 @@ typedef enum AddressGenerationType {
         _ADDRESS_GENERATION_TYPE_INVALID = -EINVAL,
 } AddressGenerationType;
 
-typedef struct IPv6Token {
+struct IPv6Token {
+        unsigned n_ref;
         AddressGenerationType type;
         struct in6_addr address;
         sd_id128_t secret_key;
-} IPv6Token;
+};
+
+DEFINE_TRIVIAL_REF_UNREF_FUNC(IPv6Token, ipv6_token, mfree);
+DEFINE_TRIVIAL_CLEANUP_FUNC(IPv6Token*, ipv6_token_unref);
+
+static void ipv6_token_hash_func(const IPv6Token *p, struct siphash *state) {
+        siphash24_compress_typesafe(p->type, state);
+        siphash24_compress_typesafe(p->address, state);
+        id128_hash_func(&p->secret_key, state);
+}
+
+static int ipv6_token_compare_func(const IPv6Token *a, const IPv6Token *b) {
+        int r;
+
+        r = CMP(a->type, b->type);
+        if (r != 0)
+                return r;
+
+        r = memcmp(&a->address, &b->address, sizeof(struct in6_addr));
+        if (r != 0)
+                return r;
+
+        return id128_compare_func(&a->secret_key, &b->secret_key);
+}
+
+DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(
+                ipv6_token_hash_ops,
+                IPv6Token,
+                ipv6_token_hash_func,
+                ipv6_token_compare_func,
+                ipv6_token_unref);
+
+DEFINE_PRIVATE_HASH_OPS_FULL(
+                ipv6_token_by_addr_hash_ops,
+                struct in6_addr,
+                in6_addr_hash_func,
+                in6_addr_compare_func,
+                free,
+                IPv6Token,
+                ipv6_token_unref);
+
+static int ipv6_token_new(AddressGenerationType type, const struct in6_addr *addr, const sd_id128_t *secret_key, IPv6Token **ret) {
+        IPv6Token *p;
+
+        assert(type >= 0 && type < _ADDRESS_GENERATION_TYPE_MAX);
+        assert(addr);
+        assert(secret_key);
+        assert(ret);
+
+        p = new(IPv6Token, 1);
+        if (!p)
+                return -ENOMEM;
+
+        *p = (IPv6Token) {
+                .n_ref = 1,
+                .type = type,
+                .address = *addr,
+                .secret_key = *secret_key,
+        };
+
+        *ret = p;
+        return 0;
+}
+
+static int ipv6_token_add(Set **tokens, AddressGenerationType type, const struct in6_addr *addr, const sd_id128_t *secret_key) {
+        IPv6Token *p;
+        int r;
+
+        assert(tokens);
+
+        r = ipv6_token_new(type, addr, secret_key, &p);
+        if (r < 0)
+                return r;
+
+        return set_ensure_consume(tokens, &ipv6_token_hash_ops, p);
+}
+
+static int ipv6_token_put_by_addr(Hashmap **tokens_by_address, const struct in6_addr *addr, IPv6Token *token) {
+        _cleanup_free_ struct in6_addr *copy = NULL;
+        int r;
+
+        assert(tokens_by_address);
+        assert(addr);
+        assert(token);
+
+        copy = newdup(struct in6_addr, addr, 1);
+        if (!copy)
+                return -ENOMEM;
+
+        r = hashmap_ensure_put(tokens_by_address, &ipv6_token_by_addr_hash_ops, copy, token);
+        if (r == -EEXIST)
+                return 0;
+        if (r < 0)
+                return r;
+
+        TAKE_PTR(copy);
+        ipv6_token_ref(token);
+        return 1;
+}
+
+static int ipv6_token_type_put_by_addr(Hashmap **tokens_by_addr, const struct in6_addr *addr, AddressGenerationType type) {
+        _cleanup_(ipv6_token_unrefp) IPv6Token *token = NULL;
+        int r;
+
+        assert(tokens_by_addr);
+        assert(addr);
+
+        r = ipv6_token_new(type, &(struct in6_addr) {}, &SD_ID128_NULL, &token);
+        if (r < 0)
+                return r;
+
+        return ipv6_token_put_by_addr(tokens_by_addr, addr, token);
+}
+
 
 static int generate_eui64_address(const Link *link, const struct in6_addr *prefix, struct in6_addr *ret) {
         assert(link);
@@ -134,10 +248,12 @@ static int generate_stable_private_address(
                 const sd_id128_t *app_id,
                 const sd_id128_t *secret_key,
                 const struct in6_addr *prefix,
+                const struct in6_addr *previous,
                 struct in6_addr *ret) {
 
         sd_id128_t secret_machine_key;
         struct in6_addr addr;
+        bool found = false;
         uint8_t i;
         int r;
 
@@ -162,16 +278,29 @@ static int generate_stable_private_address(
         for (i = 0; i < DAD_CONFLICTS_IDGEN_RETRIES_RFC7217; i++) {
                 generate_stable_private_address_one(link, secret_key, prefix, i, &addr);
 
-                if (stable_private_address_is_valid(&addr))
-                        break;
+                if (!stable_private_address_is_valid(&addr))
+                        continue;
+
+                /* When 'previous' is non-NULL, then this is called after DAD in the kernel triggered.
+                 * Let's increment the counter and provide the next address. */
+                if (previous && !found) {
+                        found = in6_addr_equal(previous, &addr);
+                        continue;
+                }
+
+                break;
         }
-        if (i >= DAD_CONFLICTS_IDGEN_RETRIES_RFC7217)
+        if (i >= DAD_CONFLICTS_IDGEN_RETRIES_RFC7217) {
                 /* propagate recognizable errors. */
-                return log_link_debug_errno(link, SYNTHETIC_ERRNO(ENOANO),
+                if (previous && !found)
+                        return -EADDRNOTAVAIL;
+
+                return log_link_debug_errno(link, SYNTHETIC_ERRNO(EADDRINUSE),
                                             "Failed to generate stable private address.");
+        }
 
         *ret = addr;
-        return 0;
+        return 1;
 }
 
 static int generate_addresses(
@@ -180,10 +309,10 @@ static int generate_addresses(
                 const sd_id128_t *app_id,
                 const struct in6_addr *prefix,
                 uint8_t prefixlen,
-                Set **ret) {
+                Hashmap **ret) {
 
-        _cleanup_set_free_ Set *addresses = NULL;
-        struct in6_addr masked;
+        _cleanup_hashmap_free_ Hashmap *tokens_by_address = NULL;
+        struct in6_addr masked, addr;
         IPv6Token *j;
         int r;
 
@@ -197,8 +326,6 @@ static int generate_addresses(
         in6_addr_mask(&masked, prefixlen);
 
         SET_FOREACH(j, tokens) {
-                struct in6_addr addr, *copy;
-
                 switch (j->type) {
                 case ADDRESS_GENERATION_EUI64:
                         if (generate_eui64_address(link, &masked, &addr) < 0)
@@ -214,7 +341,7 @@ static int generate_addresses(
                         if (in6_addr_is_set(&j->address) && !in6_addr_equal(&j->address, &masked))
                                 continue;
 
-                        if (generate_stable_private_address(link, app_id, &j->secret_key, &masked, &addr) < 0)
+                        if (generate_stable_private_address(link, app_id, &j->secret_key, &masked, /* previous = */ NULL, &addr) < 0)
                                 continue;
 
                         break;
@@ -223,97 +350,77 @@ static int generate_addresses(
                         assert_not_reached();
                 }
 
-                copy = newdup(struct in6_addr, &addr, 1);
-                if (!copy)
-                        return -ENOMEM;
-
-                r = set_ensure_consume(&addresses, &in6_addr_hash_ops_free, copy);
+                r = ipv6_token_put_by_addr(&tokens_by_address, &addr, j);
                 if (r < 0)
                         return r;
         }
 
         /* fall back to EUI-64 if no token is provided */
-        if (set_isempty(addresses)) {
-                _cleanup_free_ struct in6_addr *addr = NULL;
+        if (hashmap_isempty(tokens_by_address)) {
+                AddressGenerationType type;
 
-                addr = new(struct in6_addr, 1);
-                if (!addr)
-                        return -ENOMEM;
-
-                if (IN_SET(link->iftype, ARPHRD_ETHER, ARPHRD_INFINIBAND))
-                        r = generate_eui64_address(link, &masked, addr);
-                else
-                        r = generate_stable_private_address(link, app_id, &SD_ID128_NULL, &masked, addr);
+                if (IN_SET(link->iftype, ARPHRD_ETHER, ARPHRD_INFINIBAND)) {
+                        type = ADDRESS_GENERATION_EUI64;
+                        r = generate_eui64_address(link, &masked, &addr);
+                } else {
+                        type = ADDRESS_GENERATION_PREFIXSTABLE;
+                        r = generate_stable_private_address(link, app_id, &SD_ID128_NULL, &masked, /* previous = */ NULL, &addr);
+                }
                 if (r < 0)
                         return r;
 
-                r = set_ensure_consume(&addresses, &in6_addr_hash_ops_free, TAKE_PTR(addr));
+                r = ipv6_token_type_put_by_addr(&tokens_by_address, &addr, type);
                 if (r < 0)
                         return r;
         }
 
-        *ret = TAKE_PTR(addresses);
+        *ret = TAKE_PTR(tokens_by_address);
         return 0;
 }
 
-int dhcp_pd_generate_addresses(Link *link, const struct in6_addr *prefix, Set **ret) {
+int dhcp_pd_generate_addresses(Link *link, const struct in6_addr *prefix, Hashmap **ret) {
         return generate_addresses(link, link->network->dhcp_pd_tokens, &DHCP_PD_APP_ID, prefix, 64, ret);
 }
 
-int ndisc_generate_addresses(Link *link, const struct in6_addr *prefix, uint8_t prefixlen, Set **ret) {
+int ndisc_generate_addresses(Link *link, const struct in6_addr *prefix, uint8_t prefixlen, Hashmap **ret) {
         return generate_addresses(link, link->network->ndisc_tokens, &NDISC_APP_ID, prefix, prefixlen, ret);
 }
 
-int radv_generate_addresses(Link *link, Set *tokens, const struct in6_addr *prefix, uint8_t prefixlen, Set **ret) {
+int radv_generate_addresses(Link *link, Set *tokens, const struct in6_addr *prefix, uint8_t prefixlen, Hashmap **ret) {
         return generate_addresses(link, tokens, &RADV_APP_ID, prefix, prefixlen, ret);
 }
 
-static void ipv6_token_hash_func(const IPv6Token *p, struct siphash *state) {
-        siphash24_compress_typesafe(p->type, state);
-        siphash24_compress_typesafe(p->address, state);
-        id128_hash_func(&p->secret_key, state);
-}
-
-static int ipv6_token_compare_func(const IPv6Token *a, const IPv6Token *b) {
-        int r;
-
-        r = CMP(a->type, b->type);
-        if (r != 0)
-                return r;
-
-        r = memcmp(&a->address, &b->address, sizeof(struct in6_addr));
-        if (r != 0)
-                return r;
-
-        return id128_compare_func(&a->secret_key, &b->secret_key);
-}
-
-DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(
-                ipv6_token_hash_ops,
-                IPv6Token,
-                ipv6_token_hash_func,
-                ipv6_token_compare_func,
-                free);
+int regenerate_address(Address *address, Link *link) {
+        struct in6_addr masked;
+        sd_id128_t app_id;
 
-static int ipv6_token_add(Set **tokens, AddressGenerationType type, const struct in6_addr *addr, const sd_id128_t *secret_key) {
-        IPv6Token *p;
+        assert(link);
+        assert(address);
+        assert(address->family == AF_INET6);
+        assert(!address->link && !address->network);
 
-        assert(tokens);
-        assert(type >= 0 && type < _ADDRESS_GENERATION_TYPE_MAX);
-        assert(addr);
-        assert(secret_key);
+        if (!address->token ||
+            address->token->type != ADDRESS_GENERATION_PREFIXSTABLE)
+                return 0;
 
-        p = new(IPv6Token, 1);
-        if (!p)
-                return -ENOMEM;
+        switch (address->source) {
+        case NETWORK_CONFIG_SOURCE_STATIC:
+                app_id = RADV_APP_ID;
+                break;
+        case NETWORK_CONFIG_SOURCE_DHCP_PD:
+                app_id = DHCP_PD_APP_ID;
+                break;
+        case NETWORK_CONFIG_SOURCE_NDISC:
+                app_id = NDISC_APP_ID;
+                break;
+        default:
+                assert_not_reached();
+        }
 
-        *p = (IPv6Token) {
-                 .type = type,
-                 .address = *addr,
-                 .secret_key = *secret_key,
-        };
+        masked = address->in_addr.in6;
+        in6_addr_mask(&masked, address->prefixlen);
 
-        return set_ensure_consume(tokens, &ipv6_token_hash_ops, p);
+        return generate_stable_private_address(link, &app_id, &address->token->secret_key, &masked, &address->in_addr.in6, &address->in_addr.in6);
 }
 
 int config_parse_address_generation_type(
index 901b2ec4bf32d8332983bbd30220331e04513f65..2c6091321aeb6bc727ef32ef5390ed983ff8f028 100644 (file)
@@ -2,13 +2,20 @@
 #pragma once
 
 #include "conf-parser.h"
+#include "hashmap.h"
 #include "in-addr-util.h"
-#include "set.h"
 
+typedef struct Address Address;
+typedef struct IPv6Token IPv6Token;
 typedef struct Link Link;
 
-int dhcp_pd_generate_addresses(Link *link, const struct in6_addr *prefix, Set **ret);
-int ndisc_generate_addresses(Link *link, const struct in6_addr *prefix, uint8_t prefixlen, Set **ret);
-int radv_generate_addresses(Link *link, Set *tokens, const struct in6_addr *prefix, uint8_t prefixlen, Set **ret);
+IPv6Token* ipv6_token_ref(IPv6Token *token);
+IPv6Token* ipv6_token_unref(IPv6Token *token);
+
+int dhcp_pd_generate_addresses(Link *link, const struct in6_addr *prefix, Hashmap **ret);
+int ndisc_generate_addresses(Link *link, const struct in6_addr *prefix, uint8_t prefixlen, Hashmap **ret);
+int radv_generate_addresses(Link *link, Set *tokens, const struct in6_addr *prefix, uint8_t prefixlen, Hashmap **ret);
+
+int regenerate_address(Address *address, Link *link);
 
 CONFIG_PARSER_PROTOTYPE(config_parse_address_generation_type);
index 745b959f7748cc592fffa694474f80b2d493dc04..e91ce3d2fa46f1c8e9b0778793e197a2c08b0d2c 100644 (file)
@@ -1,5 +1,6 @@
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 
+/* Make sure the net/if.h header is included before any linux/ one */
 #include <net/if.h>
 #include <linux/if_addrlabel.h>
 
index 542298c8c3019a7460f45faca518fe8a95f25c05..b4ac0bc41b6e3e576ba44de824488a6917432e4d 100644 (file)
 #include "netlink-util.h"
 #include "networkd-address-pool.h"
 #include "networkd-address.h"
+#include "networkd-dhcp-prefix-delegation.h"
 #include "networkd-dhcp-server.h"
 #include "networkd-ipv4acd.h"
 #include "networkd-manager.h"
+#include "networkd-ndisc.h"
 #include "networkd-netlabel.h"
 #include "networkd-network.h"
 #include "networkd-queue.h"
@@ -260,6 +262,7 @@ static Address* address_free(Address *address) {
         config_section_free(address->section);
         free(address->label);
         free(address->netlabel);
+        ipv6_token_unref(address->token);
         nft_set_context_clear(&address->nft_set_context);
         return mfree(address);
 }
@@ -608,6 +611,7 @@ int address_dup(const Address *src, Address **ret) {
         dest->section = NULL;
         dest->link = NULL;
         dest->label = NULL;
+        dest->token = ipv6_token_ref(src->token);
         dest->netlabel = NULL;
         dest->nft_set_context.sets = NULL;
         dest->nft_set_context.n_sets = 0;
@@ -800,8 +804,49 @@ static int address_update(Address *address) {
         return 0;
 }
 
-static int address_drop(Address *address) {
-        Link *link = ASSERT_PTR(ASSERT_PTR(address)->link);
+static int address_removed_maybe_kernel_dad(Link *link, Address *address) {
+        int r;
+
+        assert(link);
+        assert(address);
+
+        if (!IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED))
+                return 0;
+
+        if (address->family != AF_INET6)
+                return 0;
+
+        if (!FLAGS_SET(address->flags, IFA_F_TENTATIVE))
+                return 0;
+
+        log_link_info(link, "Address %s with tentative flag is removed, maybe a duplicated address is assigned on another node or link?",
+                      IN6_ADDR_TO_STRING(&address->in_addr.in6));
+
+        /* Reset the address state, as the object may be reused in the below. */
+        address->state = 0;
+
+        switch (address->source) {
+        case NETWORK_CONFIG_SOURCE_STATIC:
+                r = link_reconfigure_radv_address(address, link);
+                break;
+        case NETWORK_CONFIG_SOURCE_DHCP_PD:
+                r = dhcp_pd_reconfigure_address(address, link);
+                break;
+        case NETWORK_CONFIG_SOURCE_NDISC:
+                r = ndisc_reconfigure_address(address, link);
+                break;
+        default:
+                r = 0;
+        }
+        if (r < 0)
+                return log_link_warning_errno(link, r, "Failed to configure an alternative address: %m");
+
+        return 0;
+}
+
+static int address_drop(Address *in, bool removed_by_us) {
+        _cleanup_(address_unrefp) Address *address = address_ref(ASSERT_PTR(in));
+        Link *link = ASSERT_PTR(address->link);
         int r;
 
         r = address_set_masquerade(address, /* add = */ false);
@@ -821,6 +866,14 @@ static int address_drop(Address *address) {
 
         address_detach(address);
 
+        if (!removed_by_us) {
+                r = address_removed_maybe_kernel_dad(link, address);
+                if (r < 0) {
+                        link_enter_failed(link);
+                        return r;
+                }
+        }
+
         link_update_operstate(link, /* also_update_master = */ true);
         link_check_ready(link);
         return 0;
@@ -1135,7 +1188,7 @@ static int address_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, Remov
                         if (address_get_request(link, address, &req) >= 0)
                                 address_enter_removed(req->userdata);
 
-                        (void) address_drop(address);
+                        (void) address_drop(address, /* removed_by_us = */ true);
                 }
         }
 
@@ -1496,7 +1549,7 @@ static int address_acquire(Link *link, const Address *address, union in_addr_uni
         assert(ret);
 
         r = address_acquire_from_dhcp_server_leases_file(link, address, ret);
-        if (r != -ENOENT)
+        if (!IN_SET(r, -ENOENT, -ENXIO, -EINVAL))
                 return r;
 
         r = address_pool_acquire(link->manager, address->family, address->prefixlen, &a);
@@ -1845,9 +1898,11 @@ int manager_rtnl_process_address(sd_netlink *rtnl, sd_netlink_message *message,
 
         if (type == RTM_DELADDR) {
                 if (address) {
+                        bool removed_by_us = FLAGS_SET(address->state, NETWORK_CONFIG_STATE_REMOVING);
+
                         address_enter_removed(address);
                         log_address_debug(address, "Forgetting removed", link);
-                        (void) address_drop(address);
+                        (void) address_drop(address, removed_by_us);
                 } else
                         log_address_debug(tmp, "Kernel removed unknown", link);
 
@@ -1886,6 +1941,10 @@ int manager_rtnl_process_address(sd_netlink *rtnl, sd_netlink_message *message,
                 (void) nft_set_context_dup(&a->nft_set_context, &address->nft_set_context);
                 address->requested_as_null = a->requested_as_null;
                 address->callback = a->callback;
+
+                ipv6_token_ref(a->token);
+                ipv6_token_unref(address->token);
+                address->token = a->token;
         }
 
         /* Then, update miscellaneous info. */
index ac6179a9248f4770ae9d3e270497937842afb7dc..e09551ecda4c2bf4f120e16d224c3eb35d5fd4fc 100644 (file)
@@ -10,6 +10,7 @@
 #include "hash-funcs.h"
 #include "in-addr-util.h"
 #include "network-util.h"
+#include "networkd-address-generation.h"
 #include "networkd-link.h"
 #include "networkd-util.h"
 #include "time-util.h"
@@ -63,6 +64,9 @@ struct Address {
          * To control DAD for IPv6 dynamic addresses, set IFA_F_NODAD to flags. */
         AddressFamily duplicate_address_detection;
 
+        /* Used by address generator. */
+        IPv6Token *token;
+
         /* Called when address become ready */
         address_ready_callback_t callback;
 
index bd1a9745dce63fa73e137ef38a7c9d1df6cabb22..7ff4a188467185c13147d2aa3edcfc073b3feb24 100644 (file)
@@ -1,5 +1,6 @@
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 
+/* Make sure the net/if.h header is included before any linux/ one */
 #include <net/if.h>
 #include <linux/if_bridge.h>
 
index b8a1871a35016b1bbbc1518a021b84c61b8ff972..a5b003ad228d842fa3712db324dd15b40c036324 100644 (file)
@@ -1,5 +1,6 @@
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 
+/* Make sure the net/if.h header is included before any linux/ one */
 #include <net/if.h>
 #include <linux/can/netlink.h>
 
index 0f7015eb2545666235de7c6db12adbf5fd23b37a..a84b5ce18b5b682d5e497402dc0dc67dd9fcf3c1 100644 (file)
@@ -19,6 +19,7 @@ int manager_parse_config_file(Manager *m) {
                         "Network\0"
                         "DHCPv4\0"
                         "DHCPv6\0"
+                        "DHCPServer\0"
                         "DHCP\0",
                         config_item_perf_lookup, networkd_gperf_lookup,
                         CONFIG_PARSE_WARN,
index 2b442aa4c18bda44ae5bfdd2bab92248e912b457..3015e2b5c8b03d8bc715bd38a21a13afb90c7806 100644 (file)
@@ -632,6 +632,8 @@ int config_parse_dhcp_use_domains(
         return 0;
 }
 
+DEFINE_CONFIG_PARSE_ENUM(config_parse_default_dhcp_use_domains, dhcp_use_domains, DHCPUseDomains, "Failed to parse UseDomains=")
+
 int config_parse_dhcp_use_ntp(
                 const char* unit,
                 const char *filename,
index c3e6a6d51cd8d39eab018d3a0b840c0626947ff5..53e3e12f53c39487f912d8c7bc26374281d655f1 100644 (file)
@@ -99,6 +99,7 @@ CONFIG_PARSER_PROTOTYPE(config_parse_ndisc_route_metric);
 CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_send_hostname);
 CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_use_dns);
 CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_use_domains);
+CONFIG_PARSER_PROTOTYPE(config_parse_default_dhcp_use_domains);
 CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_use_ntp);
 CONFIG_PARSER_PROTOTYPE(config_parse_iaid);
 CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_or_ra_route_table);
index 61295d9ce6458b7c57a847d7b4482cd096232918..2e660b77631a5efacc36df7911425eb556c95534 100644 (file)
@@ -353,14 +353,50 @@ static void log_dhcp_pd_address(Link *link, const Address *address) {
                       FORMAT_LIFETIME(address->lifetime_preferred_usec));
 }
 
+static int dhcp_pd_request_address_one(Address *address, Link *link) {
+        Address *existing;
+
+        assert(address);
+        assert(link);
+
+        log_dhcp_pd_address(link, address);
+
+        if (address_get(link, address, &existing) < 0)
+                link->dhcp_pd_configured = false;
+        else
+                address_unmark(existing);
+
+        return link_request_address(link, address, &link->dhcp_pd_messages, dhcp_pd_address_handler, NULL);
+}
+
+int dhcp_pd_reconfigure_address(Address *address, Link *link) {
+        int r;
+
+        assert(address);
+        assert(address->source == NETWORK_CONFIG_SOURCE_DHCP_PD);
+        assert(link);
+
+        r = regenerate_address(address, link);
+        if (r <= 0)
+                return r;
+
+        r = dhcp_pd_request_address_one(address, link);
+        if (r < 0)
+                return r;
+
+        if (!link->dhcp_pd_configured)
+                link_set_state(link, LINK_STATE_CONFIGURING);
+
+        link_check_ready(link);
+        return 0;
+}
+
 static int dhcp_pd_request_address(
                 Link *link,
                 const struct in6_addr *prefix,
                 usec_t lifetime_preferred_usec,
                 usec_t lifetime_valid_usec) {
 
-        _cleanup_set_free_ Set *addresses = NULL;
-        struct in6_addr *a;
         int r;
 
         assert(link);
@@ -370,13 +406,15 @@ static int dhcp_pd_request_address(
         if (!link->network->dhcp_pd_assign)
                 return 0;
 
-        r = dhcp_pd_generate_addresses(link, prefix, &addresses);
+        _cleanup_hashmap_free_ Hashmap *tokens_by_address = NULL;
+        r = dhcp_pd_generate_addresses(link, prefix, &tokens_by_address);
         if (r < 0)
                 return log_link_warning_errno(link, r, "Failed to generate addresses for acquired DHCP delegated prefix: %m");
 
-        SET_FOREACH(a, addresses) {
+        IPv6Token *token;
+        struct in6_addr *a;
+        HASHMAP_FOREACH_KEY(token, a, tokens_by_address) {
                 _cleanup_(address_unrefp) Address *address = NULL;
-                Address *existing;
 
                 r = address_new(&address);
                 if (r < 0)
@@ -390,20 +428,13 @@ static int dhcp_pd_request_address(
                 address->lifetime_valid_usec = lifetime_valid_usec;
                 SET_FLAG(address->flags, IFA_F_MANAGETEMPADDR, link->network->dhcp_pd_manage_temporary_address);
                 address->route_metric = link->network->dhcp_pd_route_metric;
-
-                log_dhcp_pd_address(link, address);
+                address->token = ipv6_token_ref(token);
 
                 r = free_and_strdup_warn(&address->netlabel, link->network->dhcp_pd_netlabel);
                 if (r < 0)
                         return r;
 
-                if (address_get(link, address, &existing) < 0)
-                        link->dhcp_pd_configured = false;
-                else
-                        address_unmark(existing);
-
-                r = link_request_address(link, address, &link->dhcp_pd_messages,
-                                         dhcp_pd_address_handler, NULL);
+                r = dhcp_pd_request_address_one(address, link);
                 if (r < 0)
                         return log_link_error_errno(link, r, "Failed to request DHCP delegated prefix address: %m");
         }
index e591b8ae2e0947587abcbeb9ea3d83ee1f3a6a37..4a8cca92b6dfccd745a3688bacaef7ce38b46f31 100644 (file)
@@ -8,6 +8,7 @@
 
 #include "conf-parser.h"
 
+typedef struct Address Address;
 typedef struct Link Link;
 
 bool link_dhcp_pd_is_enabled(Link *link);
@@ -19,5 +20,6 @@ int dhcp4_pd_prefix_acquired(Link *uplink);
 int dhcp6_pd_prefix_acquired(Link *uplink);
 void dhcp_pd_prefix_lost(Link *uplink);
 void dhcp4_pd_prefix_lost(Link *uplink);
+int dhcp_pd_reconfigure_address(Address *address, Link *link);
 
 CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_pd_subnet_id);
index 292022f32283978aff6b11cc1376edf9ad537c44..b1953545eda769e3885fea87b9056f7fd5bc7838 100644 (file)
@@ -147,6 +147,20 @@ int network_adjust_dhcp_server(Network *network, Set **addresses) {
         return 0;
 }
 
+static bool dhcp_server_persist_leases(Link *link) {
+        assert(link);
+        assert(link->manager);
+        assert(link->network);
+
+        if (in4_addr_is_set(&link->network->dhcp_server_relay_target))
+                return false; /* On relay mode. Nothing saved in the persistent storage. */
+
+        if (link->network->dhcp_server_persist_leases >= 0)
+                return link->network->dhcp_server_persist_leases;
+
+        return link->manager->dhcp_server_persist_leases;
+}
+
 int address_acquire_from_dhcp_server_leases_file(Link *link, const Address *address, union in_addr_union *ret) {
         struct in_addr a;
         uint8_t prefixlen;
@@ -168,6 +182,9 @@ int address_acquire_from_dhcp_server_leases_file(Link *link, const Address *addr
         if (!link_dhcp4_server_enabled(link))
                 return -ENOENT;
 
+        if (!dhcp_server_persist_leases(link))
+                return -ENOENT;
+
         if (link->manager->persistent_storage_fd < 0)
                 return -EBUSY; /* The persistent storage is not ready, try later again. */
 
@@ -180,8 +197,14 @@ int address_acquire_from_dhcp_server_leases_file(Link *link, const Address *addr
                         lease_file,
                         &a,
                         &prefixlen);
-        if (r < 0)
+        if (r == -ENOENT)
                 return r;
+        if (r < 0)
+                return log_warning_errno(r, "Failed to load lease file %s: %s",
+                                         lease_file,
+                                         r == -ENXIO ? "expected JSON content not found" :
+                                         r == -EINVAL ? "invalid JSON" :
+                                         STRERROR(r));
 
         if (prefixlen != address->prefixlen)
                 return -ENOENT;
@@ -208,7 +231,7 @@ int link_start_dhcp4_server(Link *link) {
         /* TODO: Maybe, also check the system time is synced. If the system does not have RTC battery, then
          * the realtime clock in not usable in the early boot stage, and all saved leases may be wrongly
          * handled as expired and dropped. */
-        if (!sd_dhcp_server_is_in_relay_mode(link->dhcp_server)) {
+        if (dhcp_server_persist_leases(link)) {
 
                 if (link->manager->persistent_storage_fd < 0)
                         return 0; /* persistent storage is not ready. */
@@ -239,7 +262,7 @@ void manager_toggle_dhcp4_server_state(Manager *manager, bool start) {
         HASHMAP_FOREACH(link, manager->links_by_index) {
                 if (!link->dhcp_server)
                         continue;
-                if (sd_dhcp_server_is_in_relay_mode(link->dhcp_server))
+                if (!dhcp_server_persist_leases(link))
                         continue;
 
                 /* Even if 'start' is true, first we need to stop the server. Otherwise, we cannot (re)set
index d280251e07e16715b359ecd433291f227c199622..cf643194d58f55bab25792aa7339ab409143665f 100644 (file)
@@ -352,6 +352,9 @@ static int dhcp6_lease_lost(Link *link) {
         assert(link);
         assert(link->manager);
 
+        if (!link->dhcp6_lease)
+                return 0;
+
         log_link_info(link, "DHCPv6 lease lost");
 
         if (sd_dhcp6_lease_has_pd_prefix(link->dhcp6_lease))
index bff64633e03da13bdcab5fd5044e54a861c7fd63..0c23b4ba8202442fc79772c5cc4ffc4948ef41b1 100644 (file)
@@ -30,10 +30,13 @@ Network.RouteTable,                      config_parse_route_table_names,
 Network.IPv4Forwarding,                  config_parse_tristate,                  0,          offsetof(Manager, ip_forwarding[0])
 Network.IPv6Forwarding,                  config_parse_tristate,                  0,          offsetof(Manager, ip_forwarding[1])
 Network.IPv6PrivacyExtensions,           config_parse_ipv6_privacy_extensions,   0,          offsetof(Manager, ipv6_privacy_extensions)
+DHCPv4.UseDomains,                       config_parse_default_dhcp_use_domains,  0,          offsetof(Manager, dhcp_use_domains)
 DHCPv4.DUIDType,                         config_parse_duid_type,                 0,          offsetof(Manager, dhcp_duid)
 DHCPv4.DUIDRawData,                      config_parse_duid_rawdata,              0,          offsetof(Manager, dhcp_duid)
+DHCPv6.UseDomains,                       config_parse_default_dhcp_use_domains,  0,          offsetof(Manager, dhcp6_use_domains)
 DHCPv6.DUIDType,                         config_parse_duid_type,                 0,          offsetof(Manager, dhcp6_duid)
 DHCPv6.DUIDRawData,                      config_parse_duid_rawdata,              0,          offsetof(Manager, dhcp6_duid)
+DHCPServer.PersistLeases,                config_parse_bool,                      0,          offsetof(Manager, dhcp_server_persist_leases)
 /* Deprecated */
 DHCP.DUIDType,                           config_parse_manager_duid_type,         0,          0
 DHCP.DUIDRawData,                        config_parse_manager_duid_rawdata,      0,          0
index 3e8aa9e37aaadedf31dd6799558a7b8bfd1cc513..61e0e16818e5e3c042529bfddd29b1212446465b 100644 (file)
@@ -1,5 +1,6 @@
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 
+/* Make sure the net/if.h header is included before any linux/ one */
 #include <net/if.h>
 #include <netinet/in.h>
 #include <linux/if.h>
@@ -1630,9 +1631,9 @@ static int link_check_initialized(Link *link) {
                 return 0;
         }
 
-        r = sd_device_get_is_initialized(device);
+        r = device_is_processed(device);
         if (r < 0)
-                return log_link_warning_errno(link, r, "Could not determine whether the device is initialized: %m");
+                return log_link_warning_errno(link, r, "Could not determine whether the device is processed by udevd: %m");
         if (r == 0) {
                 /* not yet ready */
                 log_link_debug(link, "link pending udev initialization...");
@@ -1647,14 +1648,6 @@ static int link_check_initialized(Link *link) {
                 return 0;
         }
 
-        r = device_is_processing(device);
-        if (r < 0)
-                return log_link_warning_errno(link, r, "Failed to determine whether the device is being processed: %m");
-        if (r > 0) {
-                log_link_debug(link, "Interface is being processed by udevd, pending initialization.");
-                return 0;
-        }
-
         return link_initialized(link, device);
 }
 
@@ -1888,7 +1881,7 @@ static int link_admin_state_up(Link *link) {
 
         /* We set the ipv6 mtu after the device mtu, but the kernel resets
          * ipv6 mtu on NETDEV_UP, so we need to reset it. */
-        r = link_set_ipv6_mtu(link);
+        r = link_set_ipv6_mtu(link, LOG_INFO);
         if (r < 0)
                 log_link_warning_errno(link, r, "Cannot set IPv6 MTU, ignoring: %m");
 
@@ -2441,6 +2434,13 @@ static int link_update_mtu(Link *link, sd_netlink_message *message) {
 
         link->mtu = mtu;
 
+        if (IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED)) {
+                /* The kernel resets IPv6 MTU after changing device MTU. So, we need to re-set IPv6 MTU again. */
+                r = link_set_ipv6_mtu(link, LOG_INFO);
+                if (r < 0)
+                        log_link_warning_errno(link, r, "Failed to set IPv6 MTU, ignoring: %m");
+        }
+
         if (link->dhcp_client) {
                 r = sd_dhcp_client_set_mtu(link->dhcp_client, link->mtu);
                 if (r < 0)
index b3e8bb001fede8e05b50aa35cb8673f1796435b0..e56ee4f41974d8c6825e25bf9f13a8c999bc747e 100644 (file)
@@ -160,11 +160,14 @@ typedef struct Link {
         sd_dhcp_server *dhcp_server;
 
         sd_ndisc *ndisc;
+        sd_ndisc_router *ndisc_default_router;
         sd_event_source *ndisc_expire;
         Set *ndisc_rdnss;
         Set *ndisc_dnssl;
         Set *ndisc_captive_portals;
         Set *ndisc_pref64;
+        Set *ndisc_redirects;
+        uint32_t ndisc_mtu;
         unsigned ndisc_messages;
         bool ndisc_configured:1;
 
index 31aebfc389782ad78eceeb084d9c53b5134d38d0..5eeed957916313d256e9ecea9fbc222e0b5c55e0 100644 (file)
@@ -207,7 +207,7 @@ static int vl_method_set_persistent_storage(Varlink *vlink, JsonVariant *paramet
                 }
 
                 if (fstat(fd, &st) < 0)
-                        return log_warning_errno(r, "Failed to stat the passed persistent storage fd: %m");
+                        return log_warning_errno(errno, "Failed to stat the passed persistent storage fd: %m");
 
                 r = stat_verify_directory(&st);
                 if (r < 0)
index 15f5e8a6ccbc038818c9c2a1f3f809c0157c8407..8e8b27d78db00cdfa520c03ff2ed7e380b1180f7 100644 (file)
@@ -568,7 +568,7 @@ static int persistent_storage_open(void) {
         if (r <= 0)
                 return -EBADF;
 
-        fd = open("/var/lib/systemd/network/", O_CLOEXEC | O_DIRECTORY | O_PATH);
+        fd = open("/var/lib/systemd/network/", O_CLOEXEC | O_DIRECTORY);
         if (fd < 0)
                 return log_debug_errno(errno, "Failed to open /var/lib/systemd/network/, ignoring: %m");
 
@@ -602,6 +602,7 @@ int manager_new(Manager **ret, bool test_mode) {
                 .dhcp_duid.type = DUID_TYPE_EN,
                 .dhcp6_duid.type = DUID_TYPE_EN,
                 .duid_product_uuid.type = DUID_TYPE_UUID,
+                .dhcp_server_persist_leases = true,
                 .ip_forwarding = { -1, -1, },
         };
 
index fd9ab341c83545af7d0253dc0f901e5fc72ca1d7..9f621b748648dfc9ae53fb012193df1be099bc00 100644 (file)
@@ -42,6 +42,7 @@ struct Manager {
         bool manage_foreign_routes;
         bool manage_foreign_rules;
         bool manage_foreign_nexthops;
+        bool dhcp_server_persist_leases;
 
         Set *dirty_links;
         Set *new_wlan_ifindices;
@@ -63,6 +64,9 @@ struct Manager {
         OrderedSet *address_pools;
         Set *dhcp_pd_subnet_ids;
 
+        DHCPUseDomains dhcp_use_domains;
+        DHCPUseDomains dhcp6_use_domains;
+
         DUID dhcp_duid;
         DUID dhcp6_duid;
         DUID duid_product_uuid;
index 61d591a2af283b69a8892800083e5f2b82f7dc28..7e74712680fde39a50baefd9ace9acfee0465a9e 100644 (file)
@@ -177,47 +177,19 @@ static void ndisc_set_route_priority(Link *link, Route *route) {
         }
 }
 
-static int ndisc_request_route(Route *route, Link *link, sd_ndisc_router *rt) {
-        struct in6_addr router;
-        uint8_t hop_limit = 0;
-        uint32_t mtu = 0;
-        bool is_new;
+static int ndisc_request_route(Route *route, Link *link) {
         int r;
 
         assert(route);
         assert(link);
         assert(link->manager);
         assert(link->network);
-        assert(rt);
-
-        r = sd_ndisc_router_get_sender_address(rt, &router);
-        if (r < 0)
-                return r;
-
-        if (link->network->ndisc_use_mtu) {
-                r = sd_ndisc_router_get_mtu(rt, &mtu);
-                if (r < 0 && r != -ENODATA)
-                        return log_link_warning_errno(link, r, "Failed to get MTU from RA: %m");
-        }
-
-        if (link->network->ndisc_use_hop_limit) {
-                r = sd_ndisc_router_get_hop_limit(rt, &hop_limit);
-                if (r < 0 && r != -ENODATA)
-                        return log_link_warning_errno(link, r, "Failed to get hop limit from RA: %m");
-        }
 
         route->source = NETWORK_CONFIG_SOURCE_NDISC;
-        route->provider.in6 = router;
+
         if (!route->table_set)
                 route->table = link_get_ndisc_route_table(link);
-        if (!route->protocol_set)
-                route->protocol = RTPROT_RA;
-        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->ndisc_quickack);
         if (r < 0)
                 return r;
@@ -267,7 +239,7 @@ static int ndisc_request_route(Route *route, Link *link, sd_ndisc_router *rt) {
         route->pref = pref_original;
         ndisc_set_route_priority(link, route);
 
-        is_new = route_get(link->manager, route, NULL) < 0;
+        bool is_new = route_get(link->manager, route, NULL) < 0;
 
         r = link_request_route(link, route, &link->ndisc_messages, ndisc_route_handler);
         if (r < 0)
@@ -278,6 +250,23 @@ static int ndisc_request_route(Route *route, Link *link, sd_ndisc_router *rt) {
         return 0;
 }
 
+static int ndisc_request_router_route(Route *route, Link *link, sd_ndisc_router *rt) {
+        int r;
+
+        assert(route);
+        assert(link);
+        assert(rt);
+
+        r = sd_ndisc_router_get_sender_address(rt, &route->provider.in6);
+        if (r < 0)
+                return r;
+
+        if (!route->protocol_set)
+                route->protocol = RTPROT_RA;
+
+        return ndisc_request_route(route, link);
+}
+
 static int ndisc_remove_route(Route *route, Link *link) {
         int r;
 
@@ -327,21 +316,14 @@ static int ndisc_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Reques
         return 1;
 }
 
-static int ndisc_request_address(Address *address, Link *link, sd_ndisc_router *rt) {
-        struct in6_addr router;
+static int ndisc_request_address(Address *address, Link *link) {
         bool is_new;
         int r;
 
         assert(address);
         assert(link);
-        assert(rt);
-
-        r = sd_ndisc_router_get_sender_address(rt, &router);
-        if (r < 0)
-                return r;
 
         address->source = NETWORK_CONFIG_SOURCE_NDISC;
-        address->provider.in6 = router;
 
         r = free_and_strdup_warn(&address->netlabel, link->network->ndisc_netlabel);
         if (r < 0)
@@ -378,6 +360,296 @@ static int ndisc_request_address(Address *address, Link *link, sd_ndisc_router *
         return 0;
 }
 
+int ndisc_reconfigure_address(Address *address, Link *link) {
+        int r;
+
+        assert(address);
+        assert(address->source == NETWORK_CONFIG_SOURCE_NDISC);
+        assert(link);
+
+        r = regenerate_address(address, link);
+        if (r <= 0)
+                return r;
+
+        r = ndisc_request_address(address, link);
+        if (r < 0)
+                return r;
+
+        if (!link->ndisc_configured)
+                link_set_state(link, LINK_STATE_CONFIGURING);
+
+        link_check_ready(link);
+        return 0;
+}
+
+static int ndisc_redirect_route_new(sd_ndisc_redirect *rd, Route **ret) {
+        _cleanup_(route_unrefp) Route *route = NULL;
+        struct in6_addr gateway, destination;
+        int r;
+
+        assert(rd);
+        assert(ret);
+
+        r = sd_ndisc_redirect_get_target_address(rd, &gateway);
+        if (r < 0)
+                return r;
+
+        r = sd_ndisc_redirect_get_destination_address(rd, &destination);
+        if (r < 0)
+                return r;
+
+        r = route_new(&route);
+        if (r < 0)
+                return r;
+
+        route->family = AF_INET6;
+        if (!in6_addr_equal(&gateway, &destination)) {
+                route->nexthop.gw.in6 = gateway;
+                route->nexthop.family = AF_INET6;
+        }
+        route->dst.in6 = destination;
+        route->dst_prefixlen = 128;
+        route->protocol = RTPROT_REDIRECT;
+
+        r = sd_ndisc_redirect_get_sender_address(rd, &route->provider.in6);
+        if (r < 0)
+                return r;
+
+        *ret = TAKE_PTR(route);
+        return 0;
+}
+
+static int ndisc_remove_redirect_route(Link *link, sd_ndisc_redirect *rd) {
+        _cleanup_(route_unrefp) Route *route = NULL;
+        int r;
+
+        assert(link);
+        assert(rd);
+
+        r = ndisc_redirect_route_new(rd, &route);
+        if (r < 0)
+                return r;
+
+        return ndisc_remove_route(route, link);
+}
+
+static void ndisc_redirect_hash_func(const sd_ndisc_redirect *x, struct siphash *state) {
+        struct in6_addr dest = {};
+
+        assert(x);
+        assert(state);
+
+        (void) sd_ndisc_redirect_get_destination_address((sd_ndisc_redirect*) x, &dest);
+
+        siphash24_compress_typesafe(dest, state);
+}
+
+static int ndisc_redirect_compare_func(const sd_ndisc_redirect *x, const sd_ndisc_redirect *y) {
+        struct in6_addr dest_x = {}, dest_y = {};
+
+        assert(x);
+        assert(y);
+
+        (void) sd_ndisc_redirect_get_destination_address((sd_ndisc_redirect*) x, &dest_x);
+        (void) sd_ndisc_redirect_get_destination_address((sd_ndisc_redirect*) y, &dest_y);
+
+        return memcmp(&dest_x, &dest_y, sizeof(dest_x));
+}
+
+DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(
+                ndisc_redirect_hash_ops,
+                sd_ndisc_redirect,
+                ndisc_redirect_hash_func,
+                ndisc_redirect_compare_func,
+                sd_ndisc_redirect_unref);
+
+static int ndisc_redirect_equal(sd_ndisc_redirect *x, sd_ndisc_redirect *y) {
+        struct in6_addr a, b;
+        int r;
+
+        assert(x);
+        assert(y);
+
+        r = sd_ndisc_redirect_get_destination_address(x, &a);
+        if (r < 0)
+                return r;
+
+        r = sd_ndisc_redirect_get_destination_address(y, &b);
+        if (r < 0)
+                return r;
+
+        if (!in6_addr_equal(&a, &b))
+                return false;
+
+        r = sd_ndisc_redirect_get_target_address(x, &a);
+        if (r < 0)
+                return r;
+
+        r = sd_ndisc_redirect_get_target_address(y, &b);
+        if (r < 0)
+                return r;
+
+        return in6_addr_equal(&a, &b);
+}
+
+static int ndisc_redirect_drop_conflict(Link *link, sd_ndisc_redirect *rd) {
+        _cleanup_(sd_ndisc_redirect_unrefp) sd_ndisc_redirect *existing = NULL;
+        int r;
+
+        assert(link);
+        assert(rd);
+
+        existing = set_remove(link->ndisc_redirects, rd);
+        if (!existing)
+                return 0;
+
+        r = ndisc_redirect_equal(rd, existing);
+        if (r != 0)
+                return r;
+
+        return ndisc_remove_redirect_route(link, existing);
+}
+
+static int ndisc_redirect_verify_sender(Link *link, sd_ndisc_redirect *rd) {
+        sd_ndisc_redirect *existing;
+        struct in6_addr router, sender;
+        int r;
+
+        assert(link);
+        assert(rd);
+
+        /* RFC 4861 section 8.1
+        * The IP source address of the Redirect is the same as the current first-hop router for the specified
+        * ICMP Destination Address. */
+
+        r = sd_ndisc_redirect_get_sender_address(rd, &sender);
+        if (r < 0)
+                return r;
+
+        existing = set_get(link->ndisc_redirects, rd);
+        if (existing) {
+                struct in6_addr target, dest;
+
+                /* If we have received Redirect message for the host, the sender must be the previous target. */
+
+                r = sd_ndisc_redirect_get_target_address(existing, &target);
+                if (r < 0)
+                        return r;
+
+                if (in6_addr_equal(&sender, &target))
+                        return true;
+
+                /* If the existing redirect route is on-link, that is, the destination and target address are
+                 * equivalent, then also accept Redirect message from the current default router. This is not
+                 * mentioned by the RFC, but without this, we cannot update on-link redirect route. */
+                r = sd_ndisc_redirect_get_destination_address(existing, &dest);
+                if (r < 0)
+                        return r;
+
+                if (!in6_addr_equal(&dest, &target))
+                        return false;
+        }
+
+        if (!link->ndisc_default_router)
+                return false;
+
+        r = sd_ndisc_router_get_sender_address(link->ndisc_default_router, &router);
+        if (r < 0)
+                return r;
+
+        /* The sender must be the default router. */
+        return in6_addr_equal(&sender, &router);
+}
+
+static int ndisc_redirect_handler(Link *link, sd_ndisc_redirect *rd) {
+        int r;
+
+        assert(link);
+        assert(link->network);
+        assert(rd);
+
+        if (!link->network->ndisc_use_redirect)
+                return 0;
+
+        r = ndisc_redirect_verify_sender(link, rd);
+        if (r <= 0)
+                return r;
+
+        /* First, drop conflicting redirect route, if exists. */
+        r = ndisc_redirect_drop_conflict(link, rd);
+        if (r < 0)
+                return r;
+
+        /* Then, remember the received message. */
+        r = set_ensure_put(&link->ndisc_redirects, &ndisc_redirect_hash_ops, rd);
+        if (r < 0)
+                return r;
+
+        sd_ndisc_redirect_ref(rd);
+
+        /* Finally, request the corresponding route. */
+        _cleanup_(route_unrefp) Route *route = NULL;
+        r = ndisc_redirect_route_new(rd, &route);
+        if (r < 0)
+                return r;
+
+        return ndisc_request_route(route, link);
+}
+
+static int ndisc_drop_redirect(Link *link, const struct in6_addr *router) {
+        int r, ret = 0;
+
+        assert(link);
+
+        sd_ndisc_redirect *rd;
+        SET_FOREACH(rd, link->ndisc_redirects) {
+                if (router) {
+                        struct in6_addr target;
+
+                        r = sd_ndisc_redirect_get_target_address(rd, &target);
+                        if (r < 0)
+                                return r;
+
+                        if (!in6_addr_equal(&target, router))
+                                continue;
+                }
+
+                r = ndisc_remove_redirect_route(link, rd);
+                if (r < 0)
+                        RET_GATHER(ret, log_link_warning_errno(link, r, "Failed to remove redirect route, ignoring: %m"));
+
+                sd_ndisc_redirect_unref(set_remove(link->ndisc_redirects, rd));
+        }
+
+        return ret;
+}
+
+static int ndisc_update_redirect_sender(Link *link, const struct in6_addr *original_address, const struct in6_addr *current_address) {
+        int r;
+
+        assert(link);
+        assert(original_address);
+        assert(current_address);
+
+        sd_ndisc_redirect *rd;
+        SET_FOREACH(rd, link->ndisc_redirects) {
+                struct in6_addr sender;
+
+                r = sd_ndisc_redirect_get_sender_address(rd, &sender);
+                if (r < 0)
+                        return r;
+
+                if (!in6_addr_equal(&sender, original_address))
+                        continue;
+
+                r = sd_ndisc_redirect_set_sender_address(rd, current_address);
+                if (r < 0)
+                        return r;
+        }
+
+        return 0;
+}
+
 static int ndisc_router_drop_default(Link *link, sd_ndisc_router *rt) {
         _cleanup_(route_unrefp) Route *route = NULL;
         struct in6_addr gateway;
@@ -473,7 +745,7 @@ static int ndisc_router_process_default(Link *link, sd_ndisc_router *rt) {
                 route->nexthop.gw.in6 = gateway;
                 route->lifetime_usec = lifetime_usec;
 
-                r = ndisc_request_route(route, link, rt);
+                r = ndisc_request_router_route(route, link, rt);
                 if (r < 0)
                         return log_link_warning_errno(link, r, "Could not request default route: %m");
         }
@@ -497,7 +769,7 @@ static int ndisc_router_process_default(Link *link, sd_ndisc_router *rt) {
                         route->pref = preference;
                 route->lifetime_usec = lifetime_usec;
 
-                r = ndisc_request_route(route, link, rt);
+                r = ndisc_request_router_route(route, link, rt);
                 if (r < 0)
                         return log_link_warning_errno(link, r, "Could not request gateway: %m");
         }
@@ -505,6 +777,126 @@ static int ndisc_router_process_default(Link *link, sd_ndisc_router *rt) {
         return 0;
 }
 
+static int update_default_router_address(Link *link, const struct in6_addr *original_address, const struct in6_addr *current_address) {
+        struct in6_addr a;
+        int r;
+
+        assert(link);
+        assert(original_address);
+        assert(current_address);
+
+        if (!link->ndisc_default_router)
+                return 0;
+
+        r = sd_ndisc_router_get_sender_address(link->ndisc_default_router, &a);
+        if (r < 0)
+                return r;
+
+        if (!in6_addr_equal(&a, original_address))
+                return 0;
+
+        return sd_ndisc_router_set_sender_address(link->ndisc_default_router, current_address);
+}
+
+static int drop_default_router(Link *link, const struct in6_addr *router, usec_t timestamp_usec) {
+        usec_t lifetime_usec;
+        int r;
+
+        assert(link);
+
+        if (!link->ndisc_default_router)
+                return 0;
+
+        if (router) {
+                struct in6_addr a;
+
+                r = sd_ndisc_router_get_sender_address(link->ndisc_default_router, &a);
+                if (r < 0)
+                        return r;
+
+                if (!in6_addr_equal(&a, router))
+                        return 0;
+        }
+
+        r = sd_ndisc_router_get_lifetime_timestamp(link->ndisc_default_router, CLOCK_BOOTTIME, &lifetime_usec);
+        if (r < 0)
+                return r;
+
+        if (lifetime_usec > timestamp_usec)
+                return 0;
+
+        link->ndisc_default_router = sd_ndisc_router_unref(link->ndisc_default_router);
+        return 0;
+}
+
+static int accept_default_router(sd_ndisc_router *new_router, sd_ndisc_router *existing_router) {
+        usec_t lifetime_usec;
+        struct in6_addr a, b;
+        uint8_t p, q;
+        int r;
+
+        assert(new_router);
+
+        r = sd_ndisc_router_get_lifetime(new_router, &lifetime_usec);
+        if (r < 0)
+                return r;
+
+        if (lifetime_usec == 0)
+                return false; /* Received a new RA about revoking the router, ignoring. */
+
+        if (!existing_router)
+                return true;
+
+        /* lifetime of the existing router is already checked in ndisc_drop_outdated(). */
+
+        r = sd_ndisc_router_get_sender_address(new_router, &a);
+        if (r < 0)
+                return r;
+
+        r = sd_ndisc_router_get_sender_address(existing_router, &b);
+        if (r < 0)
+                return r;
+
+        if (in6_addr_equal(&a, &b))
+                return true; /* Received a new RA from the remembered router. Replace the remembered RA. */
+
+        r = sd_ndisc_router_get_preference(new_router, &p);
+        if (r < 0)
+                return r;
+
+        r = sd_ndisc_router_get_preference(existing_router, &q);
+        if (r < 0)
+                return r;
+
+        if (p == q)
+                return true;
+
+        if (p == SD_NDISC_PREFERENCE_HIGH)
+                return true;
+
+        if (p == SD_NDISC_PREFERENCE_MEDIUM && q == SD_NDISC_PREFERENCE_LOW)
+                return true;
+
+        return false;
+}
+
+static int ndisc_remember_default_router(Link *link, sd_ndisc_router *rt) {
+        int r;
+
+        assert(link);
+        assert(rt);
+
+        r = accept_default_router(rt, link->ndisc_default_router);
+        if (r <= 0)
+                return r;
+
+        sd_ndisc_router_ref(rt);
+        sd_ndisc_router_unref(link->ndisc_default_router);
+        link->ndisc_default_router = rt;
+
+        return 1; /* The received router advertisement is from the default router. */
+}
+
 static int ndisc_router_process_reachable_time(Link *link, sd_ndisc_router *rt) {
         usec_t reachable_time, msec;
         int r;
@@ -621,10 +1013,98 @@ static int ndisc_router_process_hop_limit(Link *link, sd_ndisc_router *rt) {
         return 0;
 }
 
+static int ndisc_router_process_mtu(Link *link, sd_ndisc_router *rt) {
+        uint32_t mtu;
+        int r;
+
+        assert(link);
+        assert(link->network);
+        assert(rt);
+
+        if (!link->network->ndisc_use_mtu)
+                return 0;
+
+        /* Ignore the MTU option if the lifetime is zero. */
+        r = sd_ndisc_router_get_lifetime(rt, NULL);
+        if (r <= 0)
+                return r;
+
+        r = sd_ndisc_router_get_mtu(rt, &mtu);
+        if (r == -ENODATA)
+                return 0;
+        if (r < 0)
+                return log_link_warning_errno(link, r, "Failed to get MTU from RA: %m");
+
+        link->ndisc_mtu = mtu;
+
+        r = link_set_ipv6_mtu(link, LOG_DEBUG);
+        if (r < 0)
+                log_link_warning_errno(link, r, "Failed to apply IPv6 MTU (%"PRIu32"), ignoring: %m", mtu);
+
+        return 0;
+}
+
+static int ndisc_address_set_lifetime(Address *address, Link *link, sd_ndisc_router *rt) {
+        Address *existing;
+        usec_t t;
+        int r;
+
+        assert(address);
+        assert(link);
+        assert(rt);
+
+        /* This is mostly based on RFC 4862 section 5.5.3 (e). However, the definition of 'RemainingLifetime'
+         * is ambiguous, and there is no clear explanation when the address is not assigned yet. If we assume
+         * that 'RemainingLifetime' is zero in that case, then IPv6 Core Conformance test [v6LC.3.2.5 Part C]
+         * fails. So, in such case, we skip the conditions about 'RemainingLifetime'. */
+
+        r = sd_ndisc_router_prefix_get_valid_lifetime_timestamp(rt, CLOCK_BOOTTIME, &address->lifetime_valid_usec);
+        if (r < 0)
+                return r;
+
+        r = sd_ndisc_router_prefix_get_preferred_lifetime_timestamp(rt, CLOCK_BOOTTIME, &address->lifetime_preferred_usec);
+        if (r < 0)
+                return r;
+
+        /* RFC 4862 section 5.5.3 (e)
+         * 1. If the received Valid Lifetime is greater than 2 hours or greater than RemainingLifetime,
+         *    set the valid lifetime of the corresponding address to the advertised Valid Lifetime. */
+        r = sd_ndisc_router_prefix_get_valid_lifetime(rt, &t);
+        if (r < 0)
+                return r;
+
+        if (t > 2 * USEC_PER_HOUR)
+                return 0;
+
+        r = sd_ndisc_router_get_timestamp(rt, CLOCK_BOOTTIME, &t);
+        if (r < 0)
+                return r;
+
+        if (address_get(link, address, &existing) >= 0 && existing->source == NETWORK_CONFIG_SOURCE_NDISC) {
+                if (address->lifetime_valid_usec > existing->lifetime_valid_usec)
+                        return 0;
+
+                /* 2. If RemainingLifetime is less than or equal to 2 hours, ignore the Prefix Information
+                 *    option with regards to the valid lifetime, unless the Router Advertisement from which
+                 *    this option was obtained has been authenticated (e.g., via Secure Neighbor Discovery
+                 *    [RFC3971]). If the Router Advertisement was authenticated, the valid lifetime of the
+                 *    corresponding address should be set to the Valid Lifetime in the received option.
+                 *
+                 * Currently, authentication is not supported. So check the lifetime of the existing address. */
+                if (existing->lifetime_valid_usec <= usec_add(t, 2 * USEC_PER_HOUR)) {
+                        address->lifetime_valid_usec = existing->lifetime_valid_usec;
+                        return 0;
+                }
+        }
+
+        /* 3. Otherwise, reset the valid lifetime of the corresponding address to 2 hours. */
+        address->lifetime_valid_usec = usec_add(t, 2 * USEC_PER_HOUR);
+        return 0;
+}
+
 static int ndisc_router_process_autonomous_prefix(Link *link, sd_ndisc_router *rt) {
         usec_t lifetime_valid_usec, lifetime_preferred_usec;
-        _cleanup_set_free_ Set *addresses = NULL;
-        struct in6_addr prefix, *a;
+        struct in6_addr prefix, router;
         uint8_t prefixlen;
         int r;
 
@@ -635,6 +1115,10 @@ static int ndisc_router_process_autonomous_prefix(Link *link, sd_ndisc_router *r
         if (!link->network->ndisc_use_autonomous_prefix)
                 return 0;
 
+        r = sd_ndisc_router_get_sender_address(rt, &router);
+        if (r < 0)
+                return log_link_warning_errno(link, r, "Failed to get router address: %m");
+
         r = sd_ndisc_router_prefix_get_address(rt, &prefix);
         if (r < 0)
                 return log_link_warning_errno(link, r, "Failed to get prefix address: %m");
@@ -650,52 +1134,50 @@ static int ndisc_router_process_autonomous_prefix(Link *link, sd_ndisc_router *r
                 return 0;
         }
 
-        r = sd_ndisc_router_prefix_get_valid_lifetime_timestamp(rt, CLOCK_BOOTTIME, &lifetime_valid_usec);
+        r = sd_ndisc_router_prefix_get_valid_lifetime(rt, &lifetime_valid_usec);
         if (r < 0)
                 return log_link_warning_errno(link, r, "Failed to get prefix valid lifetime: %m");
 
-        r = sd_ndisc_router_prefix_get_preferred_lifetime_timestamp(rt, CLOCK_BOOTTIME, &lifetime_preferred_usec);
+        r = sd_ndisc_router_prefix_get_preferred_lifetime(rt, &lifetime_preferred_usec);
         if (r < 0)
                 return log_link_warning_errno(link, r, "Failed to get prefix preferred lifetime: %m");
 
-        /* The preferred lifetime is never greater than the valid lifetime */
+        /* RFC 4862 section 5.5.3 (c)
+         * If the preferred lifetime is greater than the valid lifetime, silently ignore the Prefix
+         * Information option. */
         if (lifetime_preferred_usec > lifetime_valid_usec)
                 return 0;
 
-        r = ndisc_generate_addresses(link, &prefix, prefixlen, &addresses);
+        _cleanup_hashmap_free_ Hashmap *tokens_by_address = NULL;
+        r = ndisc_generate_addresses(link, &prefix, prefixlen, &tokens_by_address);
         if (r < 0)
                 return log_link_warning_errno(link, r, "Failed to generate SLAAC addresses: %m");
 
-        SET_FOREACH(a, addresses) {
+        IPv6Token *token;
+        struct in6_addr *a;
+        HASHMAP_FOREACH_KEY(token, a, tokens_by_address) {
                 _cleanup_(address_unrefp) Address *address = NULL;
 
                 r = address_new(&address);
                 if (r < 0)
                         return log_oom();
 
+                address->provider.in6 = router;
                 address->family = AF_INET6;
                 address->in_addr.in6 = *a;
                 address->prefixlen = prefixlen;
                 address->flags = IFA_F_NOPREFIXROUTE|IFA_F_MANAGETEMPADDR;
-                address->lifetime_valid_usec = lifetime_valid_usec;
-                address->lifetime_preferred_usec = lifetime_preferred_usec;
+                address->token = ipv6_token_ref(token);
 
-                /* draft-ietf-6man-slaac-renum-07 section 4.2
-                 * https://datatracker.ietf.org/doc/html/draft-ietf-6man-slaac-renum-07#section-4.2
-                 *
-                 * If the advertised prefix is equal to the prefix of an address configured by stateless
-                 * autoconfiguration in the list, the valid lifetime and the preferred lifetime of the
-                 * address should be updated by processing the Valid Lifetime and the Preferred Lifetime
-                 * (respectively) in the received advertisement. */
-                if (lifetime_valid_usec == 0) {
-                        r = address_remove_and_cancel(address, link);
-                        if (r < 0)
-                                return log_link_warning_errno(link, r, "Could not remove SLAAC address: %m");
-                } else {
-                        r = ndisc_request_address(address, link, rt);
-                        if (r < 0)
-                                return log_link_warning_errno(link, r, "Could not request SLAAC address: %m");
-                }
+                r = ndisc_address_set_lifetime(address, link, rt);
+                if (r < 0)
+                        return log_link_warning_errno(link, r, "Failed to set lifetime of SLAAC address: %m");
+
+                assert(address->lifetime_preferred_usec <= address->lifetime_valid_usec);
+
+                r = ndisc_request_address(address, link);
+                if (r < 0)
+                        return log_link_warning_errno(link, r, "Could not request SLAAC address: %m");
         }
 
         return 0;
@@ -742,59 +1224,23 @@ static int ndisc_router_process_onlink_prefix(Link *link, sd_ndisc_router *rt) {
         route->pref = preference;
         route->lifetime_usec = lifetime_usec;
 
-        r = ndisc_request_route(route, link, rt);
-        if (r < 0)
-                return log_link_warning_errno(link, r, "Could not request prefix route: %m");
-
-        return 0;
-}
-
-static int ndisc_router_drop_onlink_prefix(Link *link, sd_ndisc_router *rt) {
-        _cleanup_(route_unrefp) Route *route = NULL;
-        uint8_t prefixlen;
-        struct in6_addr prefix;
-        usec_t lifetime_usec;
-        int r;
-
-        assert(link);
-        assert(link->network);
-        assert(rt);
-
-        /* RFC 4861 section 6.3.4.
-         * Note, however, that a Prefix Information option with the on-link flag set to zero conveys no
-         * information concerning on-link determination and MUST NOT be interpreted to mean that addresses
-         * covered by the prefix are off-link. The only way to cancel a previous on-link indication is to
-         * advertise that prefix with the L-bit set and the Lifetime set to zero. */
-
-        if (!link->network->ndisc_use_onlink_prefix)
-                return 0;
-
-        r = sd_ndisc_router_prefix_get_valid_lifetime(rt, &lifetime_usec);
-        if (r < 0)
-                return log_link_warning_errno(link, r, "Failed to get prefix lifetime: %m");
-
-        if (lifetime_usec != 0)
-                return 0;
-
-        r = sd_ndisc_router_prefix_get_address(rt, &prefix);
-        if (r < 0)
-                return log_link_warning_errno(link, r, "Failed to get prefix address: %m");
-
-        r = sd_ndisc_router_prefix_get_prefixlen(rt, &prefixlen);
-        if (r < 0)
-                return log_link_warning_errno(link, r, "Failed to get prefix length: %m");
-
-        r = route_new(&route);
-        if (r < 0)
-                return log_oom();
-
-        route->family = AF_INET6;
-        route->dst.in6 = prefix;
-        route->dst_prefixlen = prefixlen;
-
-        r = ndisc_remove_route(route, link);
-        if (r < 0)
-                return log_link_warning_errno(link, r, "Could not remove prefix route: %m");
+        /* RFC 4861 section 6.3.4:
+         * - If the prefix is not already present in the Prefix List, and the Prefix Information option's
+         *   Valid Lifetime field is non-zero, create a new entry for the prefix and initialize its
+         *   invalidation timer to the Valid Lifetime value in the Prefix Information option.
+         *
+         * - If the prefix is already present in the host's Prefix List as the result of a previously
+         *   received advertisement, reset its invalidation timer to the Valid Lifetime value in the Prefix
+         *   Information option. If the new Lifetime value is zero, time-out the prefix immediately. */
+        if (lifetime_usec == 0) {
+                r = ndisc_remove_route(route, link);
+                if (r < 0)
+                        return log_link_warning_errno(link, r, "Failed to remove prefix route: %m");
+        } else {
+                r = ndisc_request_router_route(route, link, rt);
+                if (r < 0)
+                        return log_link_warning_errno(link, r, "Failed to request prefix route: %m");
+        }
 
         return 0;
 }
@@ -837,12 +1283,11 @@ static int ndisc_router_process_prefix(Link *link, sd_ndisc_router *rt) {
         if (r < 0)
                 return log_link_warning_errno(link, r, "Failed to get RA prefix flags: %m");
 
-        if (FLAGS_SET(flags, ND_OPT_PI_FLAG_ONLINK))
+        if (FLAGS_SET(flags, ND_OPT_PI_FLAG_ONLINK)) {
                 r = ndisc_router_process_onlink_prefix(link, rt);
-        else
-                r = ndisc_router_drop_onlink_prefix(link, rt);
-        if (r < 0)
-                return r;
+                if (r < 0)
+                        return r;
+        }
 
         if (FLAGS_SET(flags, ND_OPT_PI_FLAG_AUTO)) {
                 r = ndisc_router_process_autonomous_prefix(link, rt);
@@ -877,11 +1322,6 @@ static int ndisc_router_process_route(Link *link, sd_ndisc_router *rt) {
         if (r < 0)
                 return log_link_warning_errno(link, r, "Failed to get route prefix length: %m");
 
-        if (in6_addr_is_null(&dst) && prefixlen == 0) {
-                log_link_debug(link, "Route prefix is ::/0, ignoring");
-                return 0;
-        }
-
         if (in6_prefix_is_filtered(&dst, prefixlen,
                                    link->network->ndisc_allow_listed_route_prefix,
                                    link->network->ndisc_deny_listed_route_prefix)) {
@@ -925,9 +1365,15 @@ static int ndisc_router_process_route(Link *link, sd_ndisc_router *rt) {
         route->dst_prefixlen = prefixlen;
         route->lifetime_usec = lifetime_usec;
 
-        r = ndisc_request_route(route, link, rt);
-        if (r < 0)
-                return log_link_warning_errno(link, r, "Could not request additional route: %m");
+        if (lifetime_usec != 0) {
+                r = ndisc_request_router_route(route, link, rt);
+                if (r < 0)
+                        return log_link_warning_errno(link, r, "Could not request additional route: %m");
+        } else {
+                r = ndisc_remove_route(route, link);
+                if (r < 0)
+                        return log_link_warning_errno(link, r, "Could not remove additional route with zero lifetime: %m");
+        }
 
         return 0;
 }
@@ -1391,7 +1837,7 @@ static int ndisc_router_process_options(Link *link, sd_ndisc_router *rt) {
         }
 }
 
-static int ndisc_drop_outdated(Link *link, usec_t timestamp_usec) {
+static int ndisc_drop_outdated(Link *link, const struct in6_addr *router, usec_t timestamp_usec) {
         bool updated = false;
         NDiscDNSSL *dnssl;
         NDiscRDNSS *rdnss;
@@ -1410,6 +1856,10 @@ static int ndisc_drop_outdated(Link *link, usec_t timestamp_usec) {
          * valid lifetimes to improve the reaction of SLAAC to renumbering events.
          * See draft-ietf-6man-slaac-renum-02, section 4.2. */
 
+        r = drop_default_router(link, router, timestamp_usec);
+        if (r < 0)
+                RET_GATHER(ret, log_link_warning_errno(link, r, "Failed to drop outdated default router, ignoring: %m"));
+
         SET_FOREACH(route, link->manager->routes) {
                 if (route->source != NETWORK_CONFIG_SOURCE_NDISC)
                         continue;
@@ -1417,9 +1867,15 @@ static int ndisc_drop_outdated(Link *link, usec_t timestamp_usec) {
                 if (route->nexthop.ifindex != link->ifindex)
                         continue;
 
+                if (route->protocol == RTPROT_REDIRECT)
+                        continue; /* redirect route will be dropped by ndisc_drop_redirect(). */
+
                 if (route->lifetime_usec > timestamp_usec)
                         continue; /* the route is still valid */
 
+                if (router && !in6_addr_equal(&route->provider.in6, router))
+                        continue;
+
                 r = route_remove_and_cancel(route, link->manager);
                 if (r < 0)
                         RET_GATHER(ret, log_link_warning_errno(link, r, "Failed to remove outdated SLAAC route, ignoring: %m"));
@@ -1432,6 +1888,9 @@ static int ndisc_drop_outdated(Link *link, usec_t timestamp_usec) {
                 if (address->lifetime_valid_usec > timestamp_usec)
                         continue; /* the address is still valid */
 
+                if (router && !in6_addr_equal(&address->provider.in6, router))
+                        continue;
+
                 r = address_remove_and_cancel(address, link);
                 if (r < 0)
                         RET_GATHER(ret, log_link_warning_errno(link, r, "Failed to remove outdated SLAAC address, ignoring: %m"));
@@ -1441,6 +1900,9 @@ static int ndisc_drop_outdated(Link *link, usec_t timestamp_usec) {
                 if (rdnss->lifetime_usec > timestamp_usec)
                         continue; /* the DNS server is still valid */
 
+                if (router && !in6_addr_equal(&rdnss->router, router))
+                        continue;
+
                 free(set_remove(link->ndisc_rdnss, rdnss));
                 updated = true;
         }
@@ -1449,6 +1911,9 @@ static int ndisc_drop_outdated(Link *link, usec_t timestamp_usec) {
                 if (dnssl->lifetime_usec > timestamp_usec)
                         continue; /* the DNS domain is still valid */
 
+                if (router && !in6_addr_equal(&dnssl->router, router))
+                        continue;
+
                 free(set_remove(link->ndisc_dnssl, dnssl));
                 updated = true;
         }
@@ -1457,6 +1922,9 @@ static int ndisc_drop_outdated(Link *link, usec_t timestamp_usec) {
                 if (cp->lifetime_usec > timestamp_usec)
                         continue; /* the captive portal is still valid */
 
+                if (router && !in6_addr_equal(&cp->router, router))
+                        continue;
+
                 ndisc_captive_portal_free(set_remove(link->ndisc_captive_portals, cp));
                 updated = true;
         }
@@ -1465,6 +1933,9 @@ static int ndisc_drop_outdated(Link *link, usec_t timestamp_usec) {
                 if (p64->lifetime_usec > timestamp_usec)
                         continue; /* the pref64 prefix is still valid */
 
+                if (router && !in6_addr_equal(&p64->router, router))
+                        continue;
+
                 free(set_remove(link->ndisc_pref64, p64));
                 /* The pref64 prefix is not exported through the state file, hence it is not necessary to set
                  * the 'updated' flag. */
@@ -1486,7 +1957,7 @@ static int ndisc_expire_handler(sd_event_source *s, uint64_t usec, void *userdat
 
         assert_se(sd_event_now(link->manager->event, CLOCK_BOOTTIME, &now_usec) >= 0);
 
-        (void) ndisc_drop_outdated(link, now_usec);
+        (void) ndisc_drop_outdated(link, /* router = */ NULL, now_usec);
         (void) ndisc_setup_expire(link);
         return 0;
 }
@@ -1635,7 +2106,11 @@ static int ndisc_router_handler(Link *link, sd_ndisc_router *rt) {
         if (r < 0)
                 return r;
 
-        r = ndisc_drop_outdated(link, timestamp_usec);
+        r = ndisc_drop_outdated(link, /* router = */ NULL, timestamp_usec);
+        if (r < 0)
+                return r;
+
+        r = ndisc_remember_default_router(link, rt);
         if (r < 0)
                 return r;
 
@@ -1659,6 +2134,10 @@ static int ndisc_router_handler(Link *link, sd_ndisc_router *rt) {
         if (r < 0)
                 return r;
 
+        r = ndisc_router_process_mtu(link, rt);
+        if (r < 0)
+                return r;
+
         r = ndisc_router_process_options(link, rt);
         if (r < 0)
                 return r;
@@ -1667,6 +2146,9 @@ static int ndisc_router_handler(Link *link, sd_ndisc_router *rt) {
         if (r < 0)
                 return r;
 
+        if (sd_ndisc_router_get_lifetime(rt, NULL) <= 0)
+                (void) ndisc_drop_redirect(link, &router);
+
         if (link->ndisc_messages == 0)
                 link->ndisc_configured = true;
         else
@@ -1679,6 +2161,141 @@ static int ndisc_router_handler(Link *link, sd_ndisc_router *rt) {
         return 0;
 }
 
+static int ndisc_neighbor_handle_non_router_message(Link *link, sd_ndisc_neighbor *na) {
+        struct in6_addr address;
+        int r;
+
+        assert(link);
+        assert(na);
+
+        /* Received Neighbor Advertisement message without Router flag. The node might have been a router,
+         * and now it is not. Let's drop all configurations based on RAs sent from the node. */
+
+        r = sd_ndisc_neighbor_get_target_address(na, &address);
+        if (r == -ENODATA)
+                return 0;
+        if (r < 0)
+                return r;
+
+        (void) ndisc_drop_outdated(link, /* router = */ &address, /* timestamp_usec = */ USEC_INFINITY);
+        (void) ndisc_drop_redirect(link, &address);
+
+        return 0;
+}
+
+static int ndisc_neighbor_handle_router_message(Link *link, sd_ndisc_neighbor *na) {
+        struct in6_addr current_address, original_address;
+        int r;
+
+        assert(link);
+        assert(link->manager);
+        assert(na);
+
+        /* Received Neighbor Advertisement message with Router flag. If the router address is changed, update
+         * the provider field of configurations. */
+
+        r = sd_ndisc_neighbor_get_sender_address(na, &current_address);
+        if (r == -ENODATA)
+                return 0;
+        if (r < 0)
+                return r;
+
+        r = sd_ndisc_neighbor_get_target_address(na, &original_address);
+        if (r == -ENODATA)
+                return 0;
+        if (r < 0)
+                return r;
+
+        if (in6_addr_equal(&current_address, &original_address))
+                return 0; /* the router address is not changed */
+
+        r = update_default_router_address(link, &original_address, &current_address);
+        if (r < 0)
+                return r;
+
+        r = ndisc_update_redirect_sender(link, &original_address, &current_address);
+        if (r < 0)
+                return r;
+
+        Route *route;
+        SET_FOREACH(route, link->manager->routes) {
+                if (route->source != NETWORK_CONFIG_SOURCE_NDISC)
+                        continue;
+
+                if (route->nexthop.ifindex != link->ifindex)
+                        continue;
+
+                if (!in6_addr_equal(&route->provider.in6, &original_address))
+                        continue;
+
+                route->provider.in6 = current_address;
+        }
+
+        Address *address;
+        SET_FOREACH(address, link->addresses) {
+                if (address->source != NETWORK_CONFIG_SOURCE_NDISC)
+                        continue;
+
+                if (!in6_addr_equal(&address->provider.in6, &original_address))
+                        continue;
+
+                address->provider.in6 = current_address;
+        }
+
+        NDiscRDNSS *rdnss;
+        SET_FOREACH(rdnss, link->ndisc_rdnss) {
+                if (!in6_addr_equal(&rdnss->router, &original_address))
+                        continue;
+
+                rdnss->router = current_address;
+        }
+
+        NDiscDNSSL *dnssl;
+        SET_FOREACH(dnssl, link->ndisc_dnssl) {
+                if (!in6_addr_equal(&dnssl->router, &original_address))
+                        continue;
+
+                dnssl->router = current_address;
+        }
+
+        NDiscCaptivePortal *cp;
+        SET_FOREACH(cp, link->ndisc_captive_portals) {
+                if (!in6_addr_equal(&cp->router, &original_address))
+                        continue;
+
+                cp->router = current_address;
+        }
+
+        NDiscPREF64 *p64;
+        SET_FOREACH(p64, link->ndisc_pref64) {
+                if (!in6_addr_equal(&p64->router, &original_address))
+                        continue;
+
+                p64->router = current_address;
+        }
+
+        return 0;
+}
+
+static int ndisc_neighbor_handler(Link *link, sd_ndisc_neighbor *na) {
+        int r;
+
+        assert(link);
+        assert(na);
+
+        r = sd_ndisc_neighbor_is_router(na);
+        if (r < 0)
+                return r;
+        if (r == 0)
+                r = ndisc_neighbor_handle_non_router_message(link, na);
+        else
+                r = ndisc_neighbor_handle_router_message(link, na);
+        if (r < 0)
+                return r;
+
+        return 0;
+}
+
 static void ndisc_handler(sd_ndisc *nd, sd_ndisc_event_t event, void *message, void *userdata) {
         Link *link = ASSERT_PTR(userdata);
         int r;
@@ -1696,6 +2313,23 @@ static void ndisc_handler(sd_ndisc *nd, sd_ndisc_event_t event, void *message, v
                 }
                 break;
 
+        case SD_NDISC_EVENT_NEIGHBOR:
+                r = ndisc_neighbor_handler(link, ASSERT_PTR(message));
+                if (r < 0 && r != -EBADMSG) {
+                        link_enter_failed(link);
+                        return;
+                }
+                break;
+
+        case SD_NDISC_EVENT_REDIRECT:
+                r = ndisc_redirect_handler(link, ASSERT_PTR(message));
+                if (r < 0 && r != -EBADMSG) {
+                        log_link_warning_errno(link, r, "Failed to process Redirect message: %m");
+                        link_enter_failed(link);
+                        return;
+                }
+                break;
+
         case SD_NDISC_EVENT_TIMEOUT:
                 log_link_debug(link, "NDisc handler get timeout event");
                 if (link->ndisc_messages == 0) {
@@ -1825,12 +2459,15 @@ void ndisc_flush(Link *link) {
         assert(link);
 
         /* Remove all addresses, routes, RDNSS, DNSSL, and Captive Portal entries, without exception. */
-        (void) ndisc_drop_outdated(link, /* timestamp_usec = */ USEC_INFINITY);
+        (void) ndisc_drop_outdated(link, /* router = */ NULL, /* timestamp_usec = */ USEC_INFINITY);
+        (void) ndisc_drop_redirect(link, /* router = */ NULL);
 
         link->ndisc_rdnss = set_free(link->ndisc_rdnss);
         link->ndisc_dnssl = set_free(link->ndisc_dnssl);
         link->ndisc_captive_portals = set_free(link->ndisc_captive_portals);
         link->ndisc_pref64 = set_free(link->ndisc_pref64);
+        link->ndisc_redirects = set_free(link->ndisc_redirects);
+        link->ndisc_mtu = 0;
 }
 
 static const char* const ndisc_start_dhcp6_client_table[_IPV6_ACCEPT_RA_START_DHCP6_CLIENT_MAX] = {
index 2766f5e4350d8ba0569e2d59c65277ca10104dc1..019fe4da425a0a7c2d2ff14ab19bbe3f102e0728 100644 (file)
@@ -4,6 +4,7 @@
 #include "conf-parser.h"
 #include "time-util.h"
 
+typedef struct Address Address;
 typedef struct Link Link;
 typedef struct Network Network;
 
@@ -61,6 +62,7 @@ int ndisc_stop(Link *link);
 void ndisc_flush(Link *link);
 
 int link_request_ndisc(Link *link);
+int ndisc_reconfigure_address(Address *address, Link *link);
 
 CONFIG_PARSER_PROTOTYPE(config_parse_ndisc_start_dhcp6_client);
 CONFIG_PARSER_PROTOTYPE(config_parse_ndisc_use_domains);
index dffc40308c7966f84fa8f501859a457a12a7f647..2f9855465fe5b63db1701eef34eb6877cfa3eb5a 100644 (file)
@@ -292,6 +292,7 @@ DHCPv6.RapidCommit,                          config_parse_bool,
 DHCPv6.NetLabel,                             config_parse_string,                                      CONFIG_PARSE_STRING_SAFE,      offsetof(Network, dhcp6_netlabel)
 DHCPv6.SendRelease,                          config_parse_bool,                                        0,                             offsetof(Network, dhcp6_send_release)
 DHCPv6.NFTSet,                               config_parse_nft_set,                                     NFT_SET_PARSE_NETWORK,         offsetof(Network, dhcp6_nft_set_context)
+IPv6AcceptRA.UseRedirect,                    config_parse_bool,                                        0,                             offsetof(Network, ndisc_use_redirect)
 IPv6AcceptRA.UseGateway,                     config_parse_bool,                                        0,                             offsetof(Network, ndisc_use_gateway)
 IPv6AcceptRA.UseRoutePrefix,                 config_parse_bool,                                        0,                             offsetof(Network, ndisc_use_route_prefix)
 IPv6AcceptRA.UseAutonomousPrefix,            config_parse_bool,                                        0,                             offsetof(Network, ndisc_use_autonomous_prefix)
@@ -350,6 +351,7 @@ DHCPServer.BootServerAddress,                config_parse_in_addr_non_null,
 DHCPServer.BootServerName,                   config_parse_dns_name,                                    0,                             offsetof(Network, dhcp_server_boot_server_name)
 DHCPServer.BootFilename,                     config_parse_string,                                      CONFIG_PARSE_STRING_SAFE_AND_ASCII, offsetof(Network, dhcp_server_boot_filename)
 DHCPServer.RapidCommit,                      config_parse_bool,                                        0,                             offsetof(Network, dhcp_server_rapid_commit)
+DHCPServer.PersistLeases,                    config_parse_tristate,                                    0,                             offsetof(Network, dhcp_server_persist_leases)
 DHCPServerStaticLease.Address,               config_parse_dhcp_static_lease_address,                   0,                             0
 DHCPServerStaticLease.MACAddress,            config_parse_dhcp_static_lease_hwaddr,                    0,                             0
 Bridge.Cost,                                 config_parse_uint32,                                      0,                             offsetof(Network, cost)
index 3d418006d671c78474b1770cad4e10dd492103a8..833f1bd059c8d9cf5c26000fe57f1ec1eaa9db2e 100644 (file)
@@ -1,5 +1,6 @@
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 
+/* Make sure the net/if.h header is included before any linux/ one */
 #include <net/if.h>
 #include <netinet/in.h>
 #include <linux/netdevice.h>
@@ -387,6 +388,7 @@ int network_load_one(Manager *manager, OrderedHashmap **networks, const char *fi
                 .dhcp_use_captive_portal = true,
                 .dhcp_use_dns = true,
                 .dhcp_routes_to_dns = true,
+                .dhcp_use_domains = manager->dhcp_use_domains,
                 .dhcp_use_hostname = true,
                 .dhcp_use_routes = true,
                 .dhcp_use_gateway = -1,
@@ -403,6 +405,7 @@ int network_load_one(Manager *manager, OrderedHashmap **networks, const char *fi
                 .dhcp6_use_address = true,
                 .dhcp6_use_pd_prefix = true,
                 .dhcp6_use_dns = true,
+                .dhcp6_use_domains = manager->dhcp6_use_domains,
                 .dhcp6_use_hostname = true,
                 .dhcp6_use_ntp = true,
                 .dhcp6_use_captive_portal = true,
@@ -426,6 +429,7 @@ int network_load_one(Manager *manager, OrderedHashmap **networks, const char *fi
                 .dhcp_server_emit_router = true,
                 .dhcp_server_emit_timezone = true,
                 .dhcp_server_rapid_commit = true,
+                .dhcp_server_persist_leases = -1,
 
                 .router_lifetime_usec = RADV_DEFAULT_ROUTER_LIFETIME_USEC,
                 .router_dns_lifetime_usec = RADV_DEFAULT_VALID_LIFETIME_USEC,
@@ -473,6 +477,7 @@ int network_load_one(Manager *manager, OrderedHashmap **networks, const char *fi
                 .ipv4_rp_filter = _IP_REVERSE_PATH_FILTER_INVALID,
 
                 .ndisc = -1,
+                .ndisc_use_redirect = true,
                 .ndisc_use_dns = true,
                 .ndisc_use_gateway = true,
                 .ndisc_use_captive_portal = true,
index 467789bd68ac845135af0747c9a8ece274726967..eacf3a3dd614ead0be852e013cc41d34d6821e22 100644 (file)
@@ -230,6 +230,7 @@ struct Network {
         char *dhcp_server_boot_filename;
         usec_t dhcp_server_ipv6_only_preferred_usec;
         bool dhcp_server_rapid_commit;
+        int dhcp_server_persist_leases;
 
         /* link-local addressing support */
         AddressFamily link_local;
@@ -336,6 +337,7 @@ struct Network {
 
         /* NDisc support */
         int ndisc;
+        bool ndisc_use_redirect;
         bool ndisc_use_dns;
         bool ndisc_use_gateway;
         bool ndisc_use_route_prefix;
index b07feede2ba5140ee684487284c7a7f037e6793a..1b44ef320c9dfaff2d08a0df3d51b67bcce37091 100644 (file)
@@ -2,6 +2,7 @@
  * Copyright © 2019 VMware, Inc.
  */
 
+/* Make sure the net/if.h header is included before any linux/ one */
 #include <net/if.h>
 #include <linux/nexthop.h>
 
index 7fd97e7206ffd4ab4168321668eeb909ce8b6791..b137b9384f7f56746d0a56d5ae1feebd00bb1525 100644 (file)
@@ -243,9 +243,6 @@ int link_request_radv_addresses(Link *link) {
                 return 0;
 
         HASHMAP_FOREACH(p, link->network->prefixes_by_section) {
-                _cleanup_set_free_ Set *addresses = NULL;
-                struct in6_addr *a;
-
                 if (!p->assign)
                         continue;
 
@@ -253,11 +250,14 @@ int link_request_radv_addresses(Link *link) {
                 if (p->prefixlen > 64)
                         continue;
 
-                r = radv_generate_addresses(link, p->tokens, &p->prefix, p->prefixlen, &addresses);
+                _cleanup_hashmap_free_ Hashmap *tokens_by_address = NULL;
+                r = radv_generate_addresses(link, p->tokens, &p->prefix, p->prefixlen, &tokens_by_address);
                 if (r < 0)
                         return r;
 
-                SET_FOREACH(a, addresses) {
+                IPv6Token *token;
+                struct in6_addr *a;
+                HASHMAP_FOREACH_KEY(token, a, tokens_by_address) {
                         _cleanup_(address_unrefp) Address *address = NULL;
 
                         r = address_new(&address);
@@ -269,6 +269,7 @@ int link_request_radv_addresses(Link *link) {
                         address->in_addr.in6 = *a;
                         address->prefixlen = p->prefixlen;
                         address->route_metric = p->route_metric;
+                        address->token = ipv6_token_ref(token);
 
                         r = link_request_static_address(link, address);
                         if (r < 0)
@@ -279,6 +280,29 @@ int link_request_radv_addresses(Link *link) {
         return 0;
 }
 
+int link_reconfigure_radv_address(Address *address, Link *link) {
+        int r;
+
+        assert(address);
+        assert(address->source == NETWORK_CONFIG_SOURCE_STATIC);
+        assert(link);
+
+        r = regenerate_address(address, link);
+        if (r <= 0)
+                return r;
+
+        r = link_request_static_address(link, address);
+        if (r < 0)
+                return r;
+
+        if (link->static_address_messages != 0) {
+                link->static_addresses_configured = false;
+                link_set_state(link, LINK_STATE_CONFIGURING);
+        }
+
+        return 0;
+}
+
 static int radv_set_prefix(Link *link, Prefix *prefix) {
         _cleanup_(sd_radv_prefix_unrefp) sd_radv_prefix *p = NULL;
         int r;
@@ -816,12 +840,6 @@ static int prefix_section_verify(Prefix *p) {
                 p->assign = false;
         }
 
-        if (p->valid_lifetime == 0)
-                return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
-                                         "%s: The valid lifetime of prefix cannot be zero. "
-                                         "Ignoring [IPv6Prefix] section from line %u.",
-                                         p->section->filename, p->section->line);
-
         if (p->preferred_lifetime > p->valid_lifetime)
                 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
                                          "%s: The preferred lifetime %s is longer than the valid lifetime %s. "
@@ -854,12 +872,6 @@ static int route_prefix_section_verify(RoutePrefix *p) {
                                          "Valid range is 0…128. Ignoring [IPv6RoutePrefix] section from line %u.",
                                          p->section->filename, p->prefixlen, p->section->line);
 
-        if (p->lifetime == 0)
-                return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
-                                         "%s: The lifetime of route cannot be zero. "
-                                         "Ignoring [IPv6RoutePrefix] section from line %u.",
-                                         p->section->filename, p->section->line);
-
         return 0;
 }
 
@@ -1214,7 +1226,7 @@ int config_parse_pref64_prefix(
                 return 0;
        }
 
-        (void) in6_addr_mask(&a.in6,prefixlen);
+        (void) in6_addr_mask(&a.in6, prefixlen);
         p->prefix = a.in6;
         p->prefixlen = prefixlen;
 
index 48677b50de4b775829c6debe272d17c8e67fdaab..94834e77a8e0d10a91335ba3f4dc262f31b09233 100644 (file)
@@ -71,6 +71,7 @@ void network_drop_invalid_pref64_prefixes(Network *network);
 void network_adjust_radv(Network *network);
 
 int link_request_radv_addresses(Link *link);
+int link_reconfigure_radv_address(Address *address, Link *link);
 
 bool link_radv_enabled(Link *link);
 int radv_start(Link *link);
index bb757eed71fbfbc1366d84a53c105384613509e8..886b4da8e0a774207d150254982177a6b17e8fe6 100644 (file)
@@ -1,5 +1,6 @@
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 
+/* Make sure the net/if.h header is included before any linux/ one */
 #include <net/if.h>
 #include <linux/fib_rules.h>
 
index eb65429a783ff0b6d31b5931baf8cbb1dc5d7f78..058bc00ba10b24f78a8d19d3db5beaa429ddc426 100644 (file)
@@ -173,19 +173,7 @@ static int link_unset_master_handler(sd_netlink *rtnl, sd_netlink_message *m, Re
 }
 
 static int link_set_mtu_handler(sd_netlink *rtnl, sd_netlink_message *m, Request *req, Link *link, void *userdata) {
-        int r;
-
-        r = set_link_handler_internal(rtnl, m, req, link, /* ignore = */ true, get_link_default_handler);
-        if (r <= 0)
-                return r;
-
-        /* The kernel resets ipv6 mtu after changing device mtu;
-         * we must set this here, after we've set device mtu */
-        r = link_set_ipv6_mtu(link);
-        if (r < 0)
-                log_link_warning_errno(link, r, "Failed to set IPv6 MTU, ignoring: %m");
-
-        return 0;
+        return set_link_handler_internal(rtnl, m, req, link, /* ignore = */ true, get_link_default_handler);
 }
 
 static int link_configure_fill_message(
@@ -453,6 +441,43 @@ static bool netdev_is_ready(NetDev *netdev) {
         return true;
 }
 
+static uint32_t link_adjust_mtu(Link *link, uint32_t mtu) {
+        const char *origin;
+        uint32_t min_mtu;
+
+        assert(link);
+        assert(link->network);
+
+        min_mtu = link->min_mtu;
+        origin = "the minimum MTU of the interface";
+        if (link_ipv6_enabled(link)) {
+                /* IPv6 protocol requires a minimum MTU of IPV6_MTU_MIN(1280) bytes on the interface. Bump up
+                 * MTU bytes to IPV6_MTU_MIN. */
+                if (min_mtu < IPV6_MIN_MTU) {
+                        min_mtu = IPV6_MIN_MTU;
+                        origin = "the minimum IPv6 MTU";
+                }
+                if (min_mtu < link->network->ipv6_mtu) {
+                        min_mtu = link->network->ipv6_mtu;
+                        origin = "the requested IPv6 MTU in IPv6MTUBytes=";
+                }
+        }
+
+        if (mtu < min_mtu) {
+                log_link_warning(link, "Bumping the requested MTU %"PRIu32" to %s (%"PRIu32")",
+                                 mtu, origin, min_mtu);
+                mtu = min_mtu;
+        }
+
+        if (mtu > link->max_mtu) {
+                log_link_warning(link, "Reducing the requested MTU %"PRIu32" to the interface's maximum MTU %"PRIu32".",
+                                 mtu, link->max_mtu);
+                mtu = link->max_mtu;
+        }
+
+        return mtu;
+}
+
 static int link_is_ready_to_set_link(Link *link, Request *req) {
         int r;
 
@@ -570,13 +595,24 @@ static int link_is_ready_to_set_link(Link *link, Request *req) {
                                          }))
                         return false;
 
-                /* Changing FD mode may affect MTU. */
+                /* Changing FD mode may affect MTU.
+                 * See https://docs.kernel.org/networking/can.html#can-fd-flexible-data-rate-driver-support
+                 *   MTU = 16 (CAN_MTU)   => Classical CAN device
+                 *   MTU = 72 (CANFD_MTU) => CAN FD capable device */
                 if (ordered_set_contains(link->manager->request_queue,
                                          &(const Request) {
                                                  .link = link,
                                                  .type = REQUEST_TYPE_SET_LINK_CAN,
                                          }))
                         return false;
+
+                /* Now, it is ready to set MTU, but before setting, adjust requested MTU. */
+                uint32_t mtu = link_adjust_mtu(link, PTR_TO_UINT32(req->userdata));
+                if (mtu == link->mtu)
+                        return -EALREADY; /* Not necessary to set the same value. */
+
+                req->userdata = UINT32_TO_PTR(mtu);
+                return true;
         }
         default:
                 break;
@@ -865,51 +901,12 @@ int link_request_to_set_master(Link *link) {
 }
 
 int link_request_to_set_mtu(Link *link, uint32_t mtu) {
-        const char *origin;
-        uint32_t min_mtu, max_mtu;
         Request *req;
         int r;
 
         assert(link);
-        assert(link->network);
-
-        min_mtu = link->min_mtu;
-        origin = "the minimum MTU of the interface";
-        if (link_ipv6_enabled(link)) {
-                /* IPv6 protocol requires a minimum MTU of IPV6_MTU_MIN(1280) bytes on the interface. Bump up
-                 * MTU bytes to IPV6_MTU_MIN. */
-                if (min_mtu < IPV6_MIN_MTU) {
-                        min_mtu = IPV6_MIN_MTU;
-                        origin = "the minimum IPv6 MTU";
-                }
-                if (min_mtu < link->network->ipv6_mtu) {
-                        min_mtu = link->network->ipv6_mtu;
-                        origin = "the requested IPv6 MTU in IPv6MTUBytes=";
-                }
-        }
-
-        if (mtu < min_mtu) {
-                log_link_warning(link, "Bumping the requested MTU %"PRIu32" to %s (%"PRIu32")",
-                                 mtu, origin, min_mtu);
-                mtu = min_mtu;
-        }
-
-        max_mtu = link->max_mtu;
-        if (link->iftype == ARPHRD_CAN)
-                /* The maximum MTU may be changed when FD mode is changed.
-                 * See https://docs.kernel.org/networking/can.html#can-fd-flexible-data-rate-driver-support
-                 *   MTU = 16 (CAN_MTU)   => Classical CAN device
-                 *   MTU = 72 (CANFD_MTU) => CAN FD capable device
-                 * So, even if the current maximum is 16, we should not reduce the requested value now. */
-                max_mtu = MAX(max_mtu, 72u);
-
-        if (mtu > max_mtu) {
-                log_link_warning(link, "Reducing the requested MTU %"PRIu32" to the interface's maximum MTU %"PRIu32".",
-                                 mtu, max_mtu);
-                mtu = max_mtu;
-        }
 
-        if (link->mtu == mtu)
+        if (mtu == 0)
                 return 0;
 
         r = link_request_set_link(link, REQUEST_TYPE_SET_LINK_MTU,
index 117db0af6c30590ee00b76e25c41ea78807cb0ac..b1bbe136097b8d8289596a7cbb7bf69d4e6b6f82 100644 (file)
@@ -127,7 +127,7 @@ static int link_put_dns(Link *link, OrderedSet **s) {
                 NDiscRDNSS *a;
 
                 SET_FOREACH(a, link->ndisc_rdnss) {
-                        r = ordered_set_put_in6_addrv(s, &a->router, 1);
+                        r = ordered_set_put_in6_addrv(s, &a->address, 1);
                         if (r < 0)
                                 return r;
                 }
@@ -190,7 +190,7 @@ static int link_put_sip(Link *link, OrderedSet **s) {
         assert(link->network);
         assert(s);
 
-        if (link->dhcp_lease && link->network->dhcp_use_ntp) {
+        if (link->dhcp_lease && link->network->dhcp_use_sip) {
                 const struct in_addr *addresses;
 
                 r = sd_dhcp_lease_get_sip(link->dhcp_lease, &addresses);
index f9db1f7f4af16c4c3085231d9f7aa234ebe33708..68c23e0eb7962f51693bb0531c8d46cf7973892a 100644 (file)
@@ -250,22 +250,28 @@ static int link_set_ipv6_proxy_ndp(Link *link) {
         return sysctl_write_ip_property_boolean(AF_INET6, link->ifname, "proxy_ndp", v);
 }
 
-int link_set_ipv6_mtu(Link *link) {
-        uint32_t mtu;
+int link_set_ipv6_mtu(Link *link, int log_level) {
+        uint32_t mtu = 0;
 
         assert(link);
 
         if (!link_is_configured_for_family(link, AF_INET6))
                 return 0;
 
-        if (link->network->ipv6_mtu == 0)
+        assert(link->network);
+
+        if (link->network->ndisc_use_mtu)
+                mtu = link->ndisc_mtu;
+        if (mtu == 0)
+                mtu = link->network->ipv6_mtu;
+        if (mtu == 0)
                 return 0;
 
-        mtu = link->network->ipv6_mtu;
-        if (mtu > link->max_mtu) {
-                log_link_warning(link, "Reducing requested IPv6 MTU %"PRIu32" to the interface's maximum MTU %"PRIu32".",
-                                 mtu, link->max_mtu);
-                mtu = link->max_mtu;
+        if (mtu > link->mtu) {
+                log_link_full(link, log_level,
+                              "Reducing requested IPv6 MTU %"PRIu32" to the interface's maximum MTU %"PRIu32".",
+                              mtu, link->mtu);
+                mtu = link->mtu;
         }
 
         return sysctl_write_ip_property_uint32(AF_INET6, link->ifname, "mtu", mtu);
@@ -355,7 +361,7 @@ int link_set_sysctl(Link *link) {
         if (r < 0)
                 log_link_warning_errno(link, r, "Cannot set IPv6 proxy NDP, ignoring: %m");
 
-        r = link_set_ipv6_mtu(link);
+        r = link_set_ipv6_mtu(link, LOG_INFO);
         if (r < 0)
                 log_link_warning_errno(link, r, "Cannot set IPv6 MTU, ignoring: %m");
 
index a47dda015defbb86579f2a04e89b63dea05dfbcd..d7a9b1f3201587e4209fb9fe15e445adcca5b691 100644 (file)
@@ -31,7 +31,7 @@ void manager_set_sysctl(Manager *manager);
 
 int link_get_ip_forwarding(Link *link, int family);
 int link_set_sysctl(Link *link);
-int link_set_ipv6_mtu(Link *link);
+int link_set_ipv6_mtu(Link *link, int log_level);
 
 const char* ipv6_privacy_extensions_to_string(IPv6PrivacyExtensions i) _const_;
 IPv6PrivacyExtensions ipv6_privacy_extensions_from_string(const char *s) _pure_;
index 2994b8b70c1c14e8341810484cda81c9ad4711fc..828dbb96388a6230daf564e62da3ab64d6c0d07a 100644 (file)
 [DHCPv4]
 #DUIDType=vendor
 #DUIDRawData=
+#UseDomains=no
 
 [DHCPv6]
 #DUIDType=vendor
 #DUIDRawData=
+#UseDomains=no
+
+[DHCPServer]
+#PersistLeases=yes
index 75bcbbf01d9e0a91c702f02a6cc2d32c1c16c422..55ce16600a73e9f261a34ac9aea8834cc7b2fe3d 100644 (file)
@@ -293,14 +293,20 @@ QDisc* qdisc_drop(QDisc *qdisc) {
 
         link = ASSERT_PTR(qdisc->link);
 
+        qdisc_mark(qdisc); /* To avoid stack overflow. */
+
         /* also drop all child classes assigned to the qdisc. */
         SET_FOREACH(tclass, link->tclasses) {
+                if (tclass_is_marked(tclass))
+                        continue;
+
                 if (TC_H_MAJ(tclass->classid) != qdisc->handle)
                         continue;
 
                 tclass_drop(tclass);
         }
 
+        qdisc_unmark(qdisc);
         qdisc_enter_removed(qdisc);
 
         if (qdisc->state == 0) {
index ab8f79ac5bf11f483cfe02f1dd5135db4a751d03..63229ec6e8df1eec44ec5c99055c19e2df32128d 100644 (file)
@@ -260,14 +260,20 @@ TClass* tclass_drop(TClass *tclass) {
 
         link = ASSERT_PTR(tclass->link);
 
+        tclass_mark(tclass); /* To avoid stack overflow. */
+
         /* Also drop all child qdiscs assigned to the class. */
         SET_FOREACH(qdisc, link->qdiscs) {
+                if (qdisc_is_marked(qdisc))
+                        continue;
+
                 if (qdisc->parent != tclass->classid)
                         continue;
 
                 qdisc_drop(qdisc);
         }
 
+        tclass_unmark(tclass);
         tclass_enter_removed(tclass);
 
         if (tclass->state == 0) {
index 564ca096099ada89acfa95ac4f2f17555e88b929..96f5b4d705aedb460434f4bb9ccf35523981cf95 100644 (file)
@@ -1,5 +1,9 @@
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 
+/* Make sure the net/if.h header is included before any linux/ one */
+#include <net/if.h>
+#include <linux/if.h>
+
 #include "bond.h"
 #include "dhcp6-internal.h"
 #include "dhcp6-protocol.h"
index c4a784fd643d0a723e02f3a44c974cd96870f984..4f28b4a225549c9b3a564b27ef29e0c0de84d3da 100644 (file)
@@ -13,6 +13,7 @@
 #include "mountpoint-util.h"
 #include "nspawn-cgroup.h"
 #include "nspawn-mount.h"
+#include "nsresource.h"
 #include "path-util.h"
 #include "rm-rf.h"
 #include "string-util.h"
@@ -46,38 +47,6 @@ static int chown_cgroup_path(const char *path, uid_t uid_shift) {
         return 0;
 }
 
-int chown_cgroup(pid_t pid, CGroupUnified unified_requested, uid_t uid_shift) {
-        _cleanup_free_ char *path = NULL, *fs = NULL;
-        int r;
-
-        r = cg_pid_get_path(NULL, pid, &path);
-        if (r < 0)
-                return log_error_errno(r, "Failed to get container cgroup path: %m");
-
-        r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, path, NULL, &fs);
-        if (r < 0)
-                return log_error_errno(r, "Failed to get file system path for container cgroup: %m");
-
-        r = chown_cgroup_path(fs, uid_shift);
-        if (r < 0)
-                return log_error_errno(r, "Failed to chown() cgroup %s: %m", fs);
-
-        if (unified_requested == CGROUP_UNIFIED_SYSTEMD || (unified_requested == CGROUP_UNIFIED_NONE && cg_unified_controller(SYSTEMD_CGROUP_CONTROLLER) > 0)) {
-                _cleanup_free_ char *lfs = NULL;
-                /* Always propagate access rights from unified to legacy controller */
-
-                r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER_LEGACY, path, NULL, &lfs);
-                if (r < 0)
-                        return log_error_errno(r, "Failed to get file system path for container cgroup: %m");
-
-                r = chown_cgroup_path(lfs, uid_shift);
-                if (r < 0)
-                        return log_error_errno(r, "Failed to chown() cgroup %s: %m", lfs);
-        }
-
-        return 0;
-}
-
 int sync_cgroup(pid_t pid, CGroupUnified unified_requested, uid_t uid_shift) {
         _cleanup_free_ char *cgroup = NULL;
         char tree[] = "/tmp/unifiedXXXXXX", pid_string[DECIMAL_STR_MAX(pid) + 1];
@@ -142,7 +111,14 @@ finish:
         return r;
 }
 
-int create_subcgroup(pid_t pid, bool keep_unit, CGroupUnified unified_requested) {
+int create_subcgroup(
+                pid_t pid,
+                bool keep_unit,
+                CGroupUnified unified_requested,
+                uid_t uid_shift,
+                int userns_fd,
+                bool privileged) {
+
         _cleanup_free_ char *cgroup = NULL, *payload = NULL;
         CGroupMask supported;
         char *e;
@@ -185,13 +161,54 @@ int create_subcgroup(pid_t pid, bool keep_unit, CGroupUnified unified_requested)
         if (!payload)
                 return log_oom();
 
-        r = cg_create_and_attach(SYSTEMD_CGROUP_CONTROLLER, payload, pid);
+        if (privileged)
+                r = cg_create_and_attach(SYSTEMD_CGROUP_CONTROLLER, payload, pid);
+        else
+                r = cg_create(SYSTEMD_CGROUP_CONTROLLER, payload);
         if (r < 0)
                 return log_error_errno(r, "Failed to create %s subcgroup: %m", payload);
 
+        if (privileged) {
+                _cleanup_free_ char *fs = NULL;
+                r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, payload, NULL, &fs);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to get file system path for container cgroup: %m");
+
+                r = chown_cgroup_path(fs, uid_shift);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to chown() cgroup %s: %m", fs);
+
+        } else if (userns_fd >= 0) {
+                _cleanup_close_ int cgroup_fd = -EBADF;
+
+                cgroup_fd = cg_path_open(SYSTEMD_CGROUP_CONTROLLER, payload);
+                if (cgroup_fd < 0)
+                        return log_error_errno(cgroup_fd, "Failed to open cgroup %s: %m", payload);
+
+                r = cg_fd_attach(cgroup_fd, pid);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to add process " PID_FMT " to cgroup %s: %m", pid, payload);
+
+                r = nsresource_add_cgroup(userns_fd, cgroup_fd);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to add cgroup %s to userns: %m", payload);
+        }
+
+        if (unified_requested == CGROUP_UNIFIED_SYSTEMD || (unified_requested == CGROUP_UNIFIED_NONE && cg_unified_controller(SYSTEMD_CGROUP_CONTROLLER) > 0)) {
+                _cleanup_free_ char *lfs = NULL;
+                /* Always propagate access rights from unified to legacy controller */
+
+                r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER_LEGACY, payload, NULL, &lfs);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to get file system path for container cgroup: %m");
+
+                r = chown_cgroup_path(lfs, uid_shift);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to chown() cgroup %s: %m", lfs);
+        }
+
         if (keep_unit) {
                 _cleanup_free_ char *supervisor = NULL;
-
                 supervisor = path_join(cgroup, "supervisor");
                 if (!supervisor)
                         return log_oom();
index 3f5ba622d8c6d0d54bfe87b1ca502a904726f401..7e2cd53ddccc56b0143be896bb352cdfd6ae2204 100644 (file)
@@ -6,9 +6,8 @@
 
 #include "cgroup-util.h"
 
-int chown_cgroup(pid_t pid, CGroupUnified unified_requested, uid_t uid_shift);
 int sync_cgroup(pid_t pid, CGroupUnified unified_requested, uid_t uid_shift);
-int create_subcgroup(pid_t pid, bool keep_unit, CGroupUnified unified_requested);
+int create_subcgroup(pid_t pid, bool keep_unit, CGroupUnified unified_requested, uid_t uid_shift, int userns_fd, bool privileged);
 
 int mount_cgroups(const char *dest, CGroupUnified unified_requested, bool userns, uid_t uid_shift, uid_t uid_range, const char *selinux_apifs_context, bool use_cgns);
 int mount_systemd_cgroup_writable(const char *dest, CGroupUnified unified_requested);
index e94ffd799ed57c235265cf41fca595a9d2eddbdf..c2bd4f6c307f4c4606318bcfdb5ee5a830fb0e05 100644 (file)
@@ -444,22 +444,38 @@ int tmpfs_patch_options(
 }
 
 int mount_sysfs(const char *dest, MountSettingsMask mount_settings) {
-        const char *full, *top;
-        int r;
+        _cleanup_free_ char *top = NULL, *full = NULL;;
         unsigned long extra_flags = 0;
+        int r;
 
-        top = prefix_roota(dest, "/sys");
-        r = path_is_fs_type(top, SYSFS_MAGIC);
+        top = path_join(dest, "/sys");
+        if (!top)
+                return log_oom();
+
+        r = path_is_mount_point(top);
         if (r < 0)
-                return log_error_errno(r, "Failed to determine filesystem type of %s: %m", top);
-        /* /sys might already be mounted as sysfs by the outer child in the
-         * !netns case. In this case, it's all good. Don't touch it because we
-         * don't have the right to do so, see https://github.com/systemd/systemd/issues/1555.
-         */
-        if (r > 0)
-                return 0;
+                return log_error_errno(r, "Failed to determine if '%s' is a mountpoint: %m", top);
+        if (r == 0) {
+                /* If this is not a mount point yet, then mount a tmpfs there */
+                r = mount_nofollow_verbose(LOG_ERR, "tmpfs", top, "tmpfs", MS_NOSUID|MS_NOEXEC|MS_NODEV, "mode=0555" TMPFS_LIMITS_SYS);
+                if (r < 0)
+                        return r;
+        } else {
+                r = path_is_fs_type(top, SYSFS_MAGIC);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to determine filesystem type of %s: %m", top);
+
+                /* /sys/ might already be mounted as sysfs by the outer child in the !netns case. In this case, it's
+                 * all good. Don't touch it because we don't have the right to do so, see
+                 * https://github.com/systemd/systemd/issues/1555.
+                 */
+                if (r > 0)
+                        return 0;
+        }
 
-        full = prefix_roota(top, "/full");
+        full = path_join(top, "/full");
+        if (!full)
+                return log_oom();
 
         (void) mkdir(full, 0755);
 
@@ -501,10 +517,11 @@ int mount_sysfs(const char *dest, MountSettingsMask mount_settings) {
         if (rmdir(full) < 0)
                 return log_error_errno(errno, "Failed to remove %s: %m", full);
 
-        /* Create mountpoint for cgroups. Otherwise we are not allowed since we
-         * remount /sys read-only.
-         */
-        const char *x = prefix_roota(top, "/fs/cgroup");
+        /* Create mountpoint for cgroups. Otherwise we are not allowed since we remount /sys/ read-only. */
+        _cleanup_free_ char *x = path_join(top, "/fs/cgroup");
+        if (!x)
+                return log_oom();
+
         (void) mkdir_p(x, 0755);
 
         return mount_nofollow_verbose(LOG_ERR, NULL, top, NULL,
@@ -541,7 +558,7 @@ int mount_all(const char *dest,
         } MountPoint;
 
         static const MountPoint mount_table[] = {
-                /* First we list inner child mounts (i.e. mounts applied *after* entering user namespacing) */
+                /* First we list inner child mounts (i.e. mounts applied *after* entering user namespacing when we are privileged) */
                 { "proc",            "/proc",           "proc",  NULL,        PROC_DEFAULT_MOUNT_FLAGS,
                   MOUNT_FATAL|MOUNT_IN_USERNS|MOUNT_MKDIR|MOUNT_FOLLOW_SYMLINKS }, /* we follow symlinks here since not following them requires /proc/ already being mounted, which we don't have here. */
 
@@ -575,15 +592,15 @@ int mount_all(const char *dest,
                 { "mqueue",                 "/dev/mqueue",                  "mqueue", NULL,                            MS_NOSUID|MS_NOEXEC|MS_NODEV,
                   MOUNT_IN_USERNS|MOUNT_MKDIR },
 
-                /* Then we list outer child mounts (i.e. mounts applied *before* entering user namespacing) */
+                /* Then we list outer child mounts (i.e. mounts applied *before* entering user namespacing when we are privileged) */
                 { "tmpfs",                  "/tmp",                         "tmpfs", "mode=01777" NESTED_TMPFS_LIMITS, MS_NOSUID|MS_NODEV|MS_STRICTATIME,
                   MOUNT_FATAL|MOUNT_APPLY_TMPFS_TMP|MOUNT_MKDIR },
                 { "tmpfs",                  "/sys",                         "tmpfs", "mode=0555" TMPFS_LIMITS_SYS,     MS_NOSUID|MS_NOEXEC|MS_NODEV,
-                  MOUNT_FATAL|MOUNT_APPLY_APIVFS_NETNS|MOUNT_MKDIR },
+                  MOUNT_FATAL|MOUNT_APPLY_APIVFS_NETNS|MOUNT_MKDIR|MOUNT_PRIVILEGED },
                 { "sysfs",                  "/sys",                         "sysfs", NULL,                             SYS_DEFAULT_MOUNT_FLAGS,
-                  MOUNT_FATAL|MOUNT_APPLY_APIVFS_RO|MOUNT_MKDIR },    /* skipped if above was mounted */
+                  MOUNT_FATAL|MOUNT_APPLY_APIVFS_RO|MOUNT_MKDIR|MOUNT_PRIVILEGED },    /* skipped if above was mounted */
                 { "sysfs",                  "/sys",                         "sysfs", NULL,                             MS_NOSUID|MS_NOEXEC|MS_NODEV,
-                  MOUNT_FATAL|MOUNT_MKDIR },                          /* skipped if above was mounted */
+                  MOUNT_FATAL|MOUNT_MKDIR|MOUNT_PRIVILEGED },                          /* skipped if above was mounted */
                 { "tmpfs",                  "/dev",                         "tmpfs", "mode=0755" TMPFS_LIMITS_PRIVATE_DEV, MS_NOSUID|MS_STRICTATIME,
                   MOUNT_FATAL|MOUNT_MKDIR },
                 { "tmpfs",                  "/dev/shm",                     "tmpfs", "mode=01777" NESTED_TMPFS_LIMITS, MS_NOSUID|MS_NODEV|MS_STRICTATIME,
@@ -604,11 +621,11 @@ int mount_all(const char *dest,
                   MOUNT_FATAL|MOUNT_IN_USERNS },
 #if HAVE_SELINUX
                 { "/sys/fs/selinux",        "/sys/fs/selinux",              NULL,    NULL,                             MS_BIND,
-                  MOUNT_MKDIR },  /* Bind mount first (mkdir/chown the mount point in case /sys/ is mounted as minimal skeleton tmpfs) */
+                  MOUNT_MKDIR|MOUNT_PRIVILEGED },  /* Bind mount first (mkdir/chown the mount point in case /sys/ is mounted as minimal skeleton tmpfs) */
                 { NULL,                     "/sys/fs/selinux",              NULL,    NULL,                             MS_BIND|MS_RDONLY|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_REMOUNT,
-                  0 },            /* Then, make it r/o (don't mkdir/chown the mount point here, the previous entry already did that) */
+                  MOUNT_PRIVILEGED },              /* Then, make it r/o (don't mkdir/chown the mount point here, the previous entry already did that) */
                 { NULL,                     "/sys/fs/selinux",              NULL,    NULL,                             MS_PRIVATE,
-                  0 },            /* Turn off propagation (we only want that for the mount propagation tunnel dir) */
+                  MOUNT_PRIVILEGED },              /* Turn off propagation (we only want that for the mount propagation tunnel dir) */
 #endif
         };
 
@@ -617,6 +634,7 @@ int mount_all(const char *dest,
         bool ro = FLAGS_SET(mount_settings, MOUNT_APPLY_APIVFS_RO);
         bool in_userns = FLAGS_SET(mount_settings, MOUNT_IN_USERNS);
         bool tmpfs_tmp = FLAGS_SET(mount_settings, MOUNT_APPLY_TMPFS_TMP);
+        bool privileged = FLAGS_SET(mount_settings, MOUNT_PRIVILEGED);
         int r;
 
         for (size_t k = 0; k < ELEMENTSOF(mount_table); k++) {
@@ -624,6 +642,10 @@ int mount_all(const char *dest,
                 bool fatal = FLAGS_SET(mount_table[k].mount_settings, MOUNT_FATAL);
                 const char *o;
 
+                /* If we are not privileged but the entry is marked as privileged and to be mounted outside the user namespace, then skip it */
+                if (!privileged && FLAGS_SET(mount_table[k].mount_settings, MOUNT_PRIVILEGED) && !FLAGS_SET(mount_table[k].mount_settings, MOUNT_IN_USERNS))
+                        continue;
+
                 if (in_userns != FLAGS_SET(mount_table[k].mount_settings, MOUNT_IN_USERNS))
                         continue;
 
index bf5e47dce405fbe4b7361338051b36256540f05c..9112f24b9424d1ec69f26425b10823393dc8f7a8 100644 (file)
@@ -20,6 +20,7 @@ typedef enum MountSettingsMask {
         MOUNT_TOUCH              = 1 << 9, /* if set, touch file to mount over first */
         MOUNT_PREFIX_ROOT        = 1 << 10,/* if set, prefix the source path with the container's root directory */
         MOUNT_FOLLOW_SYMLINKS    = 1 << 11,/* if set, we'll follow symlinks for the mount target */
+        MOUNT_PRIVILEGED         = 1 << 12,/* if set, we'll only mount this in in the outer child if we are running in privileged mode */
 } MountSettingsMask;
 
 typedef enum CustomMountType {
index 94e751cf65833ec4d313f81e0f6ce84053d4371d..1e804a2be3c8f63d40e6d84e568932aedcdc1a32 100644 (file)
@@ -1,5 +1,6 @@
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 
+/* Make sure the net/if.h header is included before any linux/ one */
 #include <net/if.h>
 #include <linux/if.h>
 #include <linux/nl80211.h>
@@ -487,7 +488,7 @@ static int test_network_interface_initialized(const char *name) {
         if (r < 0)
                 return log_error_errno(r, "Failed to get device %s: %m", name);
 
-        r = sd_device_get_is_initialized(d);
+        r = device_is_processed(d);
         if (r < 0)
                 return log_error_errno(r, "Failed to determine whether interface %s is initialized: %m", name);
         if (r == 0)
index 3d0a9a73e3d68d2f59a5d587f61587f2cc571e9d..f4da91797e3639e958047e17d5e51b57eca8843b 100644 (file)
@@ -84,6 +84,7 @@
 #include "nspawn-stub-pid1.h"
 #include "nspawn-util.h"
 #include "nspawn.h"
+#include "nsresource.h"
 #include "nulstr-util.h"
 #include "os-util.h"
 #include "pager.h"
@@ -237,6 +238,7 @@ static char *arg_settings_filename = NULL;
 static Architecture arg_architecture = _ARCHITECTURE_INVALID;
 static ImagePolicy *arg_image_policy = NULL;
 static char *arg_background = NULL;
+static bool arg_privileged = false;
 
 STATIC_DESTRUCTOR_REGISTER(arg_directory, freep);
 STATIC_DESTRUCTOR_REGISTER(arg_template, freep);
@@ -518,6 +520,12 @@ static int detect_unified_cgroup_hierarchy_from_environment(void) {
 static int detect_unified_cgroup_hierarchy_from_image(const char *directory) {
         int r;
 
+        if (!arg_privileged) {
+                /* We only support the unified mode when running unprivileged */
+                arg_unified_cgroup_hierarchy = CGROUP_UNIFIED_ALL;
+                return 0;
+        }
+
         /* Let's inherit the mode to use from the host system, but let's take into consideration what systemd
          * in the image actually supports. */
         r = cg_all_unified();
@@ -619,7 +627,6 @@ static int parse_mount_settings_env(void) {
         e = getenv("SYSTEMD_NSPAWN_API_VFS_WRITABLE");
         if (streq_ptr(e, "network"))
                 arg_mount_settings |= MOUNT_APPLY_APIVFS_RO|MOUNT_APPLY_APIVFS_NETNS;
-
         else if (e) {
                 r = parse_boolean(e);
                 if (r < 0)
@@ -1653,6 +1660,21 @@ static int parse_argv(int argc, char *argv[]) {
 static int verify_arguments(void) {
         int r;
 
+        SET_FLAG(arg_mount_settings, MOUNT_PRIVILEGED, arg_privileged);
+
+        if (!arg_privileged) {
+                /* machined is not accessible to unpriv clients */
+                if (arg_register) {
+                        log_notice("Automatically implying --register=no, since machined is not accessible to unprivileged clients.");
+                        arg_register = false;
+                }
+
+                if (!arg_private_network) {
+                        log_notice("Automatically implying --private-network, since mounting /sys/ in an unprivileged user namespaces requires network namespacing.");
+                        arg_private_network = true;
+                }
+        }
+
         if (arg_start_mode == START_PID2 && arg_unified_cgroup_hierarchy == CGROUP_UNIFIED_UNKNOWN) {
                 /* If we are running the stub init in the container, we don't need to look at what the init
                  * in the container supports, because we are not using it. Let's immediately pick the right
@@ -2692,6 +2714,9 @@ static int reset_audit_loginuid(void) {
         if ((arg_clone_ns_flags & CLONE_NEWPID) == 0)
                 return 0;
 
+        if (!arg_privileged)
+                return 0;
+
         r = read_one_line_file("/proc/self/loginuid", &p);
         if (r == -ENOENT)
                 return 0;
@@ -2721,6 +2746,11 @@ static int mount_tunnel_dig(const char *root) {
         const char *p, *q;
         int r;
 
+        if (!arg_privileged) {
+                log_debug("Not digging mount tunnel, because running unprivileged.");
+                return 0;
+        }
+
         (void) mkdir_p("/run/systemd/nspawn/", 0755);
         (void) mkdir_p("/run/systemd/nspawn/propagate", 0600);
         p = strjoina("/run/systemd/nspawn/propagate/", arg_machine);
@@ -2749,6 +2779,11 @@ static int mount_tunnel_dig(const char *root) {
 static int mount_tunnel_open(void) {
         int r;
 
+        if (!arg_privileged) {
+                log_debug("Not opening up mount tunnel, because running unprivileged.");
+                return 0;
+        }
+
         r = mount_follow_verbose(LOG_ERR, NULL, NSPAWN_MOUNT_TUNNEL, NULL, MS_SLAVE, NULL);
         if (r < 0)
                 return r;
@@ -3270,20 +3305,32 @@ static int inner_child(
                 return r;
 
         if (!arg_network_namespace_path && arg_private_network) {
-                r = unshare(CLONE_NEWNET);
+                _cleanup_close_ int netns_fd = -EBADF;
+
+                if (arg_privileged) {
+                        if (unshare(CLONE_NEWNET) < 0)
+                                return log_error_errno(errno, "Failed to unshare network namespace: %m");
+                }
+
+                netns_fd = namespace_open_by_type(NAMESPACE_NET);
+                if (netns_fd < 0)
+                        return log_error_errno(netns_fd, "Failed to open newly allocate network namespace: %m");
+
+                r = send_one_fd(fd_inner_socket, netns_fd, 0);
                 if (r < 0)
-                        return log_error_errno(errno, "Failed to unshare network namespace: %m");
+                        return log_error_errno(r, "Failed to send network namespace to supervisor: %m");
 
                 /* Tell the parent that it can setup network interfaces. */
                 (void) barrier_place(barrier); /* #3 */
         }
 
-        r = mount_sysfs(NULL, arg_mount_settings);
-        if (r < 0)
-                return r;
+        if (arg_privileged) {
+                r = mount_sysfs(NULL, arg_mount_settings);
+                if (r < 0)
+                        return r;
+        }
 
-        /* Wait until we are cgroup-ified, so that we
-         * can mount the right cgroup path writable */
+        /* Wait until we are cgroup-ified, so that we can mount the right cgroup path writable */
         if (!barrier_place_and_sync(barrier)) /* #4 */
                 return log_error_errno(SYNTHETIC_ERRNO(ESRCH),
                                        "Parent died too early");
@@ -3584,11 +3631,11 @@ static int inner_child(
         return log_error_errno(errno, "execv(%s) failed: %m", exec_target);
 }
 
-static int setup_notify_child(void) {
+static int setup_notify_child(const void *directory) {
         _cleanup_close_ int fd = -EBADF;
-        static const union sockaddr_union sa = {
+        _cleanup_free_ char *j = NULL;
+        union sockaddr_union sa = {
                 .un.sun_family = AF_UNIX,
-                .un.sun_path = NSPAWN_NOTIFY_SOCKET_PATH,
         };
         int r;
 
@@ -3596,7 +3643,17 @@ static int setup_notify_child(void) {
         if (fd < 0)
                 return log_error_errno(errno, "Failed to allocate notification socket: %m");
 
-        (void) mkdir_parents(NSPAWN_NOTIFY_SOCKET_PATH, 0755);
+        if (directory) {
+                j = path_join(directory, NSPAWN_NOTIFY_SOCKET_PATH);
+                if (!j)
+                        return log_oom();
+        }
+
+        r = sockaddr_un_set_path(&sa.un, j ?: NSPAWN_NOTIFY_SOCKET_PATH);
+        if (r < 0)
+                return log_error_errno(r, "Failed to set AF_UNIX path to %s: %m", j ?: NSPAWN_NOTIFY_SOCKET_PATH);
+
+        (void) mkdir_parents(sa.un.sun_path, 0755);
         (void) sockaddr_un_unlink(&sa.un);
 
         WITH_UMASK(0577) { /* only set "w" bit, which is all that's necessary for connecting from the container */
@@ -3605,7 +3662,7 @@ static int setup_notify_child(void) {
                         return log_error_errno(errno, "bind(" NSPAWN_NOTIFY_SOCKET_PATH ") failed: %m");
         }
 
-        r = userns_lchown(NSPAWN_NOTIFY_SOCKET_PATH, 0, 0);
+        r = userns_lchown(sa.un.sun_path, 0, 0);
         if (r < 0)
                 return log_error_errno(r, "Failed to chown " NSPAWN_NOTIFY_SOCKET_PATH ": %m");
 
@@ -3621,6 +3678,11 @@ static int setup_unix_export_dir_outside(char **ret) {
 
         assert(ret);
 
+        if (!arg_privileged) {
+                log_debug("Not digging socket tunnel, because running unprivileged.");
+                return 0;
+        }
+
         _cleanup_free_ char *p = NULL;
         p = path_join("/run/systemd/nspawn/unix-export", arg_machine);
         if (!p)
@@ -3672,6 +3734,10 @@ static int setup_unix_export_host_inside(const char *directory, const char *unix
         int r;
 
         assert(directory);
+
+        if (!arg_privileged)
+                return 0;
+
         assert(unix_export_path);
 
         r = make_run_host(directory);
@@ -3714,10 +3780,16 @@ static int setup_unix_export_host_inside(const char *directory, const char *unix
 
 static DissectImageFlags determine_dissect_image_flags(void) {
         return
+                DISSECT_IMAGE_GENERIC_ROOT |
+                DISSECT_IMAGE_REQUIRE_ROOT |
+                DISSECT_IMAGE_RELAX_VAR_CHECK |
                 DISSECT_IMAGE_USR_NO_ROOT |
                 DISSECT_IMAGE_DISCARD_ON_LOOP |
+                DISSECT_IMAGE_ADD_PARTITION_DEVICES |
+                DISSECT_IMAGE_PIN_PARTITION_DEVICES |
                 (arg_read_only ? DISSECT_IMAGE_READ_ONLY : DISSECT_IMAGE_FSCK|DISSECT_IMAGE_GROWFS) |
-                DISSECT_IMAGE_ALLOW_USERSPACE_VERITY;
+                DISSECT_IMAGE_ALLOW_USERSPACE_VERITY |
+                (arg_console_mode == CONSOLE_INTERACTIVE ? DISSECT_IMAGE_ALLOW_INTERACTIVE_AUTH : 0);
 }
 
 static int outer_child(
@@ -4093,47 +4165,59 @@ static int outer_child(
                         return r;
         }
 
-        /* Mark everything as shared so our mounts get propagated down. This is required to make new bind
-         * mounts available in systemd services inside the container that create a new mount namespace.  See
-         * https://github.com/systemd/systemd/issues/3860 Further submounts (such as /dev) done after this
-         * will inherit the shared propagation mode.
-         *
-         * IMPORTANT: Do not overmount the root directory anymore from now on to enable moving the root
-         * directory mount to root later on.
-         * https://github.com/systemd/systemd/issues/3847#issuecomment-562735251
-         */
-        r = mount_switch_root(directory, MS_SHARED);
-        if (r < 0)
-                return log_error_errno(r, "Failed to move root directory: %m");
+        /* We have different codepaths here for privileged and non-privileged mode. In privileged mode we'll
+         * now switch into the target directory, and then do the final setup from there. If a user namespace
+         * is then allocated for the container, the root mount and everything else will be out of reach for
+         * it. For unprivileged containers we cannot do that however, since we couldn't mount a sysfs and
+         * procfs then anymore, since that only works if there's an unobstructed instance currently
+         * visible. Hence there we do it the other way round: we first allocate a new set set of namespaces
+         * (and fork for it) for which we then mount sysfs/procfs, and only then switch root. */
 
-        /* We finished setting up the rootfs which is a shared mount. The mount tunnel needs to be a
-         * dependent mount otherwise we can't MS_MOVE mounts that were propagated from the host into
-         * the container. */
-        r = mount_tunnel_open();
-        if (r < 0)
-                return r;
+        if (arg_privileged) {
+                /* Mark everything as shared so our mounts get propagated down. This is required to make new
+                 * bind mounts available in systemd services inside the container that create a new mount
+                 * namespace.  See https://github.com/systemd/systemd/issues/3860 Further submounts (such as
+                 * /dev/) done after this will inherit the shared propagation mode.
+                 *
+                 * IMPORTANT: Do not overmount the root directory anymore from now on to enable moving the root
+                 * directory mount to root later on.
+                 * https://github.com/systemd/systemd/issues/3847#issuecomment-562735251
+                 */
+                r = mount_switch_root(directory, MS_SHARED);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to move root directory: %m");
 
-        if (arg_userns_mode != USER_NAMESPACE_NO) {
-                /* In order to mount procfs and sysfs in an unprivileged container the kernel
-                 * requires that a fully visible instance is already present in the target mount
-                 * namespace. Mount one here so the inner child can mount its own instances. Later
-                 * we umount the temporary instances created here before we actually exec the
-                 * payload. Since the rootfs is shared the umount will propagate into the container.
-                 * Note, the inner child wouldn't be able to unmount the instances on its own since
-                 * it doesn't own the originating mount namespace. IOW, the outer child needs to do
-                 * this. */
-                r = pin_fully_visible_fs();
+                /* We finished setting up the rootfs which is a shared mount. The mount tunnel needs to be a
+                 * dependent mount otherwise we can't MS_MOVE mounts that were propagated from the host into
+                 * the container. */
+                r = mount_tunnel_open();
                 if (r < 0)
                         return r;
-        }
 
-        fd = setup_notify_child();
+                if (arg_userns_mode != USER_NAMESPACE_NO) {
+                        /* In order to mount procfs and sysfs in an unprivileged container the kernel
+                         * requires that a fully visible instance is already present in the target mount
+                         * namespace. Mount one here so the inner child can mount its own instances. Later
+                         * we umount the temporary instances created here before we actually exec the
+                         * payload. Since the rootfs is shared the umount will propagate into the container.
+                         * Note, the inner child wouldn't be able to unmount the instances on its own since
+                         * it doesn't own the originating mount namespace. IOW, the outer child needs to do
+                         * this. */
+                        r = pin_fully_visible_fs();
+                        if (r < 0)
+                                return r;
+                }
+
+                fd = setup_notify_child(NULL);
+        } else
+                fd = setup_notify_child(directory);
         if (fd < 0)
                 return fd;
 
         pid = raw_clone(SIGCHLD|CLONE_NEWNS|
                         arg_clone_ns_flags |
-                        (arg_userns_mode != USER_NAMESPACE_NO ? CLONE_NEWUSER : 0));
+                        (arg_userns_mode != USER_NAMESPACE_NO ? CLONE_NEWUSER : 0) |
+                        ((arg_private_network && !arg_privileged) ? CLONE_NEWNET : 0));
         if (pid < 0)
                 return log_error_errno(errno, "Failed to fork inner child: %m");
         if (pid == 0) {
@@ -4152,6 +4236,26 @@ static int outer_child(
                                 return log_error_errno(r, "Failed to join network namespace: %m");
                 }
 
+                if (!arg_privileged) {
+                        /* In unprivileged operation, sysfs + procfs are special, we'll have to mount them
+                         * inside the inner namespaces, but before we switch root. Hence do so here. */
+                        _cleanup_free_ char *j = path_join(directory, "/proc");
+                        if (!j)
+                                return log_oom();
+
+                        r = mount_follow_verbose(LOG_ERR, "proc", j, "proc", MS_NOSUID|MS_NOEXEC|MS_NODEV, NULL);
+                        if (r < 0)
+                                return r;
+
+                        r = mount_sysfs(directory, arg_mount_settings);
+                        if (r < 0)
+                                return r;
+
+                        r = mount_switch_root(directory, MS_SHARED);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to move root directory: %m");
+                }
+
                 r = inner_child(barrier, fd_inner_socket, fds, os_release_pairs);
                 if (r < 0)
                         _exit(EXIT_FAILURE);
@@ -4433,6 +4537,9 @@ static int nspawn_dispatch_notify_fd(sd_event_source *source, int fd, uint32_t r
 static int setup_notify_parent(sd_event *event, int fd, pid_t *inner_child_pid, sd_event_source **notify_event_source) {
         int r;
 
+        if (fd < 0)
+                return 0;
+
         r = sd_event_add_io(event, notify_event_source, fd, EPOLLIN, nspawn_dispatch_notify_fd, inner_child_pid);
         if (r < 0)
                 return log_error_errno(r, "Failed to allocate notify event source: %m");
@@ -4812,26 +4919,28 @@ static int load_settings(void) {
                 return 0;
 
         /* We first look in the admin's directories in /etc and /run */
-        FOREACH_STRING(i, "/etc/systemd/nspawn", "/run/systemd/nspawn") {
-                _cleanup_free_ char *j = NULL;
+        if (arg_privileged) {
+                FOREACH_STRING(i, "/etc/systemd/nspawn", "/run/systemd/nspawn") {
+                        _cleanup_free_ char *j = NULL;
 
-                j = path_join(i, arg_settings_filename);
-                if (!j)
-                        return log_oom();
+                        j = path_join(i, arg_settings_filename);
+                        if (!j)
+                                return log_oom();
 
-                f = fopen(j, "re");
-                if (f) {
-                        p = TAKE_PTR(j);
+                        f = fopen(j, "re");
+                        if (f) {
+                                p = TAKE_PTR(j);
 
-                        /* By default, we trust configuration from /etc and /run */
-                        if (arg_settings_trusted < 0)
-                                arg_settings_trusted = true;
+                                /* By default, we trust configuration from /etc and /run */
+                                if (arg_settings_trusted < 0)
+                                        arg_settings_trusted = true;
 
-                        break;
-                }
+                                break;
+                        }
 
-                if (errno != ENOENT)
-                        return log_error_errno(errno, "Failed to open %s: %m", j);
+                        if (errno != ENOENT)
+                                return log_error_errno(errno, "Failed to open %s: %m", j);
+                }
         }
 
         if (!f) {
@@ -4891,10 +5000,14 @@ static int load_oci_bundle(void) {
 
 static int run_container(
                DissectedImage *dissected_image,
+               int userns_fd,
                FDSet *fds,
-               char veth_name[IFNAMSIZ], bool *veth_created,
+               char veth_name[IFNAMSIZ],
+               bool *veth_created,
                struct ExposeArgs *expose_args,
-               int *master, pid_t *pid, int *ret) {
+               int *master,
+               pid_t *pid,
+               int *ret) {
 
         static const struct sigaction sa = {
                 .sa_handler = nop_signal_handler,
@@ -4979,11 +5092,44 @@ static int run_container(
                                                "Path %s doesn't refer to a network namespace, refusing.", arg_network_namespace_path);
         }
 
-        *pid = raw_clone(SIGCHLD|CLONE_NEWNS);
-        if (*pid < 0)
-                return log_error_errno(errno, "clone() failed%s: %m",
-                                       errno == EINVAL ?
-                                       ", do you have namespace support enabled in your kernel? (You need UTS, IPC, PID and NET namespacing built in)" : "");
+        if (arg_privileged) {
+                assert(userns_fd < 0);
+
+                /* If we have no user namespace then we'll clone and create a new mount namespace right-away. */
+
+                *pid = raw_clone(SIGCHLD|CLONE_NEWNS);
+                if (*pid < 0)
+                        return log_error_errno(errno, "clone() failed%s: %m",
+                                               errno == EINVAL ?
+                                               ", do you have namespace support enabled in your kernel? (You need UTS, IPC, PID and NET namespacing built in)" : "");
+        } else {
+                assert(userns_fd >= 0);
+
+                /* If we have a user namespace then we'll clone() first, and then join the user namespace,
+                 * and then open the mount namespace, so that it is owned by the user namespace */
+
+                *pid = raw_clone(SIGCHLD);
+                if (*pid < 0)
+                        return log_error_errno(errno, "clone() failed: %m");
+
+                if (*pid == 0) {
+                        if (setns(userns_fd, CLONE_NEWUSER) < 0) {
+                                log_error_errno(errno, "Failed to join allocate user namespace: %m");
+                                _exit(EXIT_FAILURE);
+                        }
+
+                        r = reset_uid_gid();
+                        if (r < 0) {
+                                log_error_errno(r, "Failed to reset UID/GID to root: %m");
+                                _exit(EXIT_FAILURE);
+                        }
+
+                        if (unshare(CLONE_NEWNS) < 0) {
+                                log_error_errno(errno, "Failed to unshare file system namespace: %m");
+                                _exit(EXIT_FAILURE);
+                        }
+                }
+        }
 
         if (*pid == 0) {
                 /* The outer child only has a file system namespace. */
@@ -5120,19 +5266,13 @@ static int run_container(
                         /* Wait until the child has unshared its network namespace. */
                         if (!barrier_place_and_sync(&barrier)) /* #3 */
                                 return log_error_errno(SYNTHETIC_ERRNO(ESRCH), "Child died too early");
-                }
 
-                if (child_netns_fd < 0) {
-                        /* Make sure we have an open file descriptor to the child's network
-                         * namespace so it stays alive even if the child exits. */
-                        r = namespace_open(*pid,
-                                           /* ret_pidns_fd = */ NULL,
-                                           /* ret_mntns_fd = */ NULL,
-                                           &child_netns_fd,
-                                           /* ret_userns_fd = */ NULL,
-                                           /* ret_root_fd = */ NULL);
-                        if (r < 0)
-                                return log_error_errno(r, "Failed to open child network namespace: %m");
+                        /* Make sure we have an open file descriptor to the child's network namespace so it
+                         * stays alive even if the child exits. */
+                        assert(child_netns_fd < 0);
+                        child_netns_fd = receive_one_fd(fd_inner_socket_pair[0], 0);
+                        if (child_netns_fd < 0)
+                                return log_error_errno(r, "Failed to receive child network namespace: %m");
                 }
 
                 r = move_network_interfaces(child_netns_fd, arg_network_interfaces);
@@ -5140,12 +5280,29 @@ static int run_container(
                         return r;
 
                 if (arg_network_veth) {
-                        r = setup_veth(arg_machine, *pid, veth_name,
-                                       arg_network_bridge || arg_network_zone, &arg_network_provided_mac);
-                        if (r < 0)
-                                return r;
-                        else if (r > 0)
-                                ifi = r;
+                        if (arg_privileged) {
+                                r = setup_veth(arg_machine, *pid, veth_name,
+                                               arg_network_bridge || arg_network_zone, &arg_network_provided_mac);
+                                if (r < 0)
+                                        return r;
+                                else if (r > 0)
+                                        ifi = r;
+                        } else {
+                                _cleanup_free_ char *host_ifname = NULL;
+
+                                r = nsresource_add_netif(userns_fd, child_netns_fd, /* namespace_ifname= */ NULL, &host_ifname, /* ret_namespace_ifname= */ NULL);
+                                if (r < 0)
+                                        return log_error_errno(r, "Failed to add network interface to container: %m");
+
+                                ifi = if_nametoindex(host_ifname);
+                                if (ifi == 0)
+                                        return log_error_errno(errno, "Failed to resolve interface '%s': %m", host_ifname);
+
+                                if (strlen(host_ifname) >= IFNAMSIZ)
+                                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Host interface name too long?");
+
+                                strcpy(veth_name, host_ifname);
+                        }
 
                         if (arg_network_bridge) {
                                 /* Add the interface to a bridge */
@@ -5184,9 +5341,12 @@ static int run_container(
         }
 
         if (arg_register || !arg_keep_unit) {
-                r = sd_bus_default_system(&bus);
+                if (arg_privileged)
+                        r = sd_bus_default_system(&bus);
+                else
+                        r = sd_bus_default_user(&bus);
                 if (r < 0)
-                        return log_error_errno(r, "Failed to open system bus: %m");
+                        return log_error_errno(r, "Failed to open bus: %m");
 
                 r = sd_bus_set_close_on_exit(bus, false);
                 if (r < 0)
@@ -5247,7 +5407,13 @@ static int run_container(
         } else if (arg_slice || arg_property)
                 log_notice("Machine and scope registration turned off, --slice= and --property= settings will have no effect.");
 
-        r = create_subcgroup(*pid, arg_keep_unit, arg_unified_cgroup_hierarchy);
+        r = create_subcgroup(
+                        *pid,
+                        arg_keep_unit,
+                        arg_unified_cgroup_hierarchy,
+                        arg_uid_shift,
+                        userns_fd,
+                        arg_privileged);
         if (r < 0)
                 return r;
 
@@ -5255,14 +5421,8 @@ static int run_container(
         if (r < 0)
                 return r;
 
-        r = chown_cgroup(*pid, arg_unified_cgroup_hierarchy, arg_uid_shift);
-        if (r < 0)
-                return r;
-
-        /* Notify the child that the parent is ready with all
-         * its setup (including cgroup-ification), and that
-         * the child can now hand over control to the code to
-         * run inside the container. */
+        /* Notify the child that the parent is ready with all its setup (including cgroup-ification), and
+         * that the child can now hand over control to the code to run inside the container. */
         (void) barrier_place(&barrier); /* #4 */
 
         /* Block SIGCHLD here, before notifying child.
@@ -5428,7 +5588,7 @@ static int run_container(
 
         fd_kmsg_fifo = safe_close(fd_kmsg_fifo);
 
-        if (arg_private_network) {
+        if (arg_private_network && arg_privileged) {
                 r = move_back_network_interfaces(child_netns_fd, arg_network_interfaces);
                 if (r < 0)
                         return r;
@@ -5569,6 +5729,10 @@ static int cant_be_in_netns(void) {
         if (r == -ENOENT || ERRNO_IS_NEG_DISCONNECT(r))
                 return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
                                        "Sorry, but --image= requires access to the host's /run/ hierarchy, since we need access to udev.");
+        if (ERRNO_IS_NEG_PRIVILEGE(r)) {
+                log_debug_errno(r, "Can't connect to udev control socket, assuming we are in same netns.");
+                return 0;
+        }
         if (r < 0)
                 return log_error_errno(r, "Failed to connect socket to udev control socket: %m");
 
@@ -5587,7 +5751,7 @@ static int cant_be_in_netns(void) {
 
 static int run(int argc, char *argv[]) {
         bool remove_directory = false, remove_image = false, veth_created = false, remove_tmprootdir = false;
-        _cleanup_close_ int master = -EBADF;
+        _cleanup_close_ int master = -EBADF, userns_fd = -EBADF;
         _cleanup_fdset_free_ FDSet *fds = NULL;
         int r, n_fd_passed, ret = EXIT_SUCCESS;
         char veth_name[IFNAMSIZ] = "";
@@ -5602,17 +5766,12 @@ static int run(int argc, char *argv[]) {
         log_parse_environment();
         log_open();
 
+        arg_privileged = getuid() == 0;
+
         r = parse_argv(argc, argv);
         if (r <= 0)
                 goto finish;
 
-        if (geteuid() != 0) {
-                r = log_warning_errno(SYNTHETIC_ERRNO(EPERM),
-                                      argc >= 2 ? "Need to be root." :
-                                      "Need to be root (and some arguments are usually required).\nHint: try --help");
-                goto finish;
-        }
-
         r = cant_be_in_netns();
         if (r < 0)
                 goto finish;
@@ -5643,7 +5802,7 @@ static int run(int argc, char *argv[]) {
         if (!arg_private_network && arg_userns_mode != USER_NAMESPACE_NO && arg_uid_shift > 0)
                 arg_caps_retain &= ~(UINT64_C(1) << CAP_NET_BIND_SERVICE);
 
-        r = cg_unified();
+        r = cg_unified(); /* initialize cache early */
         if (r < 0) {
                 log_error_errno(r, "Failed to determine whether the unified cgroups hierarchy is used: %m");
                 goto finish;
@@ -5664,6 +5823,16 @@ static int run(int argc, char *argv[]) {
         /* Reapply environment settings. */
         (void) detect_unified_cgroup_hierarchy_from_environment();
 
+        if (!arg_privileged) {
+                r = cg_all_unified();
+                if (r < 0) {
+                        log_error_errno(r, "Failed to determine if we are in unified cgroupv2 mode: %m");
+                        goto finish;
+                }
+                if (r == 0)
+                        return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Unprivileged operation only supported in unified cgroupv2 mode.");
+        }
+
         /* Ignore SIGPIPE here, because we use splice() on the ptyfwd stuff and that will generate SIGPIPE if
          * the result is closed. Note that the container payload child will reset signal mask+handler anyway,
          * so just turning this off here means we only turn it off in nspawn itself, not any children. */
@@ -5683,9 +5852,21 @@ static int run(int argc, char *argv[]) {
         * the child. Functions like copy_devnodes() change the umask temporarily. */
         umask(0022);
 
+        if (arg_console_mode < 0)
+                arg_console_mode = isatty(STDIN_FILENO) && isatty(STDOUT_FILENO) ?
+                                   CONSOLE_INTERACTIVE : CONSOLE_READ_ONLY;
+
+        if (arg_console_mode == CONSOLE_PIPE) /* if we pass STDERR on to the container, don't add our own logs into it too */
+                arg_quiet = true;
+
         if (arg_directory) {
                 assert(!arg_image);
 
+                if (!arg_privileged) {
+                        r = log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Invoking container from plain directory tree is currently not supported if called without privileges.");
+                        goto finish;
+                }
+
                 /* Safety precaution: let's not allow running images from the live host OS image, as long as
                  * /var from the host will propagate into container dynamically (because bad things happen if
                  * two systems write to the same /var). Let's allow it for the special cases where /var is
@@ -5722,7 +5903,11 @@ static int run(int argc, char *argv[]) {
 
                         /* We take an exclusive lock on this image, since it's our private, ephemeral copy
                          * only owned by us and no one else. */
-                        r = image_path_lock(np, LOCK_EX|LOCK_NB, &tree_global_lock, &tree_local_lock);
+                        r = image_path_lock(
+                                        np,
+                                        LOCK_EX|LOCK_NB,
+                                        arg_privileged ? &tree_global_lock : NULL,
+                                        &tree_local_lock);
                         if (r < 0) {
                                 log_error_errno(r, "Failed to lock %s: %m", np);
                                 goto finish;
@@ -5754,7 +5939,11 @@ static int run(int argc, char *argv[]) {
                         if (r < 0)
                                 goto finish;
 
-                        r = image_path_lock(arg_directory, (arg_read_only ? LOCK_SH : LOCK_EX) | LOCK_NB, &tree_global_lock, &tree_local_lock);
+                        r = image_path_lock(
+                                        arg_directory,
+                                        (arg_read_only ? LOCK_SH : LOCK_EX) | LOCK_NB,
+                                        arg_privileged ? &tree_global_lock : NULL,
+                                        &tree_local_lock);
                         if (r == -EBUSY) {
                                 log_error_errno(r, "Directory tree %s is currently busy.", arg_directory);
                                 goto finish;
@@ -5846,15 +6035,12 @@ static int run(int argc, char *argv[]) {
 
         } else {
                 DissectImageFlags dissect_image_flags =
-                        DISSECT_IMAGE_GENERIC_ROOT |
-                        DISSECT_IMAGE_REQUIRE_ROOT |
-                        DISSECT_IMAGE_RELAX_VAR_CHECK |
-                        DISSECT_IMAGE_USR_NO_ROOT |
-                        DISSECT_IMAGE_ADD_PARTITION_DEVICES |
-                        DISSECT_IMAGE_PIN_PARTITION_DEVICES;
+                        determine_dissect_image_flags();
+
                 assert(arg_image);
                 assert(!arg_template);
 
+
                 r = chase_and_update(&arg_image, 0);
                 if (r < 0)
                         goto finish;
@@ -5869,7 +6055,11 @@ static int run(int argc, char *argv[]) {
                         }
 
                         /* Always take an exclusive lock on our own ephemeral copy. */
-                        r = image_path_lock(np, LOCK_EX|LOCK_NB, &tree_global_lock, &tree_local_lock);
+                        r = image_path_lock(
+                                        np,
+                                        LOCK_EX|LOCK_NB,
+                                        arg_privileged ? &tree_global_lock : NULL,
+                                        &tree_local_lock);
                         if (r < 0) {
                                 log_error_errno(r, "Failed to create image lock: %m");
                                 goto finish;
@@ -5894,7 +6084,11 @@ static int run(int argc, char *argv[]) {
                         free_and_replace(arg_image, np);
                         remove_image = true;
                 } else {
-                        r = image_path_lock(arg_image, (arg_read_only ? LOCK_SH : LOCK_EX) | LOCK_NB, &tree_global_lock, &tree_local_lock);
+                        r = image_path_lock(
+                                        arg_image,
+                                        (arg_read_only ? LOCK_SH : LOCK_EX) | LOCK_NB,
+                                        arg_privileged ? &tree_global_lock : NULL,
+                                        &tree_local_lock);
                         if (r == -EBUSY) {
                                 log_error_errno(r, "Disk image %s is currently busy.", arg_image);
                                 goto finish;
@@ -5929,56 +6123,80 @@ static int run(int argc, char *argv[]) {
                         goto finish;
                 }
 
-                r = loop_device_make_by_path(
-                                arg_image,
-                                arg_read_only ? O_RDONLY : O_RDWR,
-                                /* sector_size= */ UINT32_MAX,
-                                FLAGS_SET(dissect_image_flags, DISSECT_IMAGE_NO_PARTITION_TABLE) ? 0 : LO_FLAGS_PARTSCAN,
-                                LOCK_SH,
-                                &loop);
-                if (r < 0) {
-                        log_error_errno(r, "Failed to set up loopback block device: %m");
-                        goto finish;
-                }
+                if (arg_privileged) {
+                        r = loop_device_make_by_path(
+                                        arg_image,
+                                        arg_read_only ? O_RDONLY : O_RDWR,
+                                        /* sector_size= */ UINT32_MAX,
+                                        FLAGS_SET(dissect_image_flags, DISSECT_IMAGE_NO_PARTITION_TABLE) ? 0 : LO_FLAGS_PARTSCAN,
+                                        LOCK_SH,
+                                        &loop);
+                        if (r < 0) {
+                                log_error_errno(r, "Failed to set up loopback block device: %m");
+                                goto finish;
+                        }
 
-                r = dissect_loop_device_and_warn(
-                                loop,
-                                &arg_verity_settings,
-                                /* mount_options=*/ NULL,
-                                arg_image_policy ?: &image_policy_container,
-                                dissect_image_flags,
-                                &dissected_image);
-                if (r == -ENOPKG) {
-                        /* dissected_image_and_warn() already printed a brief error message. Extend on that with more details */
-                        log_notice("Note that the disk image needs to\n"
-                                   "    a) either contain only a single MBR partition of type 0x83 that is marked bootable\n"
-                                   "    b) or contain a single GPT partition of type 0FC63DAF-8483-4772-8E79-3D69D8477DE4\n"
-                                   "    c) or follow https://uapi-group.org/specifications/specs/discoverable_partitions_specification\n"
-                                   "    d) or contain a file system without a partition table\n"
-                                   "in order to be bootable with systemd-nspawn.");
-                        goto finish;
-                }
-                if (r < 0)
-                        goto finish;
+                        r = dissect_loop_device_and_warn(
+                                        loop,
+                                        &arg_verity_settings,
+                                        /* mount_options=*/ NULL,
+                                        arg_image_policy ?: &image_policy_container,
+                                        dissect_image_flags,
+                                        &dissected_image);
+                        if (r == -ENOPKG) {
+                                /* dissected_image_and_warn() already printed a brief error message. Extend on that with more details */
+                                log_notice("Note that the disk image needs to\n"
+                                           "    a) either contain only a single MBR partition of type 0x83 that is marked bootable\n"
+                                           "    b) or contain a single GPT partition of type 0FC63DAF-8483-4772-8E79-3D69D8477DE4\n"
+                                           "    c) or follow https://uapi-group.org/specifications/specs/discoverable_partitions_specification\n"
+                                           "    d) or contain a file system without a partition table\n"
+                                           "in order to be bootable with systemd-nspawn.");
+                                goto finish;
+                        }
+                        if (r < 0)
+                                goto finish;
 
-                r = dissected_image_load_verity_sig_partition(
-                                dissected_image,
-                                loop->fd,
-                                &arg_verity_settings);
-                if (r < 0)
-                        goto finish;
+                        r = dissected_image_load_verity_sig_partition(
+                                        dissected_image,
+                                        loop->fd,
+                                        &arg_verity_settings);
+                        if (r < 0)
+                                goto finish;
 
-                if (dissected_image->has_verity && !arg_verity_settings.root_hash && !dissected_image->has_verity_sig)
-                        log_notice("Note: image %s contains verity information, but no root hash specified and no embedded "
-                                   "root hash signature found! Proceeding without integrity checking.", arg_image);
+                        if (dissected_image->has_verity && !arg_verity_settings.root_hash && !dissected_image->has_verity_sig)
+                                log_notice("Note: image %s contains verity information, but no root hash specified and no embedded "
+                                           "root hash signature found! Proceeding without integrity checking.", arg_image);
 
-                r = dissected_image_decrypt_interactively(
-                                dissected_image,
-                                NULL,
-                                &arg_verity_settings,
-                                0);
-                if (r < 0)
-                        goto finish;
+                        r = dissected_image_decrypt_interactively(
+                                        dissected_image,
+                                        NULL,
+                                        &arg_verity_settings,
+                                        dissect_image_flags);
+                        if (r < 0)
+                                goto finish;
+                } else {
+                        _cleanup_free_ char *userns_name = strjoin("nspawn-", arg_machine);
+                        if (!userns_name) {
+                                r = log_oom();
+                                goto finish;
+                        }
+
+                        /* if we are unprivileged, let's allocate a 64K userns first */
+                        userns_fd = nsresource_allocate_userns(userns_name, UINT64_C(0x10000));
+                        if (userns_fd < 0) {
+                                r = log_error_errno(userns_fd, "Failed to allocate user namespace with 64K users: %m");
+                                goto finish;
+                        }
+
+                        r = mountfsd_mount_image(
+                                        arg_image,
+                                        userns_fd,
+                                        arg_image_policy,
+                                        dissect_image_flags,
+                                        &dissected_image);
+                        if (r < 0)
+                                goto finish;
+                }
 
                 /* Now that we mounted the image, let's try to remove it again, if it is ephemeral */
                 if (remove_image && unlink(arg_image) >= 0)
@@ -5992,13 +6210,6 @@ static int run(int argc, char *argv[]) {
         if (r < 0)
                 goto finish;
 
-        if (arg_console_mode < 0)
-                arg_console_mode = isatty(STDIN_FILENO) && isatty(STDOUT_FILENO) ?
-                                   CONSOLE_INTERACTIVE : CONSOLE_READ_ONLY;
-
-        if (arg_console_mode == CONSOLE_PIPE) /* if we pass STDERR on to the container, don't add our own logs into it too */
-                arg_quiet = true;
-
         if (!arg_quiet) {
                 const char *t = arg_image ?: arg_directory;
                 _cleanup_free_ char *u = NULL;
@@ -6029,11 +6240,13 @@ static int run(int argc, char *argv[]) {
                 expose_args.fw_ctx = fw_ctx;
         }
         for (;;) {
-                r = run_container(dissected_image,
-                                  fds,
-                                  veth_name, &veth_created,
-                                  &expose_args, &master,
-                                  &pid, &ret);
+                r = run_container(
+                                dissected_image,
+                                userns_fd,
+                                fds,
+                                veth_name, &veth_created,
+                                &expose_args, &master,
+                                &pid, &ret);
                 if (r <= 0)
                         break;
         }
@@ -6075,7 +6288,7 @@ finish:
                         log_debug_errno(errno, "Can't remove temporary root directory '%s', ignoring: %m", tmprootdir);
         }
 
-        if (arg_machine) {
+        if (arg_machine && arg_privileged) {
                 const char *p;
 
                 p = strjoina("/run/systemd/nspawn/propagate/", arg_machine);
@@ -6089,9 +6302,11 @@ finish:
         expose_port_flush(&fw_ctx, arg_expose_ports, AF_INET,  &expose_args.address4);
         expose_port_flush(&fw_ctx, arg_expose_ports, AF_INET6, &expose_args.address6);
 
-        if (veth_created)
-                (void) remove_veth_links(veth_name, arg_network_veth_extra);
-        (void) remove_bridge(arg_network_zone);
+        if (arg_privileged) {
+                if (veth_created)
+                        (void) remove_veth_links(veth_name, arg_network_veth_extra);
+                (void) remove_bridge(arg_network_zone);
+        }
 
         custom_mount_free_all(arg_custom_mounts, arg_n_custom_mounts);
         expose_port_free_all(arg_expose_ports);
diff --git a/src/nsresourced/bpf/userns_restrict/meson.build b/src/nsresourced/bpf/userns_restrict/meson.build
new file mode 100644 (file)
index 0000000..d773c75
--- /dev/null
@@ -0,0 +1,25 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+if conf.get('HAVE_VMLINUX_H') != 1
+        subdir_done()
+endif
+
+userns_restrict_bpf_o_unstripped = custom_target(
+        'userns-restrict.bpf.unstripped.o',
+        input : 'userns-restrict.bpf.c',
+        output : 'userns-restrict.bpf.unstripped.o',
+        command : bpf_o_unstripped_cmd,
+        depends : vmlinux_h_dependency)
+
+userns_restrict_bpf_o = custom_target(
+        'userns-restrict.bpf.o',
+        input : userns_restrict_bpf_o_unstripped,
+        output : 'userns-restrict.bpf.o',
+        command : bpf_o_cmd)
+
+userns_restrict_skel_h = custom_target(
+        'userns-restrict.skel.h',
+        input : userns_restrict_bpf_o,
+        output : 'userns-restrict.skel.h',
+        command : skel_h_cmd,
+        capture : true)
diff --git a/src/nsresourced/bpf/userns_restrict/userns-restrict-skel.h b/src/nsresourced/bpf/userns_restrict/userns-restrict-skel.h
new file mode 100644 (file)
index 0000000..271caf4
--- /dev/null
@@ -0,0 +1,17 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+/* The SPDX header above is actually correct in claiming this was
+ * LGPL-2.1-or-later, because it is. Since the kernel doesn't consider that
+ * compatible with GPL we will claim this to be GPL however, which should be
+ * fine given that LGPL-2.1-or-later downgrades to GPL if needed.
+ */
+
+#include "bpf-dlopen.h"
+
+/* libbpf is used via dlopen(), so rename symbols */
+#define bpf_object__attach_skeleton sym_bpf_object__attach_skeleton
+#define bpf_object__destroy_skeleton sym_bpf_object__destroy_skeleton
+#define bpf_object__load_skeleton sym_bpf_object__load_skeleton
+#define bpf_object__open_skeleton sym_bpf_object__open_skeleton
+
+#include "bpf/userns_restrict/userns-restrict.skel.h"
diff --git a/src/nsresourced/bpf/userns_restrict/userns-restrict.bpf.c b/src/nsresourced/bpf/userns_restrict/userns-restrict.bpf.c
new file mode 100644 (file)
index 0000000..126422b
--- /dev/null
@@ -0,0 +1,179 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+/* The SPDX header above is actually correct in claiming this was
+ * LGPL-2.1-or-later, because it is. Since the kernel doesn't consider that
+ * compatible with GPL we will claim this to be GPL however, which should be
+ * fine given that LGPL-2.1-or-later downgrades to GPL if needed.
+ */
+
+/* If offsetof() is implemented via __builtin_offset() then it doesn't work on current compilers, since the
+ * built-ins do not understand CO-RE. Let's undefine any such macros here, to force bpf_helpers.h to define
+ * its own definitions for this. (In new versions it will do so automatically, but at least in libbpf 1.1.0
+ * it does not.) */
+#undef offsetof
+#undef container_of
+
+#include "vmlinux.h"
+
+#include <bpf/bpf_helpers.h>
+#include <bpf/bpf_tracing.h>
+#include <bpf/bpf_core_read.h>
+#include <errno.h>
+
+#ifndef bpf_core_cast
+/* bpf_rdonly_cast() was introduced in libbpf commit 688879f together with
+ * the definition of a bpf_core_cast macro. So use that one to avoid
+ * defining a prototype for bpf_rdonly_cast */
+void *bpf_rdonly_cast(void *, __u32) __ksym;
+#endif
+
+/* BPF module that implements an allowlist of mounts (identified by mount ID) for user namespaces (identified
+ * by their inode number in nsfs) that restricts creation of inodes (which would inherit the callers UID/GID)
+ * or changing of ownership (similar).
+ *
+ * This hooks into the various path-based LSM entrypoints that control inode creation as well as chmod(), and
+ * then looks up the calling process' user namespace in a global map of namespaces, which points us to
+ * another map that is simply a list of allowed mnt_ids. */
+
+// FIXME: ACL adjustments are currently not blocked. There's no path-based LSM hook available in the kernel
+// for setting xattrs or ACLs, hence we cannot easily block them, even though we want that. We can get away
+// with ignoring this for now, as ACLs never define ownership, but purely access: i.e. ACLs never allow
+// taking possession of an object, but only control access to it. Thus, things like suid access modes should
+// not be reachable through it. It still sucks though that a user can persistently add an ACL entry to a file
+// with their transient UIDs/GIDs.
+
+/* kernel currently enforces a maximum usernamespace nesting depth of 32, see create_user_ns() in the kernel sources */
+#define USER_NAMESPACE_DEPTH_MAX 32U
+
+struct mnt_id_map {
+        __uint(type, BPF_MAP_TYPE_HASH);
+        __uint(max_entries, 1);        /* placeholder, configured otherwise by nsresourced */
+        __type(key, int);
+        __type(value, int);
+};
+
+struct {
+        __uint(type, BPF_MAP_TYPE_HASH_OF_MAPS);
+        __uint(max_entries, 1);        /* placeholder, configured otherwise by nsresourced */
+        __type(key, unsigned);         /* userns inode */
+        __array(values, struct mnt_id_map);
+} userns_mnt_id_hash SEC(".maps");
+
+struct {
+        __uint(type, BPF_MAP_TYPE_RINGBUF);
+        __uint(max_entries, 4096);
+} userns_ringbuf SEC(".maps");
+
+static inline struct mount *real_mount(struct vfsmount *mnt) {
+        return container_of(mnt, struct mount, mnt);
+}
+
+static int validate_inode_on_mount(struct inode *inode, struct vfsmount *v) {
+        struct user_namespace *mount_userns, *task_userns, *p;
+        unsigned task_userns_inode;
+        struct task_struct *task;
+        void *mnt_id_map;
+        struct mount *m;
+        int mnt_id;
+
+        /* Get user namespace from vfsmount */
+        m = bpf_rdonly_cast(real_mount(v), bpf_core_type_id_kernel(struct mount));
+        mount_userns = m->mnt_ns->user_ns;
+
+        /* Get user namespace from task */
+        task = (struct task_struct*) bpf_get_current_task_btf();
+        task_userns = task->cred->user_ns;
+
+        /* Is the file on a mount that belongs to our own user namespace or a child of it? If so, say
+         * yes immediately. */
+        p = mount_userns;
+        for (unsigned i = 0; i < USER_NAMESPACE_DEPTH_MAX; i++) {
+                if (p == task_userns)
+                        return 0; /* our task's user namespace (or a child thereof) owns this superblock: allow! */
+
+                p = p->parent;
+                if (!p)
+                        break;
+        }
+
+        /* Hmm, something is fishy if there's more than 32 levels of namespaces involved. Let's better be
+         * safe than sorry, and refuse. */
+        if (p)
+                return -EPERM;
+
+        /* This is a mount foreign to our task's user namespace, let's consult our allow list */
+        task_userns_inode = task_userns->ns.inum;
+
+        mnt_id_map = bpf_map_lookup_elem(&userns_mnt_id_hash, &task_userns_inode);
+        if (!mnt_id_map) /* No rules installed for this userns? Then say yes, too! */
+                return 0;
+
+        mnt_id = m->mnt_id;
+
+        /* Otherwise, say yes if the mount ID is allowlisted */
+        if (bpf_map_lookup_elem(mnt_id_map, &mnt_id))
+                return 0;
+
+        return -EPERM;
+}
+
+static int validate_path(const struct path *path, int ret) {
+        struct inode *inode;
+        struct vfsmount *v;
+
+        if (ret != 0) /* propagate earlier error */
+                return ret;
+
+        inode = path->dentry->d_inode;
+        v = path->mnt;
+
+        return validate_inode_on_mount(inode, v);
+}
+
+SEC("lsm/path_chown")
+int BPF_PROG(userns_restrict_path_chown, struct path *path, void* uid, void *gid, int ret) {
+        return validate_path(path, ret);
+}
+
+SEC("lsm/path_mkdir")
+int BPF_PROG(userns_restrict_path_mkdir, struct path *dir, struct dentry *dentry, umode_t mode, int ret) {
+        return validate_path(dir, ret);
+}
+
+SEC("lsm/path_mknod")
+int BPF_PROG(userns_restrict_path_mknod, const struct path *dir, struct dentry *dentry, umode_t mode, unsigned int dev, int ret) {
+        return validate_path(dir, ret);
+}
+
+SEC("lsm/path_symlink")
+int BPF_PROG(userns_restrict_path_symlink, const struct path *dir, struct dentry *dentry, const char *old_name, int ret) {
+        return validate_path(dir, ret);
+}
+
+SEC("lsm/path_link")
+int BPF_PROG(userns_restrict_path_link, struct dentry *old_dentry, const struct path *new_dir, struct dentry *new_dentry, int ret) {
+        return validate_path(new_dir, ret);
+}
+
+SEC("kprobe/free_user_ns")
+void BPF_KPROBE(userns_restrict_free_user_ns, struct work_struct *work) {
+        struct user_namespace *userns;
+        unsigned inode;
+        void *mnt_id_map;
+
+        /* Inform userspace that a user namespace just went away. I wish there was a nicer way to hook into
+         * user namespaces being deleted than using kprobes, but couldn't find any. */
+
+        userns = bpf_rdonly_cast(container_of(work, struct user_namespace, work),
+                                 bpf_core_type_id_kernel(struct user_namespace));
+
+        inode = userns->ns.inum;
+
+        mnt_id_map = bpf_map_lookup_elem(&userns_mnt_id_hash, &inode);
+        if (!mnt_id_map) /* No rules installed for this userns? Then send no notification. */
+                return;
+
+        bpf_ringbuf_output(&userns_ringbuf, &inode, sizeof(inode), 0);
+}
+
+static const char _license[] SEC("license") = "GPL";
diff --git a/src/nsresourced/meson.build b/src/nsresourced/meson.build
new file mode 100644 (file)
index 0000000..cb131f0
--- /dev/null
@@ -0,0 +1,48 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+subdir('bpf/userns_restrict')
+
+systemd_nsresourcework_sources = files(
+        'nsresourcework.c',
+        'userns-restrict.c',
+        'userns-registry.c',
+)
+
+systemd_nsresourced_sources = files(
+        'nsresourced-manager.c',
+        'nsresourced.c',
+        'userns-restrict.c',
+        'userns-registry.c',
+)
+
+userns_restrict_include = include_directories('.')
+
+if conf.get('HAVE_VMLINUX_H') == 1
+        systemd_nsresourcework_sources += userns_restrict_skel_h
+        systemd_nsresourced_sources += userns_restrict_skel_h
+
+        executables += [
+                test_template + {
+                        'sources' : files('test-userns-restrict.c', 'userns-restrict.c') + userns_restrict_skel_h,
+                        'conditions' : ['ENABLE_NSRESOURCED', 'HAVE_VMLINUX_H'],
+                        'include_directories' : [ includes, userns_restrict_include ],
+                },
+        ]
+endif
+
+executables += [
+        libexec_template + {
+                'name' : 'systemd-nsresourcework',
+                'conditions' : ['ENABLE_NSRESOURCED'],
+                'sources' : systemd_nsresourcework_sources,
+                'dependencies' : threads,
+                'include_directories' : [ includes, userns_restrict_include ],
+        },
+        libexec_template + {
+                'name' : 'systemd-nsresourced',
+                'conditions' : ['ENABLE_NSRESOURCED'],
+                'sources' : systemd_nsresourced_sources,
+                'dependencies' : threads,
+                'include_directories' : [ includes, userns_restrict_include ],
+        },
+]
diff --git a/src/nsresourced/nsresourced-manager.c b/src/nsresourced/nsresourced-manager.c
new file mode 100644 (file)
index 0000000..d87da58
--- /dev/null
@@ -0,0 +1,647 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <sys/mount.h>
+#include <sys/wait.h>
+
+#include "sd-daemon.h"
+
+#include "bpf-dlopen.h"
+#include "build-path.h"
+#include "common-signal.h"
+#include "env-util.h"
+#include "fd-util.h"
+#include "fs-util.h"
+#include "mkdir.h"
+#include "nsresourced-manager.h"
+#include "parse-util.h"
+#include "process-util.h"
+#include "recurse-dir.h"
+#include "set.h"
+#include "signal-util.h"
+#include "socket-util.h"
+#include "stat-util.h"
+#include "stdio-util.h"
+#include "strv.h"
+#include "umask-util.h"
+#include "unaligned.h"
+#include "user-util.h"
+#include "userns-registry.h"
+#include "userns-restrict.h"
+
+#define LISTEN_TIMEOUT_USEC (25 * USEC_PER_SEC)
+
+static int start_workers(Manager *m, bool explicit_request);
+
+static int on_worker_exit(sd_event_source *s, const siginfo_t *si, void *userdata) {
+        Manager *m = ASSERT_PTR(userdata);
+
+        assert(s);
+
+        assert_se(!set_remove(m->workers_dynamic, s) != !set_remove(m->workers_fixed, s));
+        sd_event_source_disable_unref(s);
+
+        if (si->si_code == CLD_EXITED) {
+                if (si->si_status == EXIT_SUCCESS)
+                        log_debug("Worker " PID_FMT " exited successfully.", si->si_pid);
+                else
+                        log_warning("Worker " PID_FMT " died with a failure exit status %i, ignoring.", si->si_pid, si->si_status);
+        } else if (si->si_code == CLD_KILLED)
+                log_warning("Worker " PID_FMT " was killed by signal %s, ignoring.", si->si_pid, signal_to_string(si->si_status));
+        else if (si->si_code == CLD_DUMPED)
+                log_warning("Worker " PID_FMT " dumped core by signal %s, ignoring.", si->si_pid, signal_to_string(si->si_status));
+        else
+                log_warning("Got unexpected exit code via SIGCHLD, ignoring.");
+
+        (void) start_workers(m, /* explicit_request= */ false); /* Fill up workers again if we fell below the low watermark */
+        return 0;
+}
+
+static int on_sigusr2(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) {
+        Manager *m = ASSERT_PTR(userdata);
+
+        assert(s);
+
+        (void) start_workers(m, /* explicit_request=*/ true); /* Workers told us there's more work, let's add one more worker as long as we are below the high watermark */
+        return 0;
+}
+
+static int on_deferred_start_worker(sd_event_source *s, uint64_t usec, void *userdata) {
+        Manager *m = ASSERT_PTR(userdata);
+
+        assert(s);
+
+        m->deferred_start_worker_event_source = sd_event_source_unref(m->deferred_start_worker_event_source);
+
+        (void) start_workers(m, /* explicit_request=*/ false);
+        return 0;
+}
+
+DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(
+                event_source_hash_ops,
+                sd_event_source,
+                (void (*)(const sd_event_source*, struct siphash*)) trivial_hash_func,
+                (int (*)(const sd_event_source*, const sd_event_source*)) trivial_compare_func,
+                sd_event_source_disable_unref);
+
+int manager_new(Manager **ret) {
+        _cleanup_(manager_freep) Manager *m = NULL;
+        int r;
+
+        m = new(Manager, 1);
+        if (!m)
+                return -ENOMEM;
+
+        *m = (Manager) {
+                .listen_fd = -EBADF,
+                .worker_ratelimit = {
+                        .interval = 2 * USEC_PER_SEC,
+                        .burst = 250,
+                },
+                .registry_fd = -EBADF,
+        };
+
+        r = sd_event_new(&m->event);
+        if (r < 0)
+                return r;
+
+        r = sd_event_set_signal_exit(m->event, true);
+        if (r < 0)
+                return r;
+
+        r = sd_event_add_signal(m->event, NULL, (SIGRTMIN+18)|SD_EVENT_SIGNAL_PROCMASK, sigrtmin18_handler, NULL);
+        if (r < 0)
+                return r;
+
+        r = sd_event_add_memory_pressure(m->event, NULL, NULL, NULL);
+        if (r < 0)
+                log_debug_errno(r, "Failed allocate memory pressure event source, ignoring: %m");
+
+        r = sd_event_set_watchdog(m->event, true);
+        if (r < 0)
+                log_debug_errno(r, "Failed to enable watchdog handling, ignoring: %m");
+
+        r = sd_event_add_signal(m->event, NULL, SIGUSR2|SD_EVENT_SIGNAL_PROCMASK, on_sigusr2, m);
+        if (r < 0)
+                return r;
+
+        *ret = TAKE_PTR(m);
+        return 0;
+}
+
+Manager* manager_free(Manager *m) {
+        if (!m)
+                return NULL;
+
+        set_free(m->workers_fixed);
+        set_free(m->workers_dynamic);
+
+        m->deferred_start_worker_event_source = sd_event_source_unref(m->deferred_start_worker_event_source);
+
+        safe_close(m->listen_fd);
+
+#if HAVE_VMLINUX_H
+        sd_event_source_disable_unref(m->userns_restrict_bpf_ring_buffer_event_source);
+        if (m->userns_restrict_bpf_ring_buffer)
+                sym_ring_buffer__free(m->userns_restrict_bpf_ring_buffer);
+        userns_restrict_bpf_free(m->userns_restrict_bpf);
+#endif
+
+        safe_close(m->registry_fd);
+
+        sd_event_unref(m->event);
+
+        return mfree(m);
+}
+
+static size_t manager_current_workers(Manager *m) {
+        assert(m);
+
+        return set_size(m->workers_fixed) + set_size(m->workers_dynamic);
+}
+
+static int start_one_worker(Manager *m) {
+        _cleanup_(sd_event_source_disable_unrefp) sd_event_source *source = NULL;
+        bool fixed;
+        pid_t pid;
+        int r;
+
+        assert(m);
+
+        fixed = set_size(m->workers_fixed) < NSRESOURCE_WORKERS_MIN;
+
+        r = safe_fork_full(
+                        "(sd-worker)",
+                        /* stdio_fds= */ NULL,
+                        &m->listen_fd, 1,
+                        FORK_RESET_SIGNALS|FORK_DEATHSIG_SIGTERM|FORK_REOPEN_LOG|FORK_LOG|FORK_CLOSE_ALL_FDS,
+                        &pid);
+        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) {
+                        r = fd_cloexec(3, false);
+                        if (r < 0) {
+                                log_error_errno(r, "Failed to turn off O_CLOEXEC for fd 3: %m");
+                                _exit(EXIT_FAILURE);
+                        }
+                } else {
+                        if (dup2(m->listen_fd, 3) < 0) { /* dup2() creates with O_CLOEXEC off */
+                                log_error_errno(errno, "Failed to move listen fd to 3: %m");
+                                _exit(EXIT_FAILURE);
+                        }
+
+                        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");
+                        _exit(EXIT_FAILURE);
+                }
+
+                if (setenv("LISTEN_FDS", "1", 1) < 0) {
+                        log_error_errno(errno, "Failed to set $LISTEN_FDS: %m");
+                        _exit(EXIT_FAILURE);
+                }
+
+                if (setenv("NSRESOURCE_FIXED_WORKER", one_zero(fixed), 1) < 0) {
+                        log_error_errno(errno, "Failed to set $NSRESOURCE_FIXED_WORKER: %m");
+                        _exit(EXIT_FAILURE);
+                }
+
+#if HAVE_VMLINUX_H
+                bool supported = m->userns_restrict_bpf;
+#else
+                bool supported = false;
+#endif
+
+                /* Tell the workers whether to enable the userns API */
+                if (setenv("NSRESOURCE_API", one_zero(supported), 1) < 0) {
+                        log_error_errno(errno, "Failed to set $NSRESOURCE_API: %m");
+                        _exit(EXIT_FAILURE);
+                }
+
+                r = setenv_systemd_log_level();
+                if (r < 0) {
+                        log_error_errno(r, "Failed to set $SYSTEMD_LOG_LEVEL: %m");
+                        _exit(EXIT_FAILURE);
+                }
+
+                r = invoke_callout_binary(SYSTEMD_NSRESOURCEWORK_PATH, STRV_MAKE("systemd-nsresourcework", "xxxxxxxxxxxxxxxx")); /* With some extra space rename_process() can make use of */
+                log_error_errno(r, "Failed start worker process: %m");
+                _exit(EXIT_FAILURE);
+        }
+
+        r = sd_event_add_child(m->event, &source, pid, WEXITED, on_worker_exit, m);
+        if (r < 0)
+                return log_error_errno(r, "Failed to watch child " PID_FMT ": %m", pid);
+
+        r = set_ensure_put(
+                        fixed ? &m->workers_fixed : &m->workers_dynamic,
+                        &event_source_hash_ops,
+                        source);
+        if (r < 0)
+                return log_error_errno(r, "Failed to add child process to set: %m");
+
+        TAKE_PTR(source);
+
+        return 0;
+}
+
+static int start_workers(Manager *m, bool explicit_request) {
+        int r;
+
+        assert(m);
+
+        for (;;)  {
+                size_t n;
+
+                n = manager_current_workers(m);
+                if (n >= NSRESOURCE_WORKERS_MIN && (!explicit_request || n >= NSRESOURCE_WORKERS_MAX))
+                        break;
+
+                if (!ratelimit_below(&m->worker_ratelimit)) {
+
+                        /* If we keep starting workers too often but none sticks, let's fail the whole
+                         * daemon, something is wrong */
+                        if (n == 0) {
+                                sd_event_exit(m->event, EXIT_FAILURE);
+                                return log_error_errno(SYNTHETIC_ERRNO(EUCLEAN), "Worker threads requested too frequently, but worker count is zero, something is wrong.");
+                        }
+
+                        /* Otherwise, let's stop spawning more for a while. */
+                        log_warning("Worker threads requested too frequently, not starting new ones for a while.");
+
+                        if (!m->deferred_start_worker_event_source) {
+                                r = sd_event_add_time(
+                                                m->event,
+                                                &m->deferred_start_worker_event_source,
+                                                CLOCK_MONOTONIC,
+                                                ratelimit_end(&m->worker_ratelimit),
+                                                /* accuracy_usec= */ 0,
+                                                on_deferred_start_worker,
+                                                m);
+                                if (r < 0)
+                                        return log_error_errno(r, "Failed to allocate deferred start worker event source: %m");
+                        }
+
+                        break;
+                }
+
+                r = start_one_worker(m);
+                if (r < 0)
+                        return r;
+
+                explicit_request = false;
+        }
+
+        return 0;
+}
+
+static void manager_release_userns_bpf(Manager *m, uint64_t inode) {
+#if HAVE_VMLINUX_H
+        int r;
+
+        assert(m);
+
+        if (inode == 0)
+                return;
+
+        assert(m->userns_restrict_bpf);
+
+        r = userns_restrict_reset_by_inode(m->userns_restrict_bpf, inode);
+        if (r < 0)
+                return (void) log_warning_errno(r, "Failed to remove namespace inode from BPF map, ignoring: %m");
+#endif
+}
+
+static void manager_release_userns_fds(Manager *m, uint64_t inode) {
+        int r;
+
+        assert(m);
+        assert(inode != 0);
+
+        r = sd_notifyf(/* unset_environment= */ false,
+                       "FDSTOREREMOVE=1\n"
+                       "FDNAME=userns-%" PRIu64 "\n", inode);
+        if (r < 0)
+                log_warning_errno(r, "Failed to send fd store removal message, ignoring: %m");
+}
+
+static void manager_release_userns_by_inode(Manager *m, uint64_t inode) {
+        _cleanup_(userns_info_freep) UserNamespaceInfo *userns_info = NULL;
+        _cleanup_close_ int lock_fd = -EBADF;
+        int r;
+
+        assert(m);
+        assert(inode != 0);
+
+        lock_fd = userns_registry_lock(m->registry_fd);
+        if (lock_fd < 0)
+                return (void) log_error_errno(lock_fd, "Failed to lock registry: %m");
+
+        r = userns_registry_load_by_userns_inode(m->registry_fd, inode, &userns_info);
+        if (r < 0)
+                log_full_errno(r == -ENOENT ? LOG_DEBUG : LOG_WARNING, r,
+                               "Failed to find userns for inode %" PRIu64 ", ignoring: %m", inode);
+
+        if (userns_info && uid_is_valid(userns_info->start))
+                log_debug("Removing user namespace mapping %" PRIu64 " for UID " UID_FMT ".", inode, userns_info->start);
+        else
+                log_debug("Removing user namespace mapping %" PRIu64 ".", inode);
+
+        /* Remove the BPF rules */
+        manager_release_userns_bpf(m, inode);
+
+        /* Remove the resources from the fdstore */
+        manager_release_userns_fds(m, inode);
+
+        /* And finally remove the resources file from disk */
+        if (userns_info) {
+                /* Remove the cgroups of this userns */
+                r = userns_info_remove_cgroups(userns_info);
+                if (r < 0)
+                        log_warning_errno(r, "Failed to remove cgroups of user namespace: %m");
+
+                r = userns_registry_remove(m->registry_fd, userns_info);
+                if (r < 0)
+                        log_warning_errno(r, "Failed to remove user namespace '%s', ignoring.", userns_info->name);
+        }
+}
+
+static int manager_scan_registry(Manager *m, Set **registry_inodes) {
+        _cleanup_free_ DirectoryEntries *de = NULL;
+        int r;
+
+        assert(m);
+        assert(registry_inodes);
+        assert(m->registry_fd >= 0);
+
+        r = readdir_all(m->registry_fd, RECURSE_DIR_IGNORE_DOT, &de);
+        if (r < 0)
+                return log_error_errno(r, "Failed to enumerate registry.");
+
+        for (size_t i = 0; i < de->n_entries; i++) {
+                struct dirent *dentry = de->entries[i];
+                _cleanup_free_ char *u = NULL;
+                const char *e, *p;
+                uint64_t inode;
+
+                p = startswith(dentry->d_name, "i");
+                if (!p)
+                        continue;
+
+                e = endswith(p, ".userns");
+                if (!e)
+                        continue;
+
+                u = strndup(p, e - p);
+                if (!u)
+                        return log_oom();
+
+                r = safe_atou64(u, &inode);
+                if (r < 0) {
+                        log_warning_errno(r, "Failed to parse userns inode number from '%s', skipping: %m", dentry->d_name);
+                        continue;
+                }
+
+                if (inode > UINT32_MAX) { /* namespace inode numbers are 23bit only right now */
+                        log_warning("userns inode number outside of 32bit range, skipping.");
+                        continue;
+                }
+
+                if (set_ensure_put(registry_inodes, NULL, UINT32_TO_PTR(inode)) < 0)
+                        return log_oom();
+
+                log_debug("Found user namespace %" PRIu64 " in registry directory", inode);
+        }
+
+        return 0;
+}
+
+static int manager_make_listen_socket(Manager *m) {
+        static const union sockaddr_union sockaddr = {
+                .un.sun_family = AF_UNIX,
+                .un.sun_path = "/run/systemd/io.systemd.NamespaceResource",
+        };
+        int r;
+
+        assert(m);
+
+        if (m->listen_fd >= 0)
+                return 0;
+
+        m->listen_fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0);
+        if (m->listen_fd < 0)
+                return log_error_errno(errno, "Failed to bind on socket: %m");
+
+        (void) sockaddr_un_unlink(&sockaddr.un);
+
+        WITH_UMASK(0000)
+                if (bind(m->listen_fd, &sockaddr.sa, SOCKADDR_UN_LEN(sockaddr.un)) < 0)
+                        return log_error_errno(errno, "Failed to bind socket: %m");
+
+        r = mkdir_p("/run/systemd/userdb", 0755);
+        if (r < 0)
+                return log_error_errno(r, "Failed to create /run/systemd/userdb: %m");
+
+        r = symlink_idempotent("../io.systemd.NamespaceResource", "/run/systemd/userdb/io.systemd.NamespaceResource", /* make_relative= */ false);
+        if (r < 0)
+                return log_error_errno(r, "Failed to symlink userdb socket: %m");
+
+        if (listen(m->listen_fd, SOMAXCONN) < 0)
+                return log_error_errno(errno, "Failed to listen on socket: %m");
+
+        return 1;
+}
+
+static int manager_scan_listen_fds(Manager *m, Set **fdstore_inodes) {
+        _cleanup_strv_free_ char **names = NULL;
+        int n, r;
+
+        assert(m);
+        assert(fdstore_inodes);
+
+        n = sd_listen_fds_with_names(/* unset_environment= */ true, &names);
+        if (n < 0)
+                return log_error_errno(n, "Failed to determine number of passed file descriptors: %m");
+
+        for (int i = 0; i < n; i++) {
+                _cleanup_close_ int fd = SD_LISTEN_FDS_START + i; /* Take possession */
+                const char *e;
+
+                /* If this is a BPF allowlist related fd, just close it, but remember which start UIDs this covers */
+                e = startswith(names[i], "userns-");
+                if (e) {
+                        uint64_t inode;
+
+                        r = safe_atou64(e, &inode);
+                        if (r < 0) {
+                                log_warning_errno(r, "Failed to parse UID from fd name '%s', ignoring: %m", e);
+                                continue;
+                        }
+
+                        if (inode > UINT32_MAX) {
+                                log_warning("Inode number outside of 32bit range, ignoring");
+                                continue;
+                        }
+
+                        if (set_ensure_put(fdstore_inodes, NULL, UINT32_TO_PTR(inode)) < 0)
+                                return log_oom();
+
+                        continue;
+                }
+
+                /* We don't check the name for the stream socket, for compatibility with older versions */
+                r = sd_is_socket(fd, AF_UNIX, SOCK_STREAM, 1);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to detect if passed file descriptor is a socket: %m");
+                if (r > 0) {
+                        if (m->listen_fd >= 0)
+                                return log_error_errno(SYNTHETIC_ERRNO(ENOTUNIQ), "Passed more than one AF_UNIX/SOCK_STREAM socket, refusing.");
+
+                        m->listen_fd = TAKE_FD(fd);
+                        continue;
+                }
+
+                log_warning("Closing passed file descriptor %i (%s) we don't recognize.", fd, names[i]);
+        }
+
+        return 0;
+}
+
+#if HAVE_VMLINUX_H
+static int ringbuf_event(void *userdata, void *data, size_t size) {
+        Manager *m = ASSERT_PTR(userdata);
+        size_t n;
+
+        if ((size % sizeof(unsigned int)) != 0) /* Not multiples of "unsigned int"? */
+                return -EIO;
+
+        n = size / sizeof(unsigned int);
+        for (size_t i = 0; i < n; i++) {
+                const void *d;
+                uint64_t inode;
+
+                d = (const uint8_t*) data + i * sizeof(unsigned int);
+                inode = unaligned_read_ne32(d);
+
+                log_debug("Got BPF ring buffer notification that user namespace %" PRIu64 " is now dead.", inode);
+                manager_release_userns_by_inode(m, inode);
+        }
+
+        return 0;
+}
+
+static int on_ringbuf_io(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
+        Manager *m = ASSERT_PTR(userdata);
+        int r;
+
+        r = sym_ring_buffer__poll(m->userns_restrict_bpf_ring_buffer, 0);
+        if (r < 0)
+                return log_error_errno(r, "Got failure reading from BPF ring buffer: %m");
+
+        return 0;
+}
+
+static int manager_setup_bpf(Manager *m) {
+        int rb_fd = -EBADF, poll_fd = -EBADF, r;
+
+        assert(m);
+        assert(!m->userns_restrict_bpf);
+        assert(!m->userns_restrict_bpf_ring_buffer);
+        assert(!m->userns_restrict_bpf_ring_buffer_event_source);
+
+        r = userns_restrict_install(/* pin= */ true, &m->userns_restrict_bpf);
+        if (r < 0) {
+                log_notice_errno(r, "Proceeding with user namespace interfaces disabled.");
+                return 0;
+        }
+
+        rb_fd = sym_bpf_map__fd(m->userns_restrict_bpf->maps.userns_ringbuf);
+        if (rb_fd < 0)
+                return log_error_errno(rb_fd, "Failed to get fd of ring buffer: %m");
+
+        m->userns_restrict_bpf_ring_buffer = sym_ring_buffer__new(rb_fd, ringbuf_event, m, NULL);
+        if (!m->userns_restrict_bpf_ring_buffer)
+                return log_error_errno(errno, "Failed to allocate BPF ring buffer object: %m");
+
+        poll_fd = sym_ring_buffer__epoll_fd(m->userns_restrict_bpf_ring_buffer);
+        if (poll_fd < 0)
+                return log_error_errno(poll_fd, "Failed to get poll fd of ring buffer: %m");
+
+        r = sd_event_add_io(
+                        m->event,
+                        &m->userns_restrict_bpf_ring_buffer_event_source,
+                        poll_fd,
+                        EPOLLIN,
+                        on_ringbuf_io,
+                        m);
+        if (r < 0)
+                return log_error_errno(r, "Failed to allocate event source for BPF ring buffer: %m");
+
+        return 0;
+}
+#else
+static int manager_setup_bpf(Manager *m) {
+        log_notice("Not setting up BPF subsystem, as functionality has been disabled at compile time.");
+        return 0;
+}
+#endif
+
+int manager_startup(Manager *m) {
+        _cleanup_(set_freep) Set *fdstore_inodes = NULL, *registry_inodes = NULL;
+        void *p;
+        int r;
+
+        assert(m);
+        assert(m->registry_fd < 0);
+        assert(m->listen_fd < 0);
+
+        m->registry_fd = userns_registry_open_fd();
+        if (m->registry_fd < 0)
+                return log_error_errno(m->registry_fd, "Failed to open registry directory: %m");
+
+        r = manager_setup_bpf(m);
+        if (r < 0)
+                return r;
+
+        r = manager_scan_listen_fds(m, &fdstore_inodes);
+        if (r < 0)
+                return r;
+
+        r = manager_scan_registry(m, &registry_inodes);
+        if (r < 0)
+                return r;
+
+        /* If there are resources tied to UIDs not found in the registry, then release them */
+        SET_FOREACH(p, fdstore_inodes)  {
+                uint64_t inode;
+
+                if (set_contains(registry_inodes, p))
+                        continue;
+
+                inode = PTR_TO_UINT32(p);
+
+                log_debug("Found stale fd store entry for user namespace %" PRIu64 ", removing.", inode);
+                manager_release_userns_by_inode(m, inode);
+        }
+
+        r = manager_make_listen_socket(m);
+        if (r < 0)
+                return r;
+
+        /* Let's make sure every accept() call on this socket times out after 25s. This allows workers to be
+         * GC'ed on idle */
+        if (setsockopt(m->listen_fd, SOL_SOCKET, SO_RCVTIMEO, TIMEVAL_STORE(LISTEN_TIMEOUT_USEC), sizeof(struct timeval)) < 0)
+                return log_error_errno(errno, "Failed to se SO_RCVTIMEO: %m");
+
+        r = start_workers(m, /* explicit_request= */ false);
+        if (r < 0)
+                return r;
+
+        return 0;
+}
diff --git a/src/nsresourced/nsresourced-manager.h b/src/nsresourced/nsresourced-manager.h
new file mode 100644 (file)
index 0000000..5ecf378
--- /dev/null
@@ -0,0 +1,40 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include "sd-bus.h"
+#include "sd-event.h"
+
+typedef struct Manager Manager;
+
+#include "hashmap.h"
+#include "ratelimit.h"
+
+#define NSRESOURCE_WORKERS_MIN 5
+#define NSRESOURCE_WORKERS_MAX 4096
+
+struct Manager {
+        sd_event *event;
+
+        Set *workers_fixed;    /* Workers 0…NSRESOURCE_WORKERS_MIN */
+        Set *workers_dynamic;  /* Workers NSRESOURCES_WORKERS_MIN+1…NSRESOURCES_WORKERS_MAX */
+
+        int listen_fd;
+
+        RateLimit worker_ratelimit;
+
+        sd_event_source *deferred_start_worker_event_source;
+
+#if HAVE_VMLINUX_H
+        struct userns_restrict_bpf *userns_restrict_bpf;
+        struct ring_buffer *userns_restrict_bpf_ring_buffer;
+        sd_event_source *userns_restrict_bpf_ring_buffer_event_source;
+#endif
+
+        int registry_fd;
+};
+
+int manager_new(Manager **ret);
+Manager* manager_free(Manager *m);
+DEFINE_TRIVIAL_CLEANUP_FUNC(Manager*, manager_free);
+
+int manager_startup(Manager *m);
diff --git a/src/nsresourced/nsresourced.c b/src/nsresourced/nsresourced.c
new file mode 100644 (file)
index 0000000..7056897
--- /dev/null
@@ -0,0 +1,46 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include "daemon-util.h"
+#include "nsresourced-manager.h"
+#include "log.h"
+#include "main-func.h"
+#include "signal-util.h"
+
+static int run(int argc, char *argv[]) {
+        _cleanup_(manager_freep) Manager *m = NULL;
+        int r;
+
+        log_setup();
+
+        umask(0022);
+
+        if (argc != 1)
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "This program takes no arguments.");
+
+        if (setenv("SYSTEMD_BYPASS_USERDB", "io.systemd.NamespaceResource", 1) < 0)
+                return log_error_errno(errno, "Failed to set $SYSTEMD_BYPASS_USERDB: %m");
+
+        assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGCHLD) >= 0);
+
+        r = manager_new(&m);
+        if (r < 0)
+                return log_error_errno(r, "Could not create manager: %m");
+
+        r = manager_startup(m);
+        if (r < 0)
+                return log_error_errno(r, "Failed to start up daemon: %m");
+
+        _unused_ _cleanup_(notify_on_cleanup) const char *notify_stop = NULL;
+        notify_stop = notify_start(NOTIFY_READY, NOTIFY_STOPPING);
+
+        r = sd_event_loop(m->event);
+        if (r < 0)
+                return log_error_errno(r, "Event loop failed: %m");
+
+        return 0;
+}
+
+DEFINE_MAIN_FUNCTION(run);
diff --git a/src/nsresourced/nsresourcework.c b/src/nsresourced/nsresourcework.c
new file mode 100644 (file)
index 0000000..6bd2fed
--- /dev/null
@@ -0,0 +1,1782 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <fcntl.h>
+#include <linux/nsfs.h>
+#include <linux/veth.h>
+#include <sys/eventfd.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+
+#include "sd-daemon.h"
+#include "sd-netlink.h"
+
+#include "env-util.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "fs-util.h"
+#include "group-record.h"
+#include "io-util.h"
+#include "lock-util.h"
+#include "main-func.h"
+#include "missing_magic.h"
+#include "missing_mount.h"
+#include "missing_syscall.h"
+#include "mount-util.h"
+#include "mountpoint-util.h"
+#include "namespace-util.h"
+#include "netlink-util.h"
+#include "process-util.h"
+#include "random-util.h"
+#include "socket-util.h"
+#include "stat-util.h"
+#include "strv.h"
+#include "time-util.h"
+#include "uid-classification.h"
+#include "uid-range.h"
+#include "user-record-nss.h"
+#include "user-record.h"
+#include "user-util.h"
+#include "userdb.h"
+#include "userns-registry.h"
+#include "userns-restrict.h"
+#include "varlink-io.systemd.NamespaceResource.h"
+#include "varlink-io.systemd.UserDatabase.h"
+#include "varlink.h"
+
+#define ITERATIONS_MAX 64U
+#define RUNTIME_MAX_USEC (5 * USEC_PER_MINUTE)
+#define PRESSURE_SLEEP_TIME_USEC (50 * USEC_PER_MSEC)
+#define CONNECTION_IDLE_USEC (15 * USEC_PER_SEC)
+#define LISTEN_IDLE_USEC (90 * USEC_PER_SEC)
+#define USERNS_PER_UID 256
+
+typedef struct LookupParameters {
+        const char *user_name;
+        const char *group_name;
+        union {
+                uid_t uid;
+                gid_t gid;
+        };
+        const char *service;
+} LookupParameters;
+
+static int build_user_json(UserNamespaceInfo *userns_info, uid_t offset, JsonVariant **ret) {
+        _cleanup_free_ char *name = NULL, *realname = NULL;
+        UserDisposition disposition;
+        int r;
+
+        assert(userns_info);
+        assert(offset < userns_info->size);
+
+        if (asprintf(&name, "ns-%s-" UID_FMT, userns_info->name, offset) < 0)
+                return -ENOMEM;
+
+        if (userns_info->size > 1) {
+                disposition = USER_CONTAINER;
+                r = asprintf(&realname, "User " UID_FMT " of Allocated Namespace %s", offset, userns_info->name);
+        } else {
+                disposition = USER_DYNAMIC;
+                r = asprintf(&realname, "Allocated Namespace %s", userns_info->name);
+        }
+        if (r < 0)
+                return -ENOMEM;
+
+        return json_build(ret, JSON_BUILD_OBJECT(
+                                          JSON_BUILD_PAIR("userName", JSON_BUILD_STRING(name)),
+                                          JSON_BUILD_PAIR("uid", JSON_BUILD_UNSIGNED(userns_info->start + offset)),
+                                          JSON_BUILD_PAIR("gid", JSON_BUILD_UNSIGNED(GID_NOBODY)),
+                                          JSON_BUILD_PAIR("realName", JSON_BUILD_STRING(realname)),
+                                          JSON_BUILD_PAIR("homeDirectory", JSON_BUILD_CONST_STRING("/")),
+                                          JSON_BUILD_PAIR("shell", JSON_BUILD_STRING(NOLOGIN)),
+                                          JSON_BUILD_PAIR("locked", JSON_BUILD_BOOLEAN(true)),
+                                          JSON_BUILD_PAIR("service", JSON_BUILD_CONST_STRING("io.systemd.NamespaceResource")),
+                                          JSON_BUILD_PAIR("disposition", JSON_BUILD_STRING(user_disposition_to_string(disposition)))));
+}
+
+static int vl_method_get_user_record(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata) {
+
+        static const JsonDispatch dispatch_table[] = {
+                { "uid",      JSON_VARIANT_UNSIGNED, json_dispatch_uid_gid,      offsetof(LookupParameters, uid),       0 },
+                { "userName", JSON_VARIANT_STRING,   json_dispatch_const_string, offsetof(LookupParameters, user_name), 0 },
+                { "service",  JSON_VARIANT_STRING,   json_dispatch_const_string, offsetof(LookupParameters, service),   0 },
+                {}
+        };
+
+        _cleanup_(userns_info_freep) UserNamespaceInfo *userns_info = NULL;
+        _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
+        LookupParameters p = {
+                .uid = UID_INVALID,
+        };
+        uid_t offset;
+        int r;
+
+        assert(parameters);
+
+        r = varlink_dispatch(link, parameters, dispatch_table, &p);
+        if (r != 0)
+                return r;
+
+        if (!streq_ptr(p.service, "io.systemd.NamespaceResource"))
+                return varlink_error(link, "io.systemd.UserDatabase.BadService", NULL);
+
+        if (p.user_name) {
+                _cleanup_free_ char *n = NULL;
+                const char *e, *f;
+
+                e = startswith(p.user_name, "ns-");
+                if (!e)
+                        goto not_found;
+
+                f = strrchr(e, '-');
+                if (!f)
+                        goto not_found;
+
+                if (parse_uid(f+1, &offset) < 0)
+                        goto not_found;
+
+                n = strndup(e, f - e);
+                if (!n)
+                        return log_oom();
+
+                r = userns_registry_load_by_name(
+                                /* registry_fd= */ -EBADF,
+                                n,
+                                &userns_info);
+                if (r == -ENOENT)
+                        goto not_found;
+                if (r < 0)
+                        return r;
+
+                if (offset >= userns_info->size) /* Outside of range? */
+                        goto not_found;
+
+                if (uid_is_valid(p.uid) && p.uid != userns_info->start + offset)
+                        return varlink_error(link, "io.systemd.UserDatabase.ConflictingRecordFound", NULL);
+
+        } else if (uid_is_valid(p.uid)) {
+                uid_t start, uidmask;
+
+                if (uid_is_container(p.uid))
+                        uidmask = (uid_t) UINT32_C(0xFFFF0000);
+                else if (uid_is_dynamic(p.uid))
+                        uidmask = (uid_t) UINT32_C(0xFFFFFFFF);
+                else
+                        goto not_found;
+
+                start = p.uid & uidmask;
+                offset = p.uid - start;
+
+                r = userns_registry_load_by_start_uid(
+                                /* registry_fd= */ -EBADF,
+                                start,
+                                &userns_info);
+                if (r == -ENOENT)
+                        goto not_found;
+                if (r < 0)
+                        return r;
+
+                if (offset >= userns_info->size) /* Outside of range? */
+                        goto not_found;
+        } else
+                return varlink_error(link, "io.systemd.UserDatabase.EnumerationNotSupported", NULL);
+
+        r = build_user_json(userns_info, offset, &v);
+        if (r < 0)
+                return r;
+
+        return varlink_replyb(link, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("record", JSON_BUILD_VARIANT(v))));
+
+not_found:
+        return varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
+}
+
+static int build_group_json(UserNamespaceInfo *userns_info, gid_t offset, JsonVariant **ret) {
+        _cleanup_free_ char *name = NULL, *description = NULL;
+        UserDisposition disposition;
+        int r;
+
+        assert(userns_info);
+        assert(offset < userns_info->size);
+
+        if (asprintf(&name, "ns-%s-" GID_FMT, userns_info->name, offset) < 0)
+                return -ENOMEM;
+
+        if (userns_info->size > 1) {
+                disposition = USER_CONTAINER;
+                r = asprintf(&description, "Group " GID_FMT " of Allocated Namespace %s", offset, userns_info->name);
+        } else {
+                disposition = USER_DYNAMIC;
+                r = asprintf(&description, "Allocated Namespace %s", userns_info->name);
+        }
+        if (r < 0)
+                return -ENOMEM;
+
+        return json_build(ret, JSON_BUILD_OBJECT(
+                                          JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(name)),
+                                          JSON_BUILD_PAIR("gid", JSON_BUILD_UNSIGNED(userns_info->start + offset)),
+                                          JSON_BUILD_PAIR("description", JSON_BUILD_STRING(description)),
+                                          JSON_BUILD_PAIR("service", JSON_BUILD_CONST_STRING("io.systemd.NamespaceResource")),
+                                          JSON_BUILD_PAIR("disposition", JSON_BUILD_STRING(user_disposition_to_string(disposition)))));
+}
+
+static int vl_method_get_group_record(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata) {
+
+        static const JsonDispatch dispatch_table[] = {
+                { "gid",       JSON_VARIANT_UNSIGNED, json_dispatch_uid_gid,      offsetof(LookupParameters, gid),        0 },
+                { "groupName", JSON_VARIANT_STRING,   json_dispatch_const_string, offsetof(LookupParameters, group_name), 0 },
+                { "service",   JSON_VARIANT_STRING,   json_dispatch_const_string, offsetof(LookupParameters, service),    0 },
+                {}
+        };
+
+        _cleanup_(userns_info_freep) UserNamespaceInfo *userns_info = NULL;
+        _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
+        LookupParameters p = {
+                .gid = GID_INVALID,
+        };
+        gid_t offset;
+        int r;
+
+        assert(parameters);
+
+        r = varlink_dispatch(link, parameters, dispatch_table, &p);
+        if (r != 0)
+                return r;
+
+        if (!streq_ptr(p.service, "io.systemd.NamespaceResource"))
+                return varlink_error(link, "io.systemd.UserDatabase.BadService", NULL);
+
+        if (p.group_name) {
+                _cleanup_free_ char *n = NULL;
+                const char *e, *f;
+
+                e = startswith(p.group_name, "ns-");
+                if (!e)
+                        goto not_found;
+
+                f = strrchr(e, '-');
+                if (!f)
+                        goto not_found;
+
+                if (parse_gid(f+1, &offset) < 0)
+                        goto not_found;
+
+                n = strndup(e, f - e);
+                if (!n)
+                        return log_oom();
+
+                r = userns_registry_load_by_name(
+                                /* registry_fd= */ -EBADF,
+                                n,
+                                &userns_info);
+                if (r == -ENOENT)
+                        goto not_found;
+                if (r < 0)
+                        return r;
+
+                if (offset >= userns_info->size) /* Outside of range? */
+                        goto not_found;
+
+                if (gid_is_valid(p.gid) && p.uid != userns_info->start + offset)
+                        return varlink_error(link, "io.systemd.UserDatabase.ConflictingRecordFound", NULL);
+
+        } else if (gid_is_valid(p.gid)) {
+                gid_t start, gidmask;
+
+                if (gid_is_container(p.gid))
+                        gidmask = (gid_t) UINT32_C(0xFFFF0000);
+                else if (gid_is_dynamic(p.gid))
+                        gidmask = (gid_t) UINT32_C(0xFFFFFFFF);
+                else
+                        goto not_found;
+
+                start = p.gid & gidmask;
+                offset = p.gid - start;
+
+                r = userns_registry_load_by_start_uid(
+                                /* registry_fd= */ -EBADF,
+                                (uid_t) start,
+                                &userns_info);
+                if (r == -ENOENT)
+                        goto not_found;
+                if (r < 0)
+                        return r;
+
+                if (offset >= userns_info->size) /* Outside of range? */
+                        goto not_found;
+        } else
+                return varlink_error(link, "io.systemd.UserDatabase.EnumerationNotSupported", NULL);
+
+        r = build_group_json(userns_info, offset, &v);
+        if (r < 0)
+                return r;
+
+        return varlink_replyb(link, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("record", JSON_BUILD_VARIANT(v))));
+
+not_found:
+        return varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
+}
+
+static int vl_method_get_memberships(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata) {
+        static const JsonDispatch dispatch_table[] = {
+                { "userName",  JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParameters, user_name),  0 },
+                { "groupName", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParameters, group_name), 0 },
+                { "service",   JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParameters, service),    0 },
+                {}
+        };
+
+        LookupParameters p = {};
+        int r;
+
+        assert(parameters);
+
+        r = varlink_dispatch(link, parameters, dispatch_table, &p);
+        if (r != 0)
+                return r;
+
+        if (!streq_ptr(p.service, "io.systemd.NamespaceResource"))
+                return varlink_error(link, "io.systemd.UserDatabase.BadService", NULL);
+
+        /* We don't support auxiliary groups for namespace allocations */
+        return varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
+}
+
+static int uid_is_available(
+                int registry_dir_fd,
+                uid_t candidate) {
+
+        int r;
+
+        assert(registry_dir_fd >= 0);
+
+        log_debug("Checking if UID " UID_FMT " is available.", candidate);
+
+        r = userns_registry_uid_exists(registry_dir_fd, candidate);
+        if (r < 0)
+                return r;
+        if (r > 0)
+                return false;
+
+        r = userdb_by_uid(candidate, USERDB_AVOID_MULTIPLEXER, NULL);
+        if (r >= 0)
+                return false;
+        if (r != -ESRCH)
+                return r;
+
+        r = groupdb_by_gid(candidate, USERDB_AVOID_MULTIPLEXER, NULL);
+        if (r >= 0)
+                return false;
+        if (r != -ESRCH)
+                return r;
+
+        log_debug("UID " UID_FMT " is available.", candidate);
+
+        return true;
+}
+
+static int name_is_available(
+                int registry_dir_fd,
+                const char *name) {
+
+        _cleanup_free_ char *user_name = NULL;
+        int r;
+
+        assert(registry_dir_fd >= 0);
+        assert(name);
+
+        r = userns_registry_name_exists(registry_dir_fd, name);
+        if (r < 0)
+                return r;
+        if (r > 0)
+                return false;
+
+        user_name = strjoin("ns-", name, "-0");
+        if (!user_name)
+                return -ENOMEM;
+
+        r = userdb_by_name(user_name, USERDB_AVOID_MULTIPLEXER, NULL);
+        if (r >= 0)
+                return false;
+        if (r != -ESRCH)
+                return r;
+
+        r = groupdb_by_name(user_name, USERDB_AVOID_MULTIPLEXER, NULL);
+        if (r >= 0)
+                return false;
+        if (r != -ESRCH)
+                return r;
+
+        log_debug("Namespace name '%s' is available.", name);
+
+        return true;
+}
+
+static int allocate_now(
+                int registry_dir_fd,
+                UserNamespaceInfo *info,
+                int *ret_lock_fd) {
+
+        static const uint8_t hash_key[16] = {
+                0xd4, 0xd7, 0x33, 0xa7, 0x4d, 0xd3, 0x42, 0xcd,
+                0xaa, 0xe9, 0x45, 0xd0, 0xfb, 0xec, 0x79, 0xee,
+        };
+
+        _cleanup_(uid_range_freep) UIDRange *valid_range = NULL;
+        uid_t candidate, uidmin, uidmax, uidmask;
+        unsigned n_tries = 100;
+        int r;
+
+        /* Returns the following error codes:
+         *
+         * EBUSY   → all UID candidates we checked are already taken
+         * EEXIST  → the name for the userns already exists
+         * EDEADLK → the userns is already registered in the registry
+         */
+
+        assert(registry_dir_fd >= 0);
+        assert(info);
+
+        switch (info->size) {
+
+        case 0x10000U:
+                uidmin = CONTAINER_UID_BASE_MIN;
+                uidmax = CONTAINER_UID_BASE_MAX;
+                uidmask = (uid_t) UINT32_C(0xFFFF0000);
+                break;
+
+        case 1U:
+                uidmin = DYNAMIC_UID_MIN;
+                uidmax = DYNAMIC_UID_MAX;
+                uidmask = (uid_t) UINT32_C(0xFFFFFFFF);
+                break;
+
+        default:
+                assert_not_reached();
+        }
+
+        r = uid_range_load_userns(/* path= */ NULL, UID_RANGE_USERNS_INSIDE, &valid_range);
+        if (r < 0)
+                return r;
+
+        /* Check early whether we have any chance at all given our own uid range */
+        if (!uid_range_overlaps(valid_range, uidmin, uidmax))
+                return log_debug_errno(SYNTHETIC_ERRNO(EHOSTDOWN), "Relevant UID range not delegated, can't allocate.");
+
+        _cleanup_close_ int lock_fd = -EBADF;
+        lock_fd = userns_registry_lock(registry_dir_fd);
+        if (lock_fd < 0)
+                return log_debug_errno(lock_fd, "Failed to open nsresource registry lock file: %m");
+
+        /* Enforce limit on user namespaces per UID */
+        r = userns_registry_per_uid(registry_dir_fd, info->owner);
+        if (r < 0)
+                return log_debug_errno(r, "Failed to determine number of currently registered user namespaces per UID " UID_FMT ": %m", info->owner);
+        if (r >= USERNS_PER_UID)
+                return log_debug_errno(SYNTHETIC_ERRNO(EUSERS), "User already registered %i user namespaces, refusing.", r);
+
+        r = userns_registry_inode_exists(registry_dir_fd, info->userns_inode);
+        if (r < 0)
+                return r;
+        if (r > 0)
+                return -EDEADLK;
+
+        r = name_is_available(registry_dir_fd, info->name);
+        if (r < 0)
+                return r;
+        if (r == 0)
+                return -EEXIST;
+
+        for (candidate = siphash24_string(info->name, hash_key) & UINT32_MAX;; /* Start from a hash of the input name */
+             candidate = random_u32()) {                                 /* Use random values afterwards */
+
+                if (--n_tries <= 0)
+                        return log_debug_errno(SYNTHETIC_ERRNO(EBUSY), "Try limit hit, no UIDs available.");
+
+                candidate = (candidate % (uidmax - uidmin)) + uidmin;
+                candidate &= uidmask;
+
+                if (!uid_range_covers(valid_range, candidate, info->size))
+                        continue;
+
+                /* We only check the base UID for each range (!) */
+                r = uid_is_available(registry_dir_fd, candidate);
+                if (r < 0)
+                        return log_debug_errno(r, "Can't determine if UID range " UID_FMT " is available: %m", candidate);
+                if (r > 0) {
+                        info->start = candidate;
+
+                        log_debug("Allocating UID range " UID_FMT "…" UID_FMT, candidate, candidate + info->size - 1);
+
+                        if (ret_lock_fd)
+                                *ret_lock_fd = TAKE_FD(lock_fd);
+
+                        return 0;
+                }
+
+                log_debug("UID range " UID_FMT " already taken.", candidate);
+        }
+}
+
+static int write_userns(int usernsfd, const UserNamespaceInfo *userns_info) {
+        _cleanup_(sigkill_waitp) pid_t pid = 0;
+        _cleanup_close_ int efd = -EBADF;
+        uint64_t u;
+        int r;
+
+        assert(usernsfd >= 0);
+        assert(userns_info);
+        assert(uid_is_valid(userns_info->target));
+        assert(uid_is_valid(userns_info->start));
+        assert(userns_info->size > 0);
+        assert(userns_info->size <= UINT32_MAX - userns_info->start);
+
+        efd = eventfd(0, EFD_CLOEXEC);
+        if (efd < 0)
+                return log_error_errno(errno, "Failed to allocate eventfd(): %m");
+
+        r = safe_fork("(sd-userns)", FORK_RESET_SIGNALS|FORK_DEATHSIG_SIGKILL|FORK_LOG, &pid);
+        if (r < 0)
+                return r;
+        if (r == 0) {
+                /* child */
+
+                if (setns(usernsfd, CLONE_NEWUSER) < 0) {
+                        log_error_errno(errno, "Failed to join user namespace: %m");
+                        goto child_fail;
+                }
+
+                if (eventfd_write(efd, 1) < 0) {
+                        log_error_errno(errno, "Failed to ping event fd: %m");
+                        goto child_fail;
+                }
+
+                freeze();
+
+        child_fail:
+                _exit(EXIT_FAILURE);
+        }
+
+        /* Wait until child joined the user namespace */
+        if (eventfd_read(efd, &u) < 0)
+                return log_error_errno(errno, "Failed to wait for event fd: %m");
+
+        /* Now write mapping */
+
+        _cleanup_free_ char *pmap = NULL;
+
+        if (asprintf(&pmap, "/proc/" PID_FMT "/uid_map", pid) < 0)
+                return log_oom();
+
+        r = write_string_filef(pmap, 0, UID_FMT " " UID_FMT " " UID_FMT "\n", userns_info->target, userns_info->start, userns_info->size);
+        if (r < 0)
+                return log_error_errno(r, "Failed to write 'uid_map' file of user namespace: %m");
+
+        pmap = mfree(pmap);
+        if (asprintf(&pmap, "/proc/" PID_FMT "/gid_map", pid) < 0)
+                return log_oom();
+
+        r = write_string_filef(pmap, 0, GID_FMT " " GID_FMT " " GID_FMT "\n", (gid_t) userns_info->target, (gid_t) userns_info->start, (gid_t) userns_info->size);
+        if (r < 0)
+                return log_error_errno(r, "Failed to write 'gid_map' file of user namespace: %m");
+
+        /* We are done! */
+
+        log_debug("Successfully configured user namespace.");
+        return 0;
+}
+
+static int test_userns_api_support(Varlink *link) {
+        int r;
+
+        assert(link);
+
+        /* We only expose the userns API if our manager daemon told us this OK to do. It will set this
+         * boolean only if it managed to set up BPF correctly for itself (i.e. watches for userns going away
+         * via BPF APIs). This should make very sure we don't accidentally allow any of the userns stuff to
+         * go through without the BPF LSM in effect. */
+
+        r = getenv_bool("NSRESOURCE_API");
+        if (r < 0)
+                return log_error_errno(r, "Failed to parse $NSRESOURCE_API: %m");
+        if (r == 0)
+                return varlink_error(link, "io.systemd.NamespaceResource.UserNamespaceInterfaceNotSupported", NULL);
+
+        return 0;
+}
+
+static int validate_name(Varlink *link, const char *name, char **ret) {
+        _cleanup_free_ char *un = NULL;
+        int r;
+
+        assert(link);
+        assert(name);
+        assert(ret);
+
+        uid_t peer_uid;
+        r = varlink_get_peer_uid(link, &peer_uid);
+        if (r < 0)
+                return r;
+
+        if (peer_uid == 0) {
+                if (!userns_name_is_valid(name))
+                        return varlink_error_invalid_parameter_name(link, "name");
+
+                un = strdup(name);
+                if (!un)
+                        return -ENOMEM;
+        } else {
+                /* The the client is not root then prefix the name with the UID of the peer, so that they
+                 * live in separate namespaces and cannot steal each other's names. */
+
+                if (asprintf(&un, UID_FMT "-%s", peer_uid, name) < 0)
+                        return -ENOMEM;
+
+                if (!userns_name_is_valid(un))
+                        return varlink_error_invalid_parameter_name(link, "name");
+        }
+
+        *ret = TAKE_PTR(un);
+        return 0;
+}
+
+static int validate_target_and_size(Varlink *link, unsigned target, unsigned size) {
+        assert(link);
+
+        if (!IN_SET(size, 1U, 0x10000))
+                return varlink_error_invalid_parameter_name(link, "size");
+
+        if (!uid_is_valid(target) || target > UINT32_MAX - size)
+                return varlink_error_invalid_parameter_name(link, "target");
+
+        return 0;
+}
+
+static int validate_userns(Varlink *link, int userns_fd) {
+        int r;
+
+        assert(link);
+        assert(userns_fd >= 0);
+
+        r = fd_verify_safe_flags(userns_fd);
+        if (r < 0)
+                return log_debug_errno(r, "User namespace file descriptor has unsafe flags set: %m");
+
+        /* Validate this is actually a valid user namespace fd */
+        r = fd_is_ns(userns_fd, CLONE_NEWUSER);
+        if (r < 0)
+                return log_debug_errno(r, "Failed to check if user namespace fd is actually a user namespace: %m");
+        if (r == 0)
+                return varlink_error_invalid_parameter_name(link, "userNamespaceFileDescriptor");
+
+        /* And refuse the thing if it is our own */
+        r = is_our_namespace(userns_fd, NAMESPACE_USER);
+        if (r < 0)
+                return log_debug_errno(r, "Failed to check if user namespace fd refers to our own user namespace: %m");
+        if (r > 0)
+                return varlink_error_invalid_parameter_name(link, "userNamespaceFileDescriptor");
+
+        uid_t peer_uid;
+        r = varlink_get_peer_uid(link, &peer_uid);
+        if (r < 0)
+                return log_debug_errno(r, "Failed to acquire peer UID: %m");
+
+        if (peer_uid != 0) {
+                /* Refuse if the userns is not actually owned by our client. */
+                uid_t owner_uid;
+                if (ioctl(userns_fd, NS_GET_OWNER_UID, &owner_uid) < 0)
+                        return log_debug_errno(errno, "Failed to get owner UID of user namespace: %m");
+
+                if (owner_uid != peer_uid)
+                        return varlink_error_invalid_parameter_name(link, "userNamespaceFileDescriptor");
+        }
+
+        return 0;
+}
+
+static int validate_userns_is_empty(Varlink *link, int userns_fd) {
+        int r;
+
+        assert(link);
+        assert(userns_fd >= 0);
+
+        _cleanup_(uid_range_freep) UIDRange *range = NULL;
+        r = uid_range_load_userns_by_fd(userns_fd, UID_RANGE_USERNS_OUTSIDE, &range);
+        if (r < 0)
+                return log_debug_errno(r, "Failed to read userns UID range: %m");
+
+        if (!uid_range_is_empty(range))
+                return varlink_error_invalid_parameter_name(link, "userNamespaceFileDescriptor");
+
+        range = uid_range_free(range);
+        r = uid_range_load_userns_by_fd(userns_fd, GID_RANGE_USERNS_OUTSIDE, &range);
+        if (r < 0)
+                return log_debug_errno(r, "Failed to read userns GID range: %m");
+
+        if (!uid_range_is_empty(range))
+                return varlink_error_invalid_parameter_name(link, "userNamespaceFileDescriptor");
+
+        return 0;
+}
+
+typedef struct AllocateParameters {
+        const char *name;
+        unsigned size;
+        unsigned target;
+        unsigned userns_fd_idx;
+} AllocateParameters;
+
+static int vl_method_allocate_user_range(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata) {
+
+        static const JsonDispatch dispatch_table[] = {
+                { "name",                        JSON_VARIANT_STRING,        json_dispatch_const_string, offsetof(AllocateParameters, name),          JSON_MANDATORY },
+                { "size",                        _JSON_VARIANT_TYPE_INVALID, json_dispatch_uint,         offsetof(AllocateParameters, size),          JSON_MANDATORY },
+                { "target",                      _JSON_VARIANT_TYPE_INVALID, json_dispatch_uint,         offsetof(AllocateParameters, target),        0              },
+                { "userNamespaceFileDescriptor", _JSON_VARIANT_TYPE_INVALID, json_dispatch_uint,         offsetof(AllocateParameters, userns_fd_idx), JSON_MANDATORY },
+                {}
+        };
+
+        struct userns_restrict_bpf **bpf = ASSERT_PTR(userdata);
+        _cleanup_close_ int userns_fd = -EBADF, registry_dir_fd = -EBADF, lock_fd = -EBADF;
+        _cleanup_free_ char *userns_name = NULL;
+        uid_t peer_uid;
+        struct stat userns_st;
+        AllocateParameters p = {
+                .size = UINT_MAX,
+                .userns_fd_idx = UINT_MAX,
+        };
+        int r;
+
+        assert(link);
+        assert(parameters);
+
+        r = test_userns_api_support(link);
+        if (r != 0)
+                return r;
+
+        r = varlink_dispatch(link, parameters, dispatch_table, &p);
+        if (r != 0)
+                return r;
+
+        r = validate_name(link, p.name, &userns_name);
+        if (r != 0)
+                return r;
+
+        r = validate_target_and_size(link, p.target, p.size);
+        if (r != 0)
+                return r;
+
+        userns_fd = varlink_take_fd(link, p.userns_fd_idx);
+        if (userns_fd < 0)
+                return log_debug_errno(userns_fd, "Failed to take user namespace fd from Varlink connection: %m");
+
+        r = validate_userns(link, userns_fd);
+        if (r != 0)
+                return r;
+
+        r = validate_userns_is_empty(link, userns_fd);
+        if (r != 0)
+                return r;
+
+        if (fstat(userns_fd, &userns_st) < 0)
+                return log_debug_errno(errno, "Failed to fstat() user namespace fd: %m");
+
+        r = varlink_get_peer_uid(link, &peer_uid);
+        if (r < 0)
+                return r;
+
+        if (!*bpf) {
+                r = userns_restrict_install(/* pin= */ true, bpf);
+                if (r < 0)
+                        return r;
+        }
+
+        registry_dir_fd = userns_registry_open_fd();
+        if (registry_dir_fd < 0)
+                return registry_dir_fd;
+
+        _cleanup_(userns_info_freep) UserNamespaceInfo *userns_info = userns_info_new();
+        if (!userns_info)
+                return -ENOMEM;
+
+        userns_info->name = TAKE_PTR(userns_name);
+        if (!userns_info->name)
+                return -ENOMEM;
+
+        userns_info->owner = peer_uid;
+        userns_info->userns_inode = userns_st.st_ino;
+        userns_info->size = p.size;
+        userns_info->target = p.target;
+
+        r = allocate_now(registry_dir_fd, userns_info, &lock_fd);
+        if (r == -EHOSTDOWN) /* The needed UID range is not delegated to us */
+                return varlink_error(link, "io.systemd.NamespaceResource.DynamicRangeUnavailable", NULL);
+        if (r == -EBUSY)     /* All used up */
+                return varlink_error(link, "io.systemd.NamespaceResource.NoDynamicRange", NULL);
+        if (r == -EDEADLK)
+                return varlink_error(link, "io.systemd.NamespaceResource.UserNamespaceExists", NULL);
+        if (r == -EEXIST)
+                return varlink_error(link, "io.systemd.NamespaceResource.NameExists", NULL);
+        if (r < 0)
+                return r;
+
+        r = userns_registry_store(registry_dir_fd, userns_info);
+        if (r < 0)
+                return r;
+
+        /* Register the userns in the BPF map with an empty allowlist */
+        r = userns_restrict_put_by_fd(
+                        *bpf,
+                        userns_fd,
+                        /* replace= */ true,
+                        /* mount_fds= */ NULL,
+                        /* n_mount_fds= */ 0);
+        if (r < 0)
+                goto fail;
+
+        r = write_userns(userns_fd, userns_info);
+        if (r < 0)
+                goto fail;
+
+        lock_fd = safe_close(lock_fd);
+
+        /* Send user namespace and process fd to our manager process, which will watch the process and user namespace */
+        r = sd_pid_notifyf_with_fds(
+                        /* pid= */ 0,
+                        /* unset_environment= */ false,
+                        &userns_fd, 1,
+                        "FDSTORE=1\n"
+                        "FDNAME=userns-" INO_FMT "\n", userns_info->userns_inode);
+        if (r < 0)
+                goto fail;
+
+        /* Note, we'll not return UID values from the host, since the child might not run in the same
+         * user namespace as us. If they want to know the ranges they should read them off the userns fd, so
+         * that they are translated into their PoV */
+        return varlink_replyb(link, JSON_BUILD_EMPTY_OBJECT);
+
+fail:
+        /* Note: we don't have to clean-up the BPF maps in the error path: the bpf map type used will
+         * automatically do that once the userns inode goes away */
+        userns_registry_remove(registry_dir_fd, userns_info);
+        return r;
+}
+
+static int validate_userns_is_safe(Varlink *link, int userns_fd) {
+        int r;
+
+        assert(link);
+        assert(userns_fd >= 0);
+
+        /* Read the outside UID range and verify it isn't empty */
+        _cleanup_(uid_range_freep) UIDRange *outside_range = NULL;
+        r = uid_range_load_userns_by_fd(userns_fd, UID_RANGE_USERNS_OUTSIDE, &outside_range);
+        if (r < 0)
+                return log_debug_errno(r, "Failed to read userns UID range: %m");
+        if (uid_range_is_empty(outside_range))
+                return varlink_error_invalid_parameter_name(link, "userNamespaceFileDescriptor");
+
+        /* Read the outside GID range and check it is the same as the UID range */
+        _cleanup_(uid_range_freep) UIDRange *outside_range_gid = NULL;
+        r = uid_range_load_userns_by_fd(userns_fd, GID_RANGE_USERNS_OUTSIDE, &outside_range_gid);
+        if (r < 0)
+                return log_debug_errno(r, "Failed to read userns GID range: %m");
+        if (!uid_range_equal(outside_range, outside_range_gid))
+                return varlink_error_invalid_parameter_name(link, "userNamespaceFileDescriptor");
+
+        /* Read the inside UID range, and verify it matches the size of the outside UID range */
+        _cleanup_(uid_range_freep) UIDRange *inside_range = NULL;
+        r = uid_range_load_userns_by_fd(userns_fd, UID_RANGE_USERNS_INSIDE, &inside_range);
+        if (r < 0)
+                return log_debug_errno(r, "Failed to read userns contents: %m");
+        if (uid_range_size(outside_range) != uid_range_size(inside_range))
+                return log_debug_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "Uh, inside and outside UID range sizes don't match.");
+
+        /* Read the inside GID range, and verify it matches the inside UID range */
+        _cleanup_(uid_range_freep) UIDRange *inside_range_gid = NULL;
+        r = uid_range_load_userns_by_fd(userns_fd, GID_RANGE_USERNS_INSIDE, &inside_range_gid);
+        if (r < 0)
+                return log_debug_errno(r, "Failed to read userns contents: %m");
+        if (!uid_range_equal(inside_range, inside_range_gid))
+                return varlink_error_invalid_parameter_name(link, "userNamespaceFileDescriptor");
+
+        uid_t peer_uid;
+        r = varlink_get_peer_uid(link, &peer_uid);
+        if (r < 0)
+                return r;
+
+        uid_t peer_gid;
+        r = varlink_get_peer_gid(link, &peer_gid);
+        if (r < 0)
+                return r;
+
+        /* Insist that the first UID/GID in the range matches the client's UID/GID */
+        if (outside_range->entries[0].start != peer_uid ||
+            outside_range_gid->entries[0].start != peer_gid)
+                return varlink_error_invalid_parameter_name(link, "userNamespaceFileDescriptor");
+
+        /* If there are more than one UID in the range, then also insist that the first UID maps to root inside the userns */
+        if (uid_range_size(outside_range) > 1 && inside_range->entries[0].start != 0)
+                return varlink_error_invalid_parameter_name(link, "userNamespaceFileDescriptor");
+
+        return 0;
+}
+
+typedef struct RegisterParameters {
+        const char *name;
+        unsigned userns_fd_idx;
+} RegisterParameters;
+
+static int vl_method_register_user_namespace(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata) {
+
+        static const JsonDispatch dispatch_table[] = {
+                { "name",                        JSON_VARIANT_STRING,        json_dispatch_const_string, offsetof(RegisterParameters, name),          JSON_MANDATORY },
+                { "userNamespaceFileDescriptor", _JSON_VARIANT_TYPE_INVALID, json_dispatch_uint,         offsetof(RegisterParameters, userns_fd_idx), JSON_MANDATORY },
+                {}
+        };
+
+        struct userns_restrict_bpf **bpf = ASSERT_PTR(userdata);
+        _cleanup_close_ int userns_fd = -EBADF, registry_dir_fd = -EBADF;
+        _cleanup_free_ char *userns_name = NULL;
+        uid_t peer_uid;
+        struct stat userns_st;
+        RegisterParameters p = {
+                .userns_fd_idx = UINT_MAX,
+        };
+        int r;
+
+        assert(link);
+        assert(parameters);
+
+        r = test_userns_api_support(link);
+        if (r != 0)
+                return r;
+
+        r = varlink_dispatch(link, parameters, dispatch_table, &p);
+        if (r != 0)
+                return r;
+
+        r = validate_name(link, p.name, &userns_name);
+        if (r != 0)
+                return r;
+
+        userns_fd = varlink_take_fd(link, p.userns_fd_idx);
+        if (userns_fd < 0)
+                return userns_fd;
+
+        r = validate_userns(link, userns_fd);
+        if (r != 0)
+                return r;
+
+        r = validate_userns_is_safe(link, userns_fd);
+        if (r != 0)
+                return r;
+
+        if (fstat(userns_fd, &userns_st) < 0)
+                return log_debug_errno(errno, "Failed to fstat() user namespace fd: %m");
+
+        r = varlink_get_peer_uid(link, &peer_uid);
+        if (r < 0)
+                return r;
+
+        if (!*bpf) {
+                r = userns_restrict_install(/* pin= */ true, bpf);
+                if (r < 0)
+                        return r;
+        }
+
+        registry_dir_fd = userns_registry_open_fd();
+        if (registry_dir_fd < 0)
+                return registry_dir_fd;
+
+        _cleanup_close_ int lock_fd = -EBADF;
+        lock_fd = userns_registry_lock(registry_dir_fd);
+        if (lock_fd < 0)
+                return log_debug_errno(lock_fd, "Failed to open nsresource registry lock file: %m");
+
+        r = userns_registry_inode_exists(registry_dir_fd, userns_st.st_ino);
+        if (r < 0)
+                return r;
+        if (r > 0)
+                return varlink_error(link, "io.systemd.NamespaceResource.UserNamespaceExists", NULL);
+
+        r = name_is_available(registry_dir_fd, userns_name);
+        if (r < 0)
+                return r;
+        if (r == 0)
+                return varlink_error(link, "io.systemd.NamespaceResource.NameExists", NULL);
+
+        _cleanup_(userns_info_freep) UserNamespaceInfo *userns_info = userns_info_new();
+        if (!userns_info)
+                return -ENOMEM;
+
+        userns_info->name = TAKE_PTR(userns_name);
+        if (!userns_info->name)
+                return -ENOMEM;
+
+        userns_info->owner = peer_uid;
+        userns_info->userns_inode = userns_st.st_ino;
+
+        r = userns_registry_store(registry_dir_fd, userns_info);
+        if (r < 0)
+                return log_debug_errno(r, "Failed to update userns registry: %m");
+
+        /* Register the userns in the BPF map with an empty allowlist */
+        r = userns_restrict_put_by_fd(
+                        *bpf,
+                        userns_fd,
+                        /* replace= */ true,
+                        /* mount_fds= */ NULL,
+                        /* n_mount_fds= */ 0);
+        if (r < 0)
+                goto fail;
+
+        /* Send user namespace and process fd to our manager process, which will watch the process and user namespace */
+        r = sd_pid_notifyf_with_fds(
+                        /* pid= */ 0,
+                        /* unset_environment= */ false,
+                        &userns_fd, 1,
+                        "FDSTORE=1\n"
+                        "FDNAME=userns-" INO_FMT "\n", userns_info->userns_inode);
+        if (r < 0)
+                goto fail;
+
+        return varlink_replyb(link, JSON_BUILD_EMPTY_OBJECT);
+
+fail:
+        userns_registry_remove(registry_dir_fd, userns_info);
+        return r;
+}
+
+typedef struct AddMountParameters {
+        unsigned userns_fd_idx;
+        unsigned mount_fd_idx;
+} AddMountParameters;
+
+static int vl_method_add_mount_to_user_namespace(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata) {
+
+        static const JsonDispatch parameter_dispatch_table[] = {
+                { "userNamespaceFileDescriptor", _JSON_VARIANT_TYPE_INVALID, json_dispatch_uint, offsetof(AddMountParameters, userns_fd_idx), JSON_MANDATORY },
+                { "mountFileDescriptor",         _JSON_VARIANT_TYPE_INVALID, json_dispatch_uint, offsetof(AddMountParameters, mount_fd_idx),  JSON_MANDATORY },
+                {}
+        };
+
+        _cleanup_close_ int userns_fd = -EBADF, mount_fd = -EBADF, registry_dir_fd = -EBADF;
+        struct userns_restrict_bpf **bpf = ASSERT_PTR(userdata);
+        AddMountParameters p = {
+                .userns_fd_idx = UINT_MAX,
+                .mount_fd_idx = UINT_MAX,
+        };
+        int r, mnt_id = 0;
+        struct stat userns_st;
+        uid_t peer_uid;
+
+        assert(link);
+        assert(parameters);
+
+        r = test_userns_api_support(link);
+        if (r != 0)
+                return r;
+
+        /* Allowlisting arbitrary mounts is a privileged operation */
+        r = varlink_get_peer_uid(link, &peer_uid);
+        if (r < 0)
+                return r;
+        if (peer_uid != 0)
+                return varlink_error(link, VARLINK_ERROR_PERMISSION_DENIED, NULL);
+
+        r = varlink_dispatch(link, parameters, parameter_dispatch_table, &p);
+        if (r != 0)
+                return r;
+
+        userns_fd = varlink_take_fd(link, p.userns_fd_idx);
+        if (userns_fd < 0)
+                return userns_fd;
+
+        r = validate_userns(link, userns_fd);
+        if (r != 0)
+                return r;
+
+        if (fstat(userns_fd, &userns_st) < 0)
+                return -errno;
+
+        mount_fd = varlink_take_fd(link, p.mount_fd_idx);
+        if (mount_fd < 0)
+                return mount_fd;
+
+        r = fd_verify_safe_flags_full(mount_fd, O_PATH|O_DIRECTORY);
+        if (r < 0)
+                return log_debug_errno(r, "Mount file descriptor has unsafe flags set: %m");
+
+        r = fd_verify_directory(mount_fd);
+        if (r < 0)
+                return r;
+
+        r = path_get_mnt_id_at(mount_fd, NULL, &mnt_id);
+        if (r < 0)
+                return r;
+
+        registry_dir_fd = userns_registry_open_fd();
+        if (registry_dir_fd < 0)
+                return registry_dir_fd;
+
+        _cleanup_close_ int lock_fd = -EBADF;
+        lock_fd = userns_registry_lock(registry_dir_fd);
+        if (lock_fd < 0)
+                return log_debug_errno(lock_fd, "Failed to open nsresource registry lock file: %m");
+
+        _cleanup_(userns_info_freep) UserNamespaceInfo *userns_info = NULL;
+        r = userns_registry_load_by_userns_inode(
+                        registry_dir_fd,
+                        userns_st.st_ino,
+                        &userns_info);
+        if (r == -ENOENT)
+                return varlink_error(link, "io.systemd.NamespaceResource.UserNamespaceNotRegistered", NULL);
+        if (r < 0)
+                return r;
+
+        if (!*bpf) {
+                r = userns_restrict_install(/* pin= */ true, bpf);
+                if (r < 0)
+                        return r;
+        }
+
+        /* Pin the mount fd */
+        r = sd_pid_notifyf_with_fds(
+                        /* pid= */ 0,
+                        /* unset_environment= */ false,
+                        &mount_fd, 1,
+                        "FDSTORE=1\n"
+                        "FDNAME=userns-" INO_FMT "\n", userns_st.st_ino);
+        if (r < 0)
+                return r;
+
+        /* Add this mount to the user namespace's BPF map allowlist entry. */
+        r = userns_restrict_put_by_fd(
+                        *bpf,
+                        userns_fd,
+                        /* replace= */ false,
+                        &mount_fd,
+                        1);
+        if (r < 0)
+                return r;
+
+        if (userns_info->size > 0)
+                log_debug("Granting access to mount %i to user namespace " INO_FMT " ('%s' @ UID " UID_FMT ")",
+                          mnt_id, userns_st.st_ino, userns_info->name, userns_info->start);
+        else
+                log_debug("Granting access to mount %i to user namespace " INO_FMT " ('%s')",
+                          mnt_id, userns_st.st_ino, userns_info->name);
+
+        return varlink_replyb(link, JSON_BUILD_EMPTY_OBJECT);
+}
+
+static int validate_cgroup(Varlink *link, int fd, uint64_t *ret_cgroup_id) {
+        int r;
+
+        assert(link);
+        assert(fd >= 0);
+        assert(ret_cgroup_id);
+
+        r = fd_verify_safe_flags_full(fd, O_DIRECTORY);
+        if (r < 0)
+                return log_debug_errno(r, "Control group file descriptor has unsafe flags set: %m");
+
+        r = fd_verify_directory(fd);
+        if (r < 0)
+                return log_debug_errno(r, "Verification that cgroup fd refers to directory failed: %m");
+
+        r = fd_is_fs_type(fd, CGROUP2_SUPER_MAGIC);
+        if (r < 0)
+                return log_debug_errno(r, "Failed to check if cgroup fd actually refers to cgroupfs: %m");
+        if (r == 0)
+                return varlink_error_invalid_parameter_name(link, "controlGroupFileDescriptor");
+
+        r = cg_fd_get_cgroupid(fd, ret_cgroup_id);
+        if (r < 0)
+                return log_debug_errno(r, "Failed to read cgroup ID from cgroupfs: %m");
+
+        return 0;
+}
+
+typedef struct AddCGroupParameters {
+        unsigned userns_fd_idx;
+        unsigned cgroup_fd_idx;
+} AddCGroupParameters;
+
+static int vl_method_add_cgroup_to_user_namespace(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata) {
+        static const JsonDispatch parameter_dispatch_table[] = {
+                { "userNamespaceFileDescriptor", _JSON_VARIANT_TYPE_INVALID, json_dispatch_uint, offsetof(AddCGroupParameters, userns_fd_idx), JSON_MANDATORY },
+                { "controlGroupFileDescriptor",  _JSON_VARIANT_TYPE_INVALID, json_dispatch_uint, offsetof(AddCGroupParameters, cgroup_fd_idx), JSON_MANDATORY },
+                {}
+        };
+
+        _cleanup_close_ int userns_fd = -EBADF, cgroup_fd = -EBADF, registry_dir_fd = -EBADF;
+        AddCGroupParameters p = {
+                .userns_fd_idx = UINT_MAX,
+                .cgroup_fd_idx = UINT_MAX,
+        };
+        _cleanup_(userns_info_freep) UserNamespaceInfo *userns_info = NULL;
+        struct stat userns_st, cgroup_st;
+        uid_t peer_uid;
+        int r;
+
+        assert(link);
+        assert(parameters);
+
+        r = test_userns_api_support(link);
+        if (r != 0)
+                return r;
+
+        r = varlink_dispatch(link, parameters, parameter_dispatch_table, &p);
+        if (r != 0)
+                return r;
+
+        userns_fd = varlink_take_fd(link, p.userns_fd_idx);
+        if (userns_fd < 0)
+                return log_debug_errno(userns_fd, "Failed to take user namespace fd from Varlink connection: %m");
+
+        r = validate_userns(link, userns_fd);
+        if (r != 0)
+                return r;
+
+        if (fstat(userns_fd, &userns_st) < 0)
+                return log_debug_errno(errno, "Failed to fstat() user namespace fd: %m");
+
+        cgroup_fd = varlink_take_fd(link, p.cgroup_fd_idx);
+        if (cgroup_fd < 0)
+                return log_debug_errno(cgroup_fd, "Failed to take cgroup fd from Varlink connection: %m");
+
+        uint64_t cgroup_id;
+        r = validate_cgroup(link, cgroup_fd, &cgroup_id);
+        if (r != 0)
+                return r;
+
+        if (fstat(cgroup_fd, &cgroup_st) < 0)
+                return log_debug_errno(errno, "Failed to fstat() cgroup fd: %m");
+
+        registry_dir_fd = userns_registry_open_fd();
+        if (registry_dir_fd < 0)
+                return registry_dir_fd;
+
+        _cleanup_close_ int lock_fd = -EBADF;
+        lock_fd = userns_registry_lock(registry_dir_fd);
+        if (lock_fd < 0)
+                return lock_fd;
+
+        r = userns_registry_load_by_userns_inode(
+                        registry_dir_fd,
+                        userns_st.st_ino,
+                        &userns_info);
+        if (r == -ENOENT)
+                return varlink_error(link, "io.systemd.NamespaceResource.UserNamespaceNotRegistered", NULL);
+        if (r < 0)
+                return r;
+
+        /* The user namespace must have a user assigned */
+        if (userns_info->size == 0)
+                return varlink_error(link, "io.systemd.NamespaceResource.UserNamespaceWithoutUserRange", NULL);
+        if (userns_info_has_cgroup(userns_info, cgroup_id))
+                return varlink_error(link, "io.systemd.NamespaceResource.ControlGroupAlreadyAdded", NULL);
+        if (userns_info->n_cgroups > USER_NAMESPACE_CGROUPS_DELEGATE_MAX)
+                return varlink_error(link, "io.systemd.NamespaceResource.TooManyControlGroups", NULL);
+
+        /* Registering a cgroup for this client is only allowed for the root or the owner of a userns */
+        r = varlink_get_peer_uid(link, &peer_uid);
+        if (r < 0)
+                return log_debug_errno(r, "Failed to get connection peer: %m");
+        if (peer_uid != 0) {
+                if (peer_uid != userns_info->owner)
+                        return varlink_error(link, VARLINK_ERROR_PERMISSION_DENIED, NULL);
+
+                /* The cgroup must be owned by the owner of the userns */
+                if (cgroup_st.st_uid != userns_info->owner)
+                        return varlink_error(link, VARLINK_ERROR_PERMISSION_DENIED, NULL);
+        }
+
+        r = userns_info_add_cgroup(userns_info, cgroup_id);
+        if (r < 0)
+                return r;
+
+        r = userns_registry_store(registry_dir_fd, userns_info);
+        if (r < 0)
+                return r;
+
+        if (fchown(cgroup_fd, userns_info->start, userns_info->start) < 0)
+                return log_debug_errno(errno, "Failed to change ownership of cgroup: %m");
+
+        if (fchmod(cgroup_fd, 0755) < 0)
+                return log_debug_errno(errno, "Failed to change access mode of cgroup: %m");
+
+        FOREACH_STRING(attr, "cgroup.procs", "cgroup.subtree_control", "cgroup.threads") {
+                (void) fchmodat(cgroup_fd, attr, 0644, AT_SYMLINK_NOFOLLOW);
+                (void) fchownat(cgroup_fd, attr, userns_info->start, userns_info->start, AT_SYMLINK_NOFOLLOW);
+        }
+
+        log_debug("Granting ownership to cgroup %" PRIu64 " to userns " INO_FMT " ('%s' @ UID " UID_FMT ")",
+                  cgroup_id, userns_st.st_ino, userns_info->name, userns_info->start);
+
+        return varlink_replyb(link, JSON_BUILD_EMPTY_OBJECT);
+}
+
+static uint64_t hash_ifname_id(UserNamespaceInfo *userns_info, const char *ifname) {
+        struct siphash state;
+
+        assert(userns_info);
+
+        siphash24_init(&state, (const uint8_t[]) { 0xc4, 0x6c, 0x96, 0xe8, 0xad, 0x37, 0x4d, 0x5f, 0xa1, 0xae, 0xfe, 0x70, 0x40, 0xed, 0x41, 0x5f });
+        siphash24_compress_string(userns_info->name, &state);
+        siphash24_compress_byte(0, &state); /* separator */
+        siphash24_compress_string(strempty(ifname), &state);
+
+        return siphash24_finalize(&state);
+}
+
+static void hash_ether_addr(UserNamespaceInfo *userns_info, const char *ifname, uint64_t n, struct ether_addr *ret) {
+        struct siphash state;
+        uint64_t h;
+
+        assert(userns_info);
+        assert(ret);
+
+        siphash24_init(&state, (const uint8_t[]) { 0x36, 0xaa, 0xd1, 0x69, 0xc7, 0xe5, 0x4c, 0xaa, 0x1e, 0xb2, 0x9e, 0xb3, 0x3a, 0x6b, 0xd4, 0x71 });
+        siphash24_compress_string(userns_info->name, &state);
+        siphash24_compress_byte(0, &state); /* separator */
+        siphash24_compress_string(strempty(ifname), &state);
+        siphash24_compress_byte(0, &state); /* separator */
+        n = htole64(n); /* add the 'index' to the mix in an endianess-independent fashion */
+        siphash24_compress(&n, sizeof(n), &state);
+
+        h = htole64(siphash24_finalize(&state));
+
+        assert(sizeof(h) >= sizeof_field(struct ether_addr, ether_addr_octet));
+
+        memcpy(ret->ether_addr_octet, &h, sizeof_field(struct ether_addr, ether_addr_octet));
+        ether_addr_mark_random(ret);
+}
+
+static int create_veth(
+                int netns_fd,
+                const char *ifname_host,
+                const char *altifname_host,
+                struct ether_addr *mac_host,
+                const char *ifname_namespace,
+                struct ether_addr *mac_namespace) {
+
+        int r;
+
+        assert(netns_fd >= 0);
+        assert(ifname_host);
+        assert(mac_host);
+        assert(ifname_namespace);
+        assert(mac_namespace);
+
+        log_debug("Creating veth link on host %s (%s) with address %s to container as %s with address %s",
+                  ifname_host, strna(altifname_host), ETHER_ADDR_TO_STR(mac_host),
+                  ifname_namespace, ETHER_ADDR_TO_STR(mac_namespace));
+
+        _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
+        r = sd_netlink_open(&rtnl);
+        if (r < 0)
+                return r;
+
+        _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
+        r = sd_rtnl_message_new_link(rtnl, &m, RTM_NEWLINK, 0);
+        if (r < 0)
+                return log_error_errno(r, "Failed to allocate netlink message: %m");
+
+        r = sd_netlink_message_append_string(m, IFLA_IFNAME, ifname_host);
+        if (r < 0)
+                return log_error_errno(r, "Failed to add netlink interface name: %m");
+
+        r = sd_netlink_message_append_ether_addr(m, IFLA_ADDRESS, mac_host);
+        if (r < 0)
+                return log_error_errno(r, "Failed to add netlink MAC address: %m");
+
+        r = sd_netlink_message_open_container(m, IFLA_LINKINFO);
+        if (r < 0)
+                return log_error_errno(r, "Failed to open netlink container: %m");
+
+        r = sd_netlink_message_open_container_union(m, IFLA_INFO_DATA, "veth");
+        if (r < 0)
+                return log_error_errno(r, "Failed to open netlink container: %m");
+
+        r = sd_netlink_message_open_container(m, VETH_INFO_PEER);
+        if (r < 0)
+                return log_error_errno(r, "Failed to open netlink container: %m");
+
+        r = sd_netlink_message_append_string(m, IFLA_IFNAME, ifname_namespace);
+        if (r < 0)
+                return log_error_errno(r, "Failed to add netlink interface name: %m");
+
+        r = sd_netlink_message_append_ether_addr(m, IFLA_ADDRESS, mac_namespace);
+        if (r < 0)
+                return log_error_errno(r, "Failed to add netlink MAC address: %m");
+
+        r = sd_netlink_message_append_u32(m, IFLA_NET_NS_FD, netns_fd);
+        if (r < 0)
+                return log_error_errno(r, "Failed to add netlink namespace field: %m");
+
+        r = sd_netlink_message_close_container(m);
+        if (r < 0)
+                return log_error_errno(r, "Failed to close netlink container: %m");
+
+        r = sd_netlink_message_close_container(m);
+        if (r < 0)
+                return log_error_errno(r, "Failed to close netlink container: %m");
+
+        r = sd_netlink_message_close_container(m);
+        if (r < 0)
+                return log_error_errno(r, "Failed to close netlink container: %m");
+
+        r = sd_netlink_call(rtnl, m, 0, NULL);
+        if (r < 0)
+                return log_error_errno(r, "Failed to add new veth interfaces (%s:%s): %m", ifname_host, ifname_namespace);
+
+        r = rtnl_set_link_alternative_names_by_ifname(&rtnl, ifname_host, STRV_MAKE(altifname_host));
+        if (r < 0)
+                log_warning_errno(r, "Failed to set alternative interface name to '%s', ignoring: %m", altifname_host);
+
+        return 0;
+}
+
+static int validate_netns(Varlink *link, int userns_fd, int netns_fd) {
+        int r;
+
+        assert(link);
+        assert(userns_fd >= 0);
+        assert(netns_fd >= 0);
+
+        r = fd_verify_safe_flags(netns_fd);
+        if (r < 0)
+                return log_debug_errno(r, "Network namespace file descriptor has unsafe flags set: %m");
+
+        /* Validate this is actually a valid network namespace fd */
+        r = fd_is_ns(netns_fd, CLONE_NEWNET);
+        if (r < 0)
+                return r;
+        if (r == 0)
+                return varlink_error_invalid_parameter_name(link, "networkNamespaceFileDescriptor");
+
+        /* And refuse the thing if it is our own */
+        r = is_our_namespace(netns_fd, NAMESPACE_NET);
+        if (r < 0)
+                return r;
+        if (r > 0)
+                return varlink_error_invalid_parameter_name(link, "networkNamespaceFileDescriptor");
+
+        /* Check if the netns actually belongs to the userns */
+        _cleanup_close_ int owner_userns_fd = -EBADF;
+        owner_userns_fd = ioctl(netns_fd, NS_GET_USERNS);
+        if (owner_userns_fd < 0)
+                return -errno;
+
+        r = inode_same_at(owner_userns_fd, /* path_a= */ NULL, userns_fd, /* path_b= */ NULL, AT_EMPTY_PATH);
+        if (r < 0)
+                return r;
+        if (r == 0)
+                return varlink_error_invalid_parameter_name(link, "networkNamespaceFileDescriptor");
+
+        uid_t peer_uid;
+        r = varlink_get_peer_uid(link, &peer_uid);
+        if (r < 0)
+                return r;
+
+        if (peer_uid != 0) {
+                /* Refuse if the netns is not actually owned by our client. */
+
+                uid_t owner_uid;
+                if (ioctl(owner_userns_fd, NS_GET_OWNER_UID, &owner_uid) < 0)
+                        return -errno;
+
+                if (owner_uid != peer_uid)
+                        return varlink_error_invalid_parameter_name(link, "networkNamespaceFileDescriptor");
+        }
+
+        return 0;
+}
+
+typedef struct AddNetworkParameters {
+        unsigned userns_fd_idx;
+        unsigned netns_fd_idx;
+        const char *ifname;
+        const char *mode;
+} AddNetworkParameters;
+
+static int vl_method_add_netif_to_user_namespace(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata) {
+        static const JsonDispatch parameter_dispatch_table[] = {
+                { "userNamespaceFileDescriptor",    _JSON_VARIANT_TYPE_INVALID, json_dispatch_uint,         offsetof(AddNetworkParameters, userns_fd_idx), JSON_MANDATORY },
+                { "networkNamespaceFileDescriptor", _JSON_VARIANT_TYPE_INVALID, json_dispatch_uint,         offsetof(AddNetworkParameters, netns_fd_idx),  JSON_MANDATORY },
+                { "namespaceInterfaceName",         JSON_VARIANT_STRING,        json_dispatch_const_string, offsetof(AddNetworkParameters, ifname),        0              },
+                { "mode",                           JSON_VARIANT_STRING,        json_dispatch_const_string, offsetof(AddNetworkParameters, mode),          JSON_MANDATORY },
+                {}
+        };
+
+        _cleanup_close_ int userns_fd = -EBADF, netns_fd = -EBADF, registry_dir_fd = -EBADF;
+        AddNetworkParameters p = {
+                .userns_fd_idx = UINT_MAX,
+        };
+        _cleanup_(userns_info_freep) UserNamespaceInfo *userns_info = NULL;
+        struct stat userns_st;
+        uid_t peer_uid;
+        int r;
+
+        assert(link);
+        assert(parameters);
+
+        r = test_userns_api_support(link);
+        if (r != 0)
+                return r;
+
+        r = varlink_dispatch(link, parameters, parameter_dispatch_table, &p);
+        if (r != 0)
+                return r;
+
+        userns_fd = varlink_take_fd(link, p.userns_fd_idx);
+        if (userns_fd < 0)
+                return userns_fd;
+
+        r = validate_userns(link, userns_fd);
+        if (r != 0)
+                return r;
+
+        if (fstat(userns_fd, &userns_st) < 0)
+                return -errno;
+
+        netns_fd = varlink_take_fd(link, p.netns_fd_idx);
+        if (netns_fd < 0)
+                return netns_fd;
+
+        r = validate_netns(link, userns_fd, netns_fd);
+        if (r != 0)
+                return r;
+
+        if (!streq_ptr(p.mode, "veth"))
+                return varlink_error_invalid_parameter_name(link, "mode");
+
+        if (p.ifname && !ifname_valid(p.ifname))
+                return varlink_error_invalid_parameter_name(link, "interfaceName");
+
+        registry_dir_fd = userns_registry_open_fd();
+        if (registry_dir_fd < 0)
+                return registry_dir_fd;
+
+        _cleanup_close_ int lock_fd = -EBADF;
+        lock_fd = userns_registry_lock(registry_dir_fd);
+        if (lock_fd < 0)
+                return log_debug_errno(lock_fd, "Failed to open nsresource registry lock file: %m");
+
+        r = userns_registry_load_by_userns_inode(
+                        registry_dir_fd,
+                        userns_st.st_ino,
+                        &userns_info);
+        if (r == -ENOENT)
+                return varlink_error(link, "io.systemd.NamespaceResource.UserNamespaceNotRegistered", NULL);
+        if (r < 0)
+                return r;
+
+        /* Registering a network interface for this client is only allowed for the root or the owner of a userns */
+        r = varlink_get_peer_uid(link, &peer_uid);
+        if (r < 0)
+                return r;
+        if (peer_uid != 0 && peer_uid != userns_info->owner)
+                return varlink_error(link, VARLINK_ERROR_PERMISSION_DENIED, NULL);
+
+        _cleanup_free_ char *ifname_host = NULL, *altifname_host = NULL;
+        const char *ifname_namespace = p.ifname ?: "host0";
+
+        /* The short ifname is just too short to generate readable and unique names where unprivileged users
+         * can't take each others names. Hence just hash it. The alternative name however contains more useful
+         * information. */
+        if (asprintf(&ifname_host, "ns-%08" PRIx64, hash_ifname_id(userns_info, p.ifname)) < 0)
+                return -ENOMEM;
+        strshorten(ifname_host, IFNAMSIZ-1);
+
+        if (p.ifname)
+                r = asprintf(&altifname_host, "ns-" UID_FMT "-%s-%s", userns_info->owner, userns_info->name, p.ifname);
+        else
+                r = asprintf(&altifname_host, "ns-" UID_FMT "-%s", userns_info->owner, userns_info->name);
+        if (r < 0)
+                return -ENOMEM;
+
+        struct ether_addr ether_addr_host, ether_addr_namespace;
+
+        hash_ether_addr(userns_info, p.ifname, 0, &ether_addr_host);
+        hash_ether_addr(userns_info, p.ifname, 1, &ether_addr_namespace);
+
+        r = create_veth(netns_fd,
+                        ifname_host, altifname_host, &ether_addr_host,
+                        ifname_namespace, &ether_addr_namespace);
+        if (r < 0)
+                return r;
+
+        log_debug("Adding veth tunnel %s from host to userns " INO_FMT " ('%s' @ UID " UID_FMT ", interface %s).",
+                  ifname_host, userns_st.st_ino, userns_info->name, userns_info->start, ifname_namespace);
+
+        return varlink_replyb(link, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("hostInterfaceName", JSON_BUILD_STRING(ifname_host)),
+                                                      JSON_BUILD_PAIR("namespaceInterfaceName", JSON_BUILD_STRING(ifname_namespace))));
+}
+
+static int process_connection(VarlinkServer *server, int _fd) {
+        _cleanup_close_ int fd = TAKE_FD(_fd); /* always take possession */
+        _cleanup_(varlink_close_unrefp) Varlink *vl = NULL;
+        int r;
+
+        r = varlink_server_add_connection(server, fd, &vl);
+        if (r < 0)
+                return log_error_errno(r, "Failed to add connection: %m");
+
+        TAKE_FD(fd);
+        vl = varlink_ref(vl);
+
+        r = varlink_set_allow_fd_passing_input(vl, true);
+        if (r < 0)
+                return log_error_errno(r, "Failed to enable fd passing for read: %m");
+
+        r = varlink_set_allow_fd_passing_output(vl, true);
+        if (r < 0)
+                return log_error_errno(r, "Failed to enable fd passing for write: %m");
+
+        for (;;) {
+                r = varlink_process(vl);
+                if (r == -ENOTCONN) {
+                        log_debug("Connection terminated.");
+                        break;
+                }
+                if (r < 0)
+                        return log_error_errno(r, "Failed to process connection: %m");
+                if (r > 0)
+                        continue;
+
+                r = varlink_wait(vl, CONNECTION_IDLE_USEC);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to wait for connection events: %m");
+                if (r == 0)
+                        break;
+        }
+
+        return 0;
+}
+
+static int run(int argc, char *argv[]) {
+        _cleanup_(userns_restrict_bpf_freep) struct userns_restrict_bpf *bpf = NULL;
+        usec_t start_time, listen_idle_usec, last_busy_usec = USEC_INFINITY;
+        _cleanup_(varlink_server_unrefp) VarlinkServer *server = NULL;
+        _cleanup_(pidref_done) PidRef parent = PIDREF_NULL;
+        unsigned n_iterations = 0;
+        int m, listen_fd, r;
+
+        log_setup();
+
+        m = sd_listen_fds(false);
+        if (m < 0)
+                return log_error_errno(m, "Failed to determine number of listening fds: %m");
+        if (m == 0)
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "No socket to listen on received.");
+        if (m > 1)
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Worker can only listen on a single socket at a time.");
+
+        listen_fd = SD_LISTEN_FDS_START;
+
+        r = fd_nonblock(listen_fd, false);
+        if (r < 0)
+                return log_error_errno(r, "Failed to turn off non-blocking mode for listening socket: %m");
+
+        r = varlink_server_new(&server, VARLINK_SERVER_INHERIT_USERDATA);
+        if (r < 0)
+                return log_error_errno(r, "Failed to allocate server: %m");
+
+        r = varlink_server_add_interface_many(
+                        server,
+                        &vl_interface_io_systemd_NamespaceResource,
+                        &vl_interface_io_systemd_UserDatabase);
+        if (r < 0)
+                return log_error_errno(r, "Failed to add UserDatabase and NamespaceResource interface to varlink server: %m");
+
+        r = varlink_server_bind_method_many(
+                        server,
+                        "io.systemd.NamespaceResource.AllocateUserRange",              vl_method_allocate_user_range,
+                        "io.systemd.NamespaceResource.RegisterUserNamespace",          vl_method_register_user_namespace,
+                        "io.systemd.NamespaceResource.AddMountToUserNamespace",        vl_method_add_mount_to_user_namespace,
+                        "io.systemd.NamespaceResource.AddControlGroupToUserNamespace", vl_method_add_cgroup_to_user_namespace,
+                        "io.systemd.NamespaceResource.AddNetworkToUserNamespace",      vl_method_add_netif_to_user_namespace,
+                        "io.systemd.UserDatabase.GetUserRecord",                       vl_method_get_user_record,
+                        "io.systemd.UserDatabase.GetGroupRecord",                      vl_method_get_group_record,
+                        "io.systemd.UserDatabase.GetMemberships",                      vl_method_get_memberships);
+        if (r < 0)
+                return log_error_errno(r, "Failed to bind methods: %m");
+
+        varlink_server_set_userdata(server, &bpf);
+
+        r = getenv_bool("NSRESOURCE_FIXED_WORKER");
+        if (r < 0)
+                return log_error_errno(r, "Failed to parse NSRESOURCE_FIXED_WORKER: %m");
+        listen_idle_usec = r ? USEC_INFINITY : LISTEN_IDLE_USEC;
+
+        r = pidref_set_parent(&parent);
+        if (r < 0)
+                return log_error_errno(r, "Failed to acquire pidfd of parent process: %m");
+
+        start_time = now(CLOCK_MONOTONIC);
+
+        for (;;) {
+                _cleanup_close_ int fd = -EBADF;
+                usec_t n;
+
+                /* Exit the worker in regular intervals, to flush out all memory use */
+                if (n_iterations++ > ITERATIONS_MAX) {
+                        log_debug("Exiting worker, processed %u iterations, that's enough.", n_iterations);
+                        break;
+                }
+
+                n = now(CLOCK_MONOTONIC);
+                if (n >= usec_add(start_time, RUNTIME_MAX_USEC)) {
+                        log_debug("Exiting worker, ran for %s, that's enough.",
+                                  FORMAT_TIMESPAN(usec_sub_unsigned(n, start_time), 0));
+                        break;
+                }
+
+                if (last_busy_usec == USEC_INFINITY)
+                        last_busy_usec = n;
+                else if (listen_idle_usec != USEC_INFINITY && n >= usec_add(last_busy_usec, listen_idle_usec)) {
+                        log_debug("Exiting worker, been idle for %s.",
+                                  FORMAT_TIMESPAN(usec_sub_unsigned(n, last_busy_usec), 0));
+                        break;
+                }
+
+                (void) rename_process("systemd-nsresourcework: waiting...");
+                fd = RET_NERRNO(accept4(listen_fd, NULL, NULL, SOCK_NONBLOCK|SOCK_CLOEXEC));
+                (void) rename_process("systemd-nsresourcework: processing...");
+
+                if (fd == -EAGAIN)
+                        continue; /* The listening socket has SO_RECVTIMEO set, hence a timeout is expected
+                                   * after a while, let's check if it's time to exit though. */
+                if (fd == -EINTR)
+                        continue; /* Might be that somebody attached via strace, let's just continue in that
+                                   * case */
+                if (fd < 0)
+                        return log_error_errno(fd, "Failed to accept() from listening socket: %m");
+
+                if (now(CLOCK_MONOTONIC) <= usec_add(n, PRESSURE_SLEEP_TIME_USEC)) {
+                        /* We only slept a very short time? If so, let's see if there are more sockets
+                         * pending, and if so, let's ask our parent for more workers */
+
+                        r = fd_wait_for_event(listen_fd, POLLIN, 0);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to test for POLLIN on listening socket: %m");
+
+                        if (FLAGS_SET(r, POLLIN)) {
+                                r = pidref_kill(&parent, SIGUSR2);
+                                if (r == -ESRCH)
+                                        return log_error_errno(r, "Parent already died?");
+                                if (r < 0)
+                                        return log_error_errno(r, "Failed to send SIGUSR2 signal to parent. %m");
+                        }
+                }
+
+                (void) process_connection(server, TAKE_FD(fd));
+                last_busy_usec = USEC_INFINITY;
+        }
+
+        return 0;
+}
+
+DEFINE_MAIN_FUNCTION(run);
diff --git a/src/nsresourced/test-userns-restrict.c b/src/nsresourced/test-userns-restrict.c
new file mode 100644 (file)
index 0000000..7ef1d7b
--- /dev/null
@@ -0,0 +1,182 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <sys/eventfd.h>
+
+#include "fd-util.h"
+#include "main-func.h"
+#include "missing_mount.h"
+#include "missing_syscall.h"
+#include "namespace-util.h"
+#include "process-util.h"
+#include "rm-rf.h"
+#include "tmpfile-util.h"
+#include "userns-restrict.h"
+
+static int make_tmpfs_fsmount(void) {
+        _cleanup_close_ int fsfd = -EBADF, mntfd = -EBADF;
+
+        fsfd = fsopen("tmpfs", FSOPEN_CLOEXEC);
+        assert_se(fsfd >= 0);
+        assert_se(fsconfig(fsfd, FSCONFIG_CMD_CREATE, NULL, NULL, 0) >= 0);
+
+        mntfd = fsmount(fsfd, FSMOUNT_CLOEXEC, 0);
+        assert_se(mntfd >= 0);
+
+        return TAKE_FD(mntfd);
+}
+
+static void test_works_reg(int parent_fd, const char *fname) {
+        _cleanup_close_ int fd = -EBADF;
+
+        fd = openat(parent_fd, fname, O_RDWR|O_CREAT|O_CLOEXEC, 0666);
+        assert_se(fd >= 0);
+}
+
+static void test_fails_reg(int parent_fd, const char *fname) {
+        errno = 0;
+        assert_se(openat(parent_fd, fname, O_RDWR|O_CREAT|O_CLOEXEC, 0666) < 0);
+        assert_se(errno == EPERM);
+}
+
+static void test_works_dir(int parent_fd, const char *fname) {
+        assert_se(mkdirat(parent_fd, fname, 0666) >= 0);
+}
+
+static void test_fails_dir(int parent_fd, const char *fname) {
+        errno = 0;
+        assert_se(mkdirat(parent_fd, fname, 0666) < 0);
+        assert_se(errno == EPERM);
+}
+
+static int run(int argc, char *argv[]) {
+        _cleanup_(userns_restrict_bpf_freep) struct userns_restrict_bpf *obj = NULL;
+        _cleanup_close_ int userns_fd = -EBADF, host_fd1 = -EBADF, host_tmpfs = -EBADF, afd = -EBADF, bfd = -EBADF;
+        _cleanup_(rm_rf_physical_and_freep) char *t = NULL;
+        _cleanup_(sigkill_waitp) pid_t pid = 0;
+        int r;
+
+        log_set_max_level(LOG_DEBUG);
+        log_open();
+
+        r = userns_restrict_install(/* pin= */ false, &obj);
+        if (ERRNO_IS_NOT_SUPPORTED(r)) {
+                log_notice("Skipping test, LSM-BPF logic not supported.");
+                return EXIT_TEST_SKIP;
+        }
+        if (ERRNO_IS_PRIVILEGE(r)) {
+                log_notice("Skipping test, lacking privileges.");
+                return EXIT_TEST_SKIP;
+        }
+        if (r < 0)
+                return r;
+
+        assert_se(mkdtemp_malloc(NULL, &t) >= 0);
+
+        host_fd1 = open(t, O_DIRECTORY|O_CLOEXEC);
+        assert_se(host_fd1 >= 0);
+
+        host_tmpfs = make_tmpfs_fsmount();
+        assert_se(host_tmpfs >= 0);
+
+        userns_fd = userns_acquire("0 0 1", "0 0 1");
+        if (userns_fd < 0)
+                return log_error_errno(userns_fd, "Failed to make user namespace: %m");
+
+        r = userns_restrict_put_by_fd(
+                        obj,
+                        userns_fd,
+                        /* replace= */ true,
+                        /* mount_fds= */ NULL,
+                        /* n_mount_fds= */ 0);
+        if (r < 0)
+                return log_error_errno(r, "Failed to restrict user namespace: %m");
+
+        afd = eventfd(0, EFD_CLOEXEC);
+        bfd = eventfd(0, EFD_CLOEXEC);
+
+        assert_se(afd >= 0 && bfd >= 0);
+
+        r = safe_fork("(test)", FORK_DEATHSIG_SIGKILL, &pid);
+        assert_se(r >= 0);
+        if (r == 0) {
+                _cleanup_close_ int private_tmpfs = -EBADF;
+
+                assert_se(setns(userns_fd, CLONE_NEWUSER) >= 0);
+                assert_se(unshare(CLONE_NEWNS) >= 0);
+
+                /* Allocate tmpfs locally */
+                private_tmpfs = make_tmpfs_fsmount();
+
+                /* These two host mounts should be inaccessible */
+                test_fails_reg(host_fd1, "test");
+                test_fails_reg(host_tmpfs, "xxx");
+                test_fails_dir(host_fd1, "test2");
+                test_fails_dir(host_tmpfs, "xxx2");
+
+                /* But this mount created locally should be fine */
+                test_works_reg(private_tmpfs, "yyy");
+                test_works_dir(private_tmpfs, "yyy2");
+
+                /* Let's sync with the parent, so that it allowlists more stuff for us */
+                assert_se(eventfd_write(afd, 1) >= 0);
+                uint64_t x;
+                assert_se(eventfd_read(bfd, &x) >= 0);
+
+                /* And now we should also have access to the host tmpfs */
+                test_works_reg(host_tmpfs, "zzz");
+                test_works_reg(private_tmpfs, "aaa");
+                test_works_dir(host_tmpfs, "zzz2");
+                test_works_dir(private_tmpfs, "aaa2");
+
+                /* But this one should still fail */
+                test_fails_reg(host_fd1, "bbb");
+                test_fails_dir(host_fd1, "bbb2");
+
+                /* Sync again, to get more stuff allowlisted */
+                assert_se(eventfd_write(afd, 1) >= 0);
+                assert_se(eventfd_read(bfd, &x) >= 0);
+
+                /* Everything should now be allowed */
+                test_works_reg(host_tmpfs, "ccc");
+                test_works_reg(host_fd1, "ddd");
+                test_works_reg(private_tmpfs, "eee");
+                test_works_dir(host_tmpfs, "ccc2");
+                test_works_reg(host_fd1, "ddd2");
+                test_works_dir(private_tmpfs, "eee2");
+
+                _exit(EXIT_SUCCESS);
+        }
+
+        uint64_t x;
+        assert_se(eventfd_read(afd, &x) >= 0);
+
+        r = userns_restrict_put_by_fd(
+                        obj,
+                        userns_fd,
+                        /* replace= */ false,
+                        &host_tmpfs,
+                        1);
+        if (r < 0)
+                return log_error_errno(r, "Failed to loosen user namespace: %m");
+
+        assert_se(eventfd_write(bfd, 1) >= 0);
+
+        assert_se(eventfd_read(afd, &x) >= 0);
+
+        r = userns_restrict_put_by_fd(
+                        obj,
+                        userns_fd,
+                        /* replace= */ false,
+                        &host_fd1,
+                        1);
+        if (r < 0)
+                return log_error_errno(r, "Failed to loosen user namespace: %m");
+
+        assert_se(eventfd_write(bfd, 1) >= 0);
+
+        assert_se(wait_for_terminate_and_check("(test)", pid, WAIT_LOG) >= 0);
+
+        return 0;
+}
+
+DEFINE_MAIN_FUNCTION(run);
diff --git a/src/nsresourced/userns-registry.c b/src/nsresourced/userns-registry.c
new file mode 100644 (file)
index 0000000..2cc1b1f
--- /dev/null
@@ -0,0 +1,646 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "chase.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "format-util.h"
+#include "fs-util.h"
+#include "json.h"
+#include "missing_magic.h"
+#include "path-util.h"
+#include "recurse-dir.h"
+#include "rm-rf.h"
+#include "user-util.h"
+#include "userns-registry.h"
+
+int userns_registry_open_fd(void) {
+        int fd;
+
+        fd = chase_and_open(
+                        "/run/systemd/nsresource/registry",
+                        /* root= */ NULL,
+                        CHASE_MKDIR_0755,
+                        O_CLOEXEC|O_DIRECTORY|O_CREAT,
+                        /* ret_path= */ NULL);
+        if (fd < 0)
+                return log_debug_errno(fd, "Failed to open registry dir: %m");
+
+        return fd;
+}
+
+int userns_registry_lock(int dir_fd) {
+        _cleanup_close_ int registry_fd = -EBADF, lock_fd = -EBADF;
+
+        if (dir_fd < 0) {
+                registry_fd = userns_registry_open_fd();
+                if (registry_fd < 0)
+                        return registry_fd;
+
+                dir_fd = registry_fd;
+        }
+
+        lock_fd = xopenat_lock_full(dir_fd, "lock", O_CREAT|O_RDWR|O_CLOEXEC, /* xopen_flags= */ 0, 0600, LOCK_BSD, LOCK_EX);
+        if (lock_fd < 0)
+                return log_debug_errno(lock_fd, "Failed to open nsresource registry lock file: %m");
+
+        return TAKE_FD(lock_fd);
+}
+
+UserNamespaceInfo* userns_info_new(void) {
+        UserNamespaceInfo *info = new(UserNamespaceInfo, 1);
+        if (!info)
+                return NULL;
+
+        *info = (UserNamespaceInfo) {
+                .owner = UID_INVALID,
+                .start = UID_INVALID,
+                .target = UID_INVALID,
+        };
+
+        return info;
+}
+
+UserNamespaceInfo *userns_info_free(UserNamespaceInfo *userns) {
+        if (!userns)
+                return NULL;
+
+        free(userns->cgroups);
+        free(userns->name);
+
+        return mfree(userns);
+}
+
+static int dispatch_cgroups_array(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
+        UserNamespaceInfo *info = ASSERT_PTR(userdata);
+        _cleanup_free_ uint64_t *cgroups = NULL;
+        size_t n_cgroups = 0;
+
+        if (json_variant_is_null(variant)) {
+                info->cgroups = mfree(info->cgroups);
+                info->n_cgroups = 0;
+                return 0;
+        }
+
+        if (!json_variant_is_array(variant))
+                return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an array.", strna(name));
+
+        cgroups = new(uint64_t, json_variant_elements(variant));
+        if (!cgroups)
+                return json_log_oom(variant, flags);
+
+        JsonVariant *e;
+        JSON_VARIANT_ARRAY_FOREACH(e, variant) {
+                bool found = false;
+
+                if (!json_variant_is_unsigned(e))
+                        return json_log(e, flags, SYNTHETIC_ERRNO(EINVAL), "JSON array element is not a number.");
+
+                FOREACH_ARRAY(cg, cgroups, n_cgroups)
+                        if (*cg == json_variant_unsigned(e)) {
+                                found = true;
+                                break;
+                        }
+                if (found) /* suppress duplicate */
+                        continue;
+
+                cgroups[n_cgroups++] = json_variant_unsigned(e);
+        }
+
+        assert(n_cgroups <= json_variant_elements(variant));
+
+        free_and_replace(info->cgroups, cgroups);
+        info->n_cgroups = n_cgroups;
+
+        return 0;
+}
+
+static int userns_registry_load(int dir_fd, const char *fn, UserNamespaceInfo **ret) {
+
+        static const JsonDispatch dispatch_table[] = {
+                { "owner",   JSON_VARIANT_UNSIGNED, json_dispatch_uid_gid,  offsetof(UserNamespaceInfo, owner),        JSON_MANDATORY },
+                { "name",    JSON_VARIANT_STRING,   json_dispatch_string,   offsetof(UserNamespaceInfo, name),         JSON_MANDATORY },
+                { "userns",  JSON_VARIANT_UNSIGNED, json_dispatch_uint64,   offsetof(UserNamespaceInfo, userns_inode), JSON_MANDATORY },
+                { "start",   JSON_VARIANT_UNSIGNED, json_dispatch_uid_gid,  offsetof(UserNamespaceInfo, start),        0              },
+                { "size",    JSON_VARIANT_UNSIGNED, json_dispatch_uint32,   offsetof(UserNamespaceInfo, size),         0              },
+                { "target",  JSON_VARIANT_UNSIGNED, json_dispatch_uid_gid,  offsetof(UserNamespaceInfo, target),       0              },
+                { "cgroups", JSON_VARIANT_ARRAY,    dispatch_cgroups_array, 0,                                         0              },
+                {}
+        };
+
+        _cleanup_(userns_info_freep) UserNamespaceInfo *userns_info = NULL;
+        _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
+        _cleanup_close_ int registry_fd = -EBADF;
+        int r;
+
+        if (dir_fd < 0) {
+                registry_fd = userns_registry_open_fd();
+                if (registry_fd < 0)
+                        return registry_fd;
+
+                dir_fd = registry_fd;
+        }
+
+        r = json_parse_file_at(NULL, dir_fd, fn, 0, &v, NULL, NULL);
+        if (r < 0)
+                return r;
+
+        userns_info = userns_info_new();
+        if (!userns_info)
+                return -ENOMEM;
+
+        r = json_dispatch(v, dispatch_table, 0, userns_info);
+        if (r < 0)
+                return r;
+
+        if (userns_info->userns_inode == 0)
+                return -EBADMSG;
+        if (userns_info->start == 0)
+                return -EBADMSG;
+        if (userns_info->size == 0) {
+                if (uid_is_valid(userns_info->start) || uid_is_valid(userns_info->target))
+                        return -EBADMSG;
+        } else {
+                if (!uid_is_valid(userns_info->start) || !uid_is_valid(userns_info->target))
+                        return -EBADMSG;
+
+                if (userns_info->size > UINT32_MAX - userns_info->start ||
+                    userns_info->size > UINT32_MAX - userns_info->target)
+                        return -EBADMSG;
+        }
+
+        if (ret)
+                *ret = TAKE_PTR(userns_info);
+        return 0;
+}
+
+int userns_registry_uid_exists(int dir_fd, uid_t start) {
+        _cleanup_free_ char *fn = NULL;
+
+        assert(dir_fd >= 0);
+
+        if (!uid_is_valid(start))
+                return -ENOENT;
+
+        if (start == 0)
+                return true;
+
+        if (asprintf(&fn, "u" UID_FMT ".userns", start) < 0)
+                return -ENOMEM;
+
+        if (faccessat(dir_fd, fn, F_OK, AT_SYMLINK_NOFOLLOW) < 0)
+                return errno == ENOENT ? false : -errno;
+
+        return true;
+}
+
+int userns_registry_name_exists(int dir_fd, const char *name) {
+        _cleanup_free_ char *fn = NULL;
+
+        assert(dir_fd >= 0);
+
+        if (!userns_name_is_valid(name))
+                return -EINVAL;
+
+        fn = strjoin("n", name, ".userns");
+        if (!fn)
+                return -ENOMEM;
+
+        if (faccessat(dir_fd, fn, F_OK, AT_SYMLINK_NOFOLLOW) < 0)
+                return errno == ENOENT ? false : -errno;
+
+        return true;
+}
+
+int userns_registry_inode_exists(int dir_fd, uint64_t inode) {
+        _cleanup_free_ char *fn = NULL;
+
+        assert(dir_fd >= 0);
+
+        if (inode <= 0)
+                return -EINVAL;
+
+        if (asprintf(&fn, "i%" PRIu64 ".userns", inode) < 0)
+                return -ENOMEM;
+
+        if (faccessat(dir_fd, fn, F_OK, AT_SYMLINK_NOFOLLOW) < 0)
+                return errno == ENOENT ? false : -errno;
+
+        return true;
+}
+
+int userns_registry_load_by_start_uid(int dir_fd, uid_t start, UserNamespaceInfo **ret) {
+        _cleanup_(userns_info_freep) UserNamespaceInfo *userns_info = NULL;
+        _cleanup_close_ int registry_fd = -EBADF;
+        _cleanup_free_ char *fn = NULL;
+        int r;
+
+        if (!uid_is_valid(start))
+                return -ENOENT;
+
+        if (dir_fd < 0) {
+                registry_fd = userns_registry_open_fd();
+                if (registry_fd < 0)
+                        return registry_fd;
+
+                dir_fd = registry_fd;
+        }
+
+        if (asprintf(&fn, "u" UID_FMT ".userns", start) < 0)
+                return -ENOMEM;
+
+        r = userns_registry_load(dir_fd, fn, &userns_info);
+        if (r < 0)
+                return r;
+
+        if (userns_info->start != start)
+                return -EBADMSG;
+
+        if (ret)
+                *ret = TAKE_PTR(userns_info);
+
+        return 0;
+}
+
+int userns_registry_load_by_userns_inode(int dir_fd, uint64_t inode, UserNamespaceInfo **ret) {
+        _cleanup_(userns_info_freep) UserNamespaceInfo *userns_info = NULL;
+        _cleanup_close_ int registry_fd = -EBADF;
+        _cleanup_free_ char *fn = NULL;
+        int r;
+
+        if (inode == 0)
+                return -ENOENT;
+
+        if (dir_fd < 0) {
+                registry_fd = userns_registry_open_fd();
+                if (registry_fd < 0)
+                        return registry_fd;
+
+                dir_fd = registry_fd;
+        }
+
+        if (asprintf(&fn, "i%" PRIu64 ".userns", inode) < 0)
+                return -ENOMEM;
+
+        r = userns_registry_load(dir_fd, fn, &userns_info);
+        if (r < 0)
+                return r;
+
+        if (userns_info->userns_inode != inode)
+                return -EBADMSG;
+
+        if (ret)
+                *ret = TAKE_PTR(userns_info);
+
+        return 0;
+}
+
+int userns_registry_load_by_name(int dir_fd, const char *name, UserNamespaceInfo **ret) {
+        _cleanup_(userns_info_freep) UserNamespaceInfo *userns_info = NULL;
+        _cleanup_close_ int registry_fd = -EBADF;
+        _cleanup_free_ char *fn = NULL;
+        int r;
+
+        assert(name);
+
+        if (!userns_name_is_valid(name)) /* Invalid names never exist */
+                return -ENOENT;
+
+        if (dir_fd < 0) {
+                registry_fd = userns_registry_open_fd();
+                if (registry_fd < 0)
+                        return registry_fd;
+
+                dir_fd = registry_fd;
+        }
+
+        fn = strjoin("n", name, ".userns");
+        if (!fn)
+                return -ENOMEM;
+
+        r = userns_registry_load(dir_fd, fn, &userns_info);
+        if (r < 0)
+                return r;
+
+        if (!streq_ptr(userns_info->name, name))
+                return -EBADMSG;
+
+        if (ret)
+                *ret = TAKE_PTR(userns_info);
+
+        return 0;
+}
+
+int userns_registry_store(int dir_fd, UserNamespaceInfo *info) {
+        _cleanup_close_ int registry_fd = -EBADF;
+        int r;
+
+        assert(info);
+
+        if (!uid_is_valid(info->owner) ||
+            !info->name ||
+            info->userns_inode == 0)
+                return -EINVAL;
+
+        if (dir_fd < 0) {
+                registry_fd = userns_registry_open_fd();
+                if (registry_fd < 0)
+                        return registry_fd;
+
+                dir_fd = registry_fd;
+        }
+
+        _cleanup_(json_variant_unrefp) JsonVariant *cgroup_array = NULL;
+        FOREACH_ARRAY(cg, info->cgroups, info->n_cgroups) {
+                r = json_variant_append_arrayb(
+                                &cgroup_array,
+                                JSON_BUILD_UNSIGNED(*cg));
+                if (r < 0)
+                        return r;
+        }
+
+        _cleanup_(json_variant_unrefp) JsonVariant *def = NULL;
+        r = json_build(&def, JSON_BUILD_OBJECT(
+                                       JSON_BUILD_PAIR("owner", JSON_BUILD_UNSIGNED(info->owner)),
+                                       JSON_BUILD_PAIR("name", JSON_BUILD_STRING(info->name)),
+                                       JSON_BUILD_PAIR("userns", JSON_BUILD_UNSIGNED(info->userns_inode)),
+                                       JSON_BUILD_PAIR_CONDITION(uid_is_valid(info->start), "start", JSON_BUILD_UNSIGNED(info->start)),
+                                       JSON_BUILD_PAIR_CONDITION(uid_is_valid(info->start), "size", JSON_BUILD_UNSIGNED(info->size)),
+                                       JSON_BUILD_PAIR_CONDITION(uid_is_valid(info->start), "target", JSON_BUILD_UNSIGNED(info->target)),
+                                       JSON_BUILD_PAIR_CONDITION(cgroup_array, "cgroups", JSON_BUILD_VARIANT(cgroup_array))));
+        if (r < 0)
+                return r;
+
+        _cleanup_free_ char *def_buf = NULL;
+        r = json_variant_format(def, 0, &def_buf);
+        if (r < 0)
+                return log_debug_errno(r, "Failed to format userns JSON object: %m");
+
+        _cleanup_free_ char *reg_fn = NULL, *link1_fn = NULL, *link2_fn = NULL, *owner_fn = NULL, *uid_fn = NULL;
+        if (asprintf(&reg_fn, "i%" PRIu64 ".userns", info->userns_inode) < 0)
+                return log_oom_debug();
+
+        r = write_string_file_at(dir_fd, reg_fn, def_buf, WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_ATOMIC);
+        if (r < 0)
+                return log_debug_errno(r, "Failed to write userns data to '%s' in registry: %m", reg_fn);
+
+        link1_fn = strjoin("n", info->name, ".userns");
+        if (!link1_fn) {
+                r = log_oom_debug();
+                goto fail;
+        }
+
+        r = linkat_replace(dir_fd, reg_fn, dir_fd, link1_fn);
+        if (r < 0) {
+                log_debug_errno(r, "Failed to link userns data to '%s' in registry: %m", link1_fn);
+                goto fail;
+        }
+
+        if (uid_is_valid(info->start)) {
+                if (asprintf(&link2_fn, "u" UID_FMT ".userns", info->start) < 0) {
+                        r = log_oom_debug();
+                        goto fail;
+                }
+
+                r = linkat_replace(dir_fd, reg_fn, dir_fd, link2_fn);
+                if (r < 0) {
+                        log_debug_errno(r, "Failed to link userns data to '%s' in registry: %m", link2_fn);
+                        goto fail;
+                }
+        }
+
+        if (asprintf(&uid_fn, "o" UID_FMT ".owns", info->owner) < 0) {
+                r = log_oom_debug();
+                goto fail;
+        }
+
+        if (mkdirat(dir_fd, uid_fn, 0755) < 0 && errno != EEXIST) {
+                r = log_debug_errno(errno, "Failed to create per-UID subdir '%s' of registry: %m", uid_fn);
+                goto fail;
+        }
+
+        if (asprintf(&owner_fn, "%s/i%" PRIu64 ".userns", uid_fn, info->userns_inode) < 0) {
+                r = log_oom_debug();
+                goto fail;
+        }
+
+        r = linkat_replace(dir_fd, reg_fn, dir_fd, owner_fn);
+        if (r < 0) {
+                log_debug_errno(r, "Failed to link userns data to '%s' in registry: %m", owner_fn);
+                goto fail;
+        }
+
+        return 0;
+
+fail:
+        if (reg_fn)
+                (void) unlinkat(dir_fd, reg_fn, /* flags= */ 0);
+        if (link1_fn)
+                (void) unlinkat(dir_fd, link1_fn, /* flags= */ 0);
+        if (link2_fn)
+                (void) unlinkat(dir_fd, link2_fn, /* flags= */ 0);
+        if (owner_fn)
+                (void) unlinkat(dir_fd, owner_fn, /* flags= */ 0);
+        if (uid_fn)
+                (void) unlinkat(dir_fd, uid_fn, AT_REMOVEDIR);
+
+        return r;
+}
+
+int userns_registry_remove(int dir_fd, UserNamespaceInfo *info) {
+        _cleanup_close_ int registry_fd = -EBADF;
+        int ret = 0, r;
+
+        assert(info);
+
+        if (dir_fd < 0) {
+                registry_fd = userns_registry_open_fd();
+                if (registry_fd < 0)
+                        return registry_fd;
+
+                dir_fd = registry_fd;
+        }
+
+        _cleanup_free_ char *reg_fn = NULL;
+        if (asprintf(&reg_fn, "i%" PRIu64 ".userns", info->userns_inode) < 0)
+                return log_oom_debug();
+
+        ret = RET_NERRNO(unlinkat(dir_fd, reg_fn, 0));
+
+        _cleanup_free_ char *link1_fn = NULL;
+        link1_fn = strjoin("n", info->name, ".userns");
+        if (!link1_fn)
+                return log_oom_debug();
+
+        RET_GATHER(ret, RET_NERRNO(unlinkat(dir_fd, link1_fn, 0)));
+
+        if (uid_is_valid(info->start)) {
+                _cleanup_free_ char *link2_fn = NULL;
+
+                if (asprintf(&link2_fn, "u" UID_FMT ".userns", info->start) < 0)
+                        return log_oom_debug();
+
+                RET_GATHER(ret, RET_NERRNO(unlinkat(dir_fd, link2_fn, 0)));
+        }
+
+        _cleanup_free_ char *uid_fn = NULL;
+        if (asprintf(&uid_fn, "o" UID_FMT ".owns", info->owner) < 0)
+                return log_oom_debug();
+
+        _cleanup_free_ char *owner_fn = NULL;
+        if (asprintf(&owner_fn, "%s/i%" PRIu64 ".userns", uid_fn, info->userns_inode) < 0)
+                return log_oom_debug();
+
+        RET_GATHER(ret, RET_NERRNO(unlinkat(dir_fd, owner_fn, 0)));
+
+        r = RET_NERRNO(unlinkat(dir_fd, uid_fn, AT_REMOVEDIR));
+        if (r != -ENOTEMPTY)
+                RET_GATHER(ret, r);
+
+        return ret;
+}
+
+bool userns_info_has_cgroup(UserNamespaceInfo *userns, uint64_t cgroup_id) {
+        assert(userns);
+
+        FOREACH_ARRAY(i, userns->cgroups, userns->n_cgroups)
+                if (*i == cgroup_id)
+                        return true;
+
+        return false;
+}
+
+int userns_info_add_cgroup(UserNamespaceInfo *userns, uint64_t cgroup_id) {
+
+        if (userns_info_has_cgroup(userns, cgroup_id))
+                return 0;
+
+        if (!GREEDY_REALLOC(userns->cgroups, userns->n_cgroups+1))
+                return -ENOMEM;
+
+        userns->cgroups[userns->n_cgroups++] = cgroup_id;
+        return 1;
+}
+
+static int userns_destroy_cgroup(uint64_t cgroup_id) {
+        _cleanup_close_ int cgroup_fd = -EBADF, parent_fd = -EBADF;
+        int r;
+
+        cgroup_fd = cg_cgroupid_open(/* cgroupfsfd= */ -EBADF, cgroup_id);
+        if (cgroup_fd == -ESTALE) {
+                log_debug_errno(cgroup_fd, "Control group %" PRIu64 " already gone, ignoring: %m", cgroup_id);
+                return 0;
+        }
+        if (cgroup_fd < 0)
+                return log_debug_errno(errno, "Failed to open cgroup %" PRIu64 ", ignoring: %m", cgroup_id);
+
+        _cleanup_free_ char *path = NULL;
+        r = fd_get_path(cgroup_fd, &path);
+        if (r < 0)
+                return log_debug_errno(r, "Failed to get path of cgroup %" PRIu64 ", ignoring: %m", cgroup_id);
+
+        const char *e = path_startswith(path, "/sys/fs/cgroup/");
+        if (!e)
+                return log_debug_errno(SYNTHETIC_ERRNO(EPERM), "Got cgroup path that doesn't start with /sys/fs/cgroup/, refusing: %s", path);
+        if (isempty(e))
+                return log_debug_errno(SYNTHETIC_ERRNO(EPERM), "Got root cgroup path, which can't be right, refusing.");
+
+        log_debug("Path of cgroup %" PRIu64 " is: %s", cgroup_id, path);
+
+        _cleanup_free_ char *fname = NULL;
+        r = path_extract_filename(path, &fname);
+        if (r < 0)
+                return log_debug_errno(r, "Failed to extract name of cgroup %" PRIu64 ", ignoring: %m", cgroup_id);
+
+        parent_fd = openat(cgroup_fd, "..", O_CLOEXEC|O_DIRECTORY);
+        if (parent_fd < 0)
+                return log_debug_errno(errno, "Failed to open parent cgroup of %" PRIu64 ", ignoring: %m", cgroup_id);
+
+        /* Safety check, never leave cgroupfs */
+        r = fd_is_fs_type(parent_fd, CGROUP2_SUPER_MAGIC);
+        if (r < 0)
+                return log_debug_errno(r, "Failed to determine if parent directory of cgroup %" PRIu64 " is still a cgroup, ignoring: %m", cgroup_id);
+        if (!r)
+                return log_debug_errno(SYNTHETIC_ERRNO(EPERM), "Parent directory of cgroup %" PRIu64 " is not a cgroup, refusing.", cgroup_id);
+
+        cgroup_fd = safe_close(cgroup_fd);
+
+        r = rm_rf_child(parent_fd, fname, REMOVE_ONLY_DIRECTORIES|REMOVE_PHYSICAL|REMOVE_CHMOD);
+        if (r < 0)
+                log_debug_errno(r, "Failed to remove delegated cgroup %" PRIu64 ", ignoring: %m", cgroup_id);
+
+        return 0;
+}
+
+int userns_info_remove_cgroups(UserNamespaceInfo *userns) {
+        int ret = 0;
+
+        assert(userns);
+
+        FOREACH_ARRAY(c, userns->cgroups, userns->n_cgroups)
+                RET_GATHER(ret, userns_destroy_cgroup(*c));
+
+        userns->cgroups = mfree(userns->cgroups);
+        userns->n_cgroups = 0;
+
+        return ret;
+}
+
+bool userns_name_is_valid(const char *name) {
+
+        /* Checks if the specified string is suitable as user namespace name. */
+
+        if (strlen(name) > NAME_MAX) /* before we use alloca(), let's check for size */
+                return false;
+
+        const char *f = strjoina("n", name, ".userns"); /* Make sure we can name our lookup symlink with this name */
+        if (!filename_is_valid(f))
+                return false;
+
+        const char *u = strjoina("ns-", name, "-65535"); /* Make sure we can turn this into valid user names */
+        if (!valid_user_group_name(u, 0))
+                return false;
+
+        return true;
+}
+
+int userns_registry_per_uid(int dir_fd, uid_t owner) {
+        _cleanup_close_ int registry_fd = -EBADF;
+        int n = 0, r;
+
+        if (dir_fd < 0) {
+                registry_fd = userns_registry_open_fd();
+                if (registry_fd < 0)
+                        return registry_fd;
+
+                dir_fd = registry_fd;
+        }
+
+        _cleanup_free_ char *uid_fn = NULL;
+        if (asprintf(&uid_fn, "o" UID_FMT ".owns", owner) < 0)
+                return log_oom_debug();
+
+        _cleanup_free_ DirectoryEntries *de = NULL;
+
+        r = readdir_all_at(dir_fd, uid_fn, RECURSE_DIR_IGNORE_DOT|RECURSE_DIR_ENSURE_TYPE, &de);
+        if (r == -ENOENT)
+                return 0;
+        if (r < 0)
+                return log_debug_errno(r, "Failed to enumerate contents of '%s' sub-directory: %m", uid_fn);
+
+        FOREACH_ARRAY(i, de->entries, de->n_entries) {
+                struct dirent *e = *i;
+
+                if (e->d_type != DT_REG)
+                        continue;
+
+                if (!startswith(e->d_name, "i") || !endswith(e->d_name, ".userns"))
+                        continue;
+
+                n++;
+
+                if (n == INT_MAX) /* overflow safety check, just in case */
+                        break;
+        }
+
+        return n;
+}
diff --git a/src/nsresourced/userns-registry.h b/src/nsresourced/userns-registry.h
new file mode 100644 (file)
index 0000000..9e66a6f
--- /dev/null
@@ -0,0 +1,42 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#define USER_NAMESPACE_CGROUPS_DELEGATE_MAX 16
+
+typedef struct UserNamespaceInfo {
+        uid_t owner;
+        char *name;
+        uint64_t userns_inode;
+        uid_t start;
+        uint32_t size;
+        uid_t target;
+        uint64_t *cgroups;
+        size_t n_cgroups;
+} UserNamespaceInfo;
+
+UserNamespaceInfo* userns_info_new(void);
+UserNamespaceInfo* userns_info_free(UserNamespaceInfo *userns);
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(UserNamespaceInfo*, userns_info_free);
+
+bool userns_info_has_cgroup(UserNamespaceInfo *userns, uint64_t cgroup_id);
+int userns_info_add_cgroup(UserNamespaceInfo *userns, uint64_t cgroup_id);
+int userns_info_remove_cgroups(UserNamespaceInfo *userns);
+
+bool userns_name_is_valid(const char *name);
+
+int userns_registry_open_fd(void);
+int userns_registry_lock(int dir_fd);
+
+int userns_registry_load_by_start_uid(int dir_fd, uid_t start, UserNamespaceInfo **ret);
+int userns_registry_load_by_userns_inode(int dir_fd, uint64_t userns, UserNamespaceInfo **ret);
+int userns_registry_load_by_name(int dir_fd, const char *name, UserNamespaceInfo **ret);
+
+int userns_registry_store(int dir_fd, UserNamespaceInfo *info);
+int userns_registry_remove(int dir_fd, UserNamespaceInfo *info);
+
+int userns_registry_inode_exists(int dir_fd, uint64_t inode);
+int userns_registry_name_exists(int dir_fd, const char *name);
+int userns_registry_uid_exists(int dir_fd, uid_t start);
+
+int userns_registry_per_uid(int dir_fd, uid_t owner);
diff --git a/src/nsresourced/userns-restrict.c b/src/nsresourced/userns-restrict.c
new file mode 100644 (file)
index 0000000..4e917fd
--- /dev/null
@@ -0,0 +1,346 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "userns-restrict.h"
+
+#if HAVE_VMLINUX_H
+
+#include <sched.h>
+
+#include "bpf-dlopen.h"
+#include "bpf-link.h"
+#include "fd-util.h"
+#include "fs-util.h"
+#include "lsm-util.h"
+#include "missing_mount.h"
+#include "mkdir.h"
+#include "mount-util.h"
+#include "mountpoint-util.h"
+#include "namespace-util.h"
+#include "path-util.h"
+
+#define USERNS_MAX (16U*1024U)
+#define MOUNTS_MAX 4096U
+
+#define PROGRAM_LINK_PREFIX "/sys/fs/bpf/systemd/userns-restrict/programs"
+#define MAP_LINK_PREFIX "/sys/fs/bpf/systemd/userns-restrict/maps"
+
+struct userns_restrict_bpf *userns_restrict_bpf_free(struct userns_restrict_bpf *obj) {
+        (void) userns_restrict_bpf__destroy(obj); /* this call is fine with NULL */
+        return NULL;
+}
+
+static int make_inner_hash_map(void) {
+        int fd;
+
+        fd = compat_bpf_map_create(
+                        BPF_MAP_TYPE_HASH,
+                        NULL,
+                        sizeof(int),
+                        sizeof(uint32_t),
+                        MOUNTS_MAX,
+                        NULL);
+        if (fd < 0)
+                return log_debug_errno(errno, "Failed allocate inner BPF map: %m");
+
+        return fd;
+}
+
+int userns_restrict_install(
+                bool pin,
+                struct userns_restrict_bpf **ret) {
+
+        _cleanup_(userns_restrict_bpf_freep) struct userns_restrict_bpf *obj = NULL;
+        _cleanup_close_ int dummy_mnt_id_hash_fd = -EBADF;
+        int r;
+
+        r = lsm_supported("bpf");
+        if (r < 0)
+                return r;
+        if (r == 0)
+                return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "bpf-lsm not supported, can't lock down user namespace.");
+
+        r = dlopen_bpf();
+        if (r < 0)
+                return r;
+
+        /* bpf_object__next_map() is not available in libbpf pre-0.7.0, and we want to use it. */
+        if (!sym_bpf_object__next_map)
+                return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "libbpf too old for locking down user namespace.");
+
+        obj = userns_restrict_bpf__open();
+        if (!obj)
+                return log_error_errno(errno, "Failed to open userns_restrict BPF object: %m");
+
+        if (pin) {
+                struct bpf_map *map;
+
+                /* libbpf will only create one level of dirs. Let's create the rest */
+                (void) mkdir_p(MAP_LINK_PREFIX, 0755);
+                (void) mkdir_p(PROGRAM_LINK_PREFIX, 0755);
+
+                map = sym_bpf_object__next_map(obj->obj, NULL);
+                while (map) {
+                        _cleanup_free_ char *fn = NULL;
+
+                        fn = path_join(MAP_LINK_PREFIX, sym_bpf_map__name(map));
+                        if (!fn)
+                                return log_oom();
+
+                        r = sym_bpf_map__set_pin_path(map, fn);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to set pin path to '%s': %m", fn);
+
+                        map = sym_bpf_object__next_map(obj->obj, map);
+                }
+        }
+
+        r = sym_bpf_map__set_max_entries(obj->maps.userns_mnt_id_hash, USERNS_MAX);
+        if (r < 0)
+                return log_error_errno(r, "Failed to size userns/mnt_id hash table: %m");
+
+        r = sym_bpf_map__set_max_entries(obj->maps.userns_ringbuf, USERNS_MAX * sizeof(unsigned int));
+        if (r < 0)
+                return log_error_errno(r, "Failed to size userns ring buffer: %m");
+
+        /* Dummy map to satisfy the verifier */
+        dummy_mnt_id_hash_fd = make_inner_hash_map();
+        if (dummy_mnt_id_hash_fd < 0)
+                return dummy_mnt_id_hash_fd;
+
+        r = sym_bpf_map__set_inner_map_fd(obj->maps.userns_mnt_id_hash, dummy_mnt_id_hash_fd);
+        if (r < 0)
+                return log_error_errno(r, "Failed to set inner BPF map: %m");
+
+        r = userns_restrict_bpf__load(obj);
+        if (r < 0)
+                return log_error_errno(r, "Failed to load BPF object: %m");
+
+        for (int i = 0; i < obj->skeleton->prog_cnt; i++) {
+                _cleanup_(bpf_link_freep) struct bpf_link *link = NULL;
+                struct bpf_prog_skeleton *ps = obj->skeleton->progs + i;
+                _cleanup_free_ char *fn = NULL;
+                bool linked = false;
+                const char *e;
+
+                e = startswith(ps->name, "userns_restrict_");
+                assert(e);
+
+                if (pin) {
+                        fn = path_join(PROGRAM_LINK_PREFIX, e);
+                        if (!fn)
+                                return log_oom();
+
+                        link = sym_bpf_link__open(fn);
+                        r = sym_libbpf_get_error(link);
+                        if (r < 0) {
+                                if (r != -ENOENT)
+                                        return log_error_errno(r, "Unable to open pinned program link: %m");
+                                link = NULL;
+                        } else {
+                                linked = true;
+                                log_info("userns-restrict BPF-LSM program %s already attached.", ps->name);
+                        }
+                }
+
+                if (!link) {
+                        link = sym_bpf_program__attach(*ps->prog);
+                        r = sym_libbpf_get_error(link);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to attach LSM BPF program: %m");
+
+                        log_info("userns-restrict BPF-LSM program %s now attached.", ps->name);
+                }
+
+                if (pin && !linked) {
+                        assert(fn);
+
+                        r = sym_bpf_link__pin(link, fn);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to pin LSM attachment: %m");
+                }
+
+                *ps->link = TAKE_PTR(link);
+        }
+
+        if (pin) {
+                r = sym_bpf_object__pin_maps(obj->obj, NULL);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to pin BPF maps: %m");
+        }
+
+        if (ret)
+                *ret = TAKE_PTR(obj);
+
+        return 0;
+}
+
+int userns_restrict_put_by_inode(
+                struct userns_restrict_bpf *obj,
+                uint64_t userns_inode,
+                bool replace,
+                const int mount_fds[],
+                size_t n_mount_fds) {
+
+        _cleanup_close_ int inner_map_fd = -EBADF;
+        _cleanup_free_ int *mnt_ids = NULL;
+        uint64_t ino = userns_inode;
+        int r, outer_map_fd;
+
+        assert(obj);
+        assert(userns_inode != 0);
+        assert(n_mount_fds == 0 || mount_fds);
+
+        /* The BPF map type BPF_MAP_TYPE_HASH_OF_MAPS only supports 32bit keys, and user namespace inode
+         * numbers are 32bit too, even though ino_t is 64bit these days. Should we ever run into a 64bit
+         * inode let's refuse early, we can't support this with the current BPF code for now. */
+        if (userns_inode > UINT32_MAX)
+                return -EINVAL;
+
+        mnt_ids = new(int, n_mount_fds);
+        if (!mnt_ids)
+                return -ENOMEM;
+
+        for (size_t i = 0; i < n_mount_fds; i++) {
+                r = path_get_mnt_id_at(mount_fds[i], "", mnt_ids + i);
+                if (r < 0)
+                        return log_debug_errno(r, "Failed to get mount ID: %m");
+        }
+
+        outer_map_fd = sym_bpf_map__fd(obj->maps.userns_mnt_id_hash);
+        if (outer_map_fd < 0)
+                return log_debug_errno(outer_map_fd, "Failed to get outer BPF map fd: %m");
+
+        if (replace) {
+                /* Add if missing, replace if already exists */
+                inner_map_fd = make_inner_hash_map();
+                if (inner_map_fd < 0)
+                        return inner_map_fd;
+
+                r = sym_bpf_map_update_elem(outer_map_fd, &ino, &inner_map_fd, BPF_ANY);
+                if (r < 0)
+                        return log_debug_errno(errno, "Failed to replace map in inode hash: %m");
+        } else {
+                /* Let's add an entry for this userns inode if missing. If it exists just extend the existing map. We
+                 * might race against each other, hence we try a couple of times */
+                for (size_t n_try = 10;; n_try--) {
+                        uint32_t innermap_id;
+
+                        if (n_try == 0)
+                                return log_debug_errno(SYNTHETIC_ERRNO(EEXIST),
+                                                       "Stillcan't create inode entry in BPF map after 10 tries.");
+
+                        r = sym_bpf_map_lookup_elem(outer_map_fd, &ino, &innermap_id);
+                        if (r >= 0) {
+                                inner_map_fd = sym_bpf_map_get_fd_by_id(innermap_id);
+                                if (inner_map_fd < 0)
+                                        return log_debug_errno(inner_map_fd, "Failed to get file descriptor for inner map: %m");
+
+                                break;
+                        }
+                        if (errno != ENOENT)
+                                return log_debug_errno(errno, "Failed to look up inode hash entry: %m");
+
+                        /* No entry for this user namespace yet. Let's create one */
+                        inner_map_fd = make_inner_hash_map();
+                        if (inner_map_fd < 0)
+                                return inner_map_fd;
+
+                        r = sym_bpf_map_update_elem(outer_map_fd, &ino, &inner_map_fd, BPF_NOEXIST);
+                        if (r >= 0)
+                                break;
+                        if (errno != EEXIST)
+                                return log_debug_errno(errno, "Failed to add mount ID list to inode hash: %m");
+                }
+        }
+
+        FOREACH_ARRAY(mntid, mnt_ids, n_mount_fds) {
+                uint32_t dummy_value = 1;
+
+                r = sym_bpf_map_update_elem(inner_map_fd, mntid, &dummy_value, BPF_ANY);
+                if (r < 0)
+                        return log_debug_errno(errno, "Failed to add mount ID to map: %m");
+
+                log_debug("Allowing mount %i on userns inode %" PRIu64, *mntid, ino);
+        }
+
+        return 0;
+}
+
+int userns_restrict_put_by_fd(
+                struct userns_restrict_bpf *obj,
+                int userns_fd,
+                bool replace,
+                const int mount_fds[],
+                size_t n_mount_fds) {
+
+        struct stat st;
+        int r;
+
+        assert(obj);
+        assert(userns_fd >= 0);
+        assert(n_mount_fds == 0 || mount_fds);
+
+        r = fd_is_ns(userns_fd, CLONE_NEWUSER);
+        if (r < 0)
+                return log_debug_errno(r, "Failed to determine if file descriptor is user namespace: %m");
+        if (r == 0)
+                return log_debug_errno(SYNTHETIC_ERRNO(EBADF), "User namespace fd is not actually a user namespace fd.");
+
+        if (fstat(userns_fd, &st) < 0)
+                return log_debug_errno(errno, "Failed to fstat() user namespace: %m");
+
+        return userns_restrict_put_by_inode(
+                        obj,
+                        st.st_ino,
+                        replace,
+                        mount_fds,
+                        n_mount_fds);
+}
+
+int userns_restrict_reset_by_inode(
+                struct userns_restrict_bpf *obj,
+                uint64_t ino) {
+
+        int r, outer_map_fd;
+        unsigned u;
+
+        assert(obj);
+        assert(ino != 0);
+
+        if (ino > UINT32_MAX) /* inodes larger than 32bit are definitely not included in our map, exit early */
+                return 0;
+
+        outer_map_fd = sym_bpf_map__fd(obj->maps.userns_mnt_id_hash);
+        if (outer_map_fd < 0)
+                return log_debug_errno(outer_map_fd, "Failed to get outer BPF map fd: %m");
+
+        u = (uint32_t) ino;
+
+        r = sym_bpf_map_delete_elem(outer_map_fd, &u);
+        if (r < 0)
+                return log_debug_errno(outer_map_fd, "Failed to remove entry for inode %" PRIu64 " from outer map: %m", ino);
+
+        return 0;
+}
+
+#else
+int userns_restrict_install(bool pin, struct userns_restrict_bpf **ret) {
+        return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "User Namespace Restriction BPF support disabled.");
+}
+
+struct userns_restrict_bpf *userns_restrict_bpf_free(struct userns_restrict_bpf *obj) {
+        return NULL;
+}
+
+int userns_restrict_put_by_fd(struct userns_restrict_bpf *obj, int userns_fd, bool replace, const int mount_fds[], size_t n_mount_fds) {
+        return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "User Namespace Restriction BPF support disabled.");
+}
+
+int userns_restrict_put_by_inode(struct userns_restrict_bpf *obj, uint64_t userns_inode, bool replace, const int mount_fds[], size_t n_mount_fds) {
+        return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "User Namespace Restriction BPF support disabled.");
+}
+
+int userns_restrict_reset_by_inode(struct userns_restrict_bpf *obj, uint64_t userns_inode) {
+        return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "User Namespace Restriction BPF support disabled.");
+}
+#endif
diff --git a/src/nsresourced/userns-restrict.h b/src/nsresourced/userns-restrict.h
new file mode 100644 (file)
index 0000000..37aed7b
--- /dev/null
@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include <stdbool.h>
+
+#include "macro.h"
+
+#if HAVE_VMLINUX_H
+#include "bpf/userns_restrict/userns-restrict-skel.h"
+#else
+struct userns_restrict_bpf;
+#endif
+
+int userns_restrict_install(bool pin, struct userns_restrict_bpf **ret);
+struct userns_restrict_bpf *userns_restrict_bpf_free(struct userns_restrict_bpf *obj);
+
+int userns_restrict_put_by_fd(struct userns_restrict_bpf *obj, int userns_fd, bool replace, const int mount_fds[], size_t n_mount_fds);
+int userns_restrict_put_by_inode(struct userns_restrict_bpf *obj, uint64_t userns_inode, bool replace, const int mount_fds[], size_t n_mount_fds);
+
+int userns_restrict_reset_by_inode(struct userns_restrict_bpf *obj, uint64_t userns_inode);
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(struct userns_restrict_bpf*, userns_restrict_bpf_free);
index 78cde2ff5281aa3e22fd695081620d3e944fc12c..52e13681166968543aac8b854d08d08ac47bdcc5 100644 (file)
@@ -33,7 +33,6 @@ executables += [
                 'c_args' : '-DSTANDALONE',
                 'link_with' : [
                         libbasic,
-                        libbasic_gcrypt,
                         libshared_fdisk,
                         libshared_static,
                         libsystemd_static,
index ddb7fcb046d7ef0cdf812ba0e993974fbafff365..fa556874dffd3b407c47e18b02b02a9c3718adb9 100644 (file)
@@ -300,6 +300,7 @@ typedef struct Partition {
         char **exclude_files_target;
         char **make_directories;
         char **subvolumes;
+        char *default_subvolume;
         EncryptMode encrypt;
         VerityMode verity;
         char *verity_match_key;
@@ -1668,6 +1669,41 @@ static int config_parse_make_dirs(
         }
 }
 
+static int config_parse_default_subvolume(
+                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 **subvol = ASSERT_PTR(data);
+        _cleanup_free_ char *p = NULL;
+        int r;
+
+        if (isempty(rvalue)) {
+                *subvol = mfree(*subvol);
+                return 0;
+        }
+
+        r = specifier_printf(rvalue, PATH_MAX-1, system_and_tmp_specifier_table, arg_root, NULL, &p);
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "Failed to expand specifiers in DefaultSubvolume= parameter, ignoring: %s", rvalue);
+                return 0;
+        }
+
+        r = path_simplify_and_warn(p, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue);
+        if (r < 0)
+                return 0;
+
+        return free_and_replace(*subvol, p);
+}
+
 static DEFINE_CONFIG_PARSE_ENUM_WITH_DEFAULT(config_parse_encrypt, encrypt_mode, EncryptMode, ENCRYPT_OFF, "Invalid encryption mode");
 
 static int config_parse_gpt_flags(
@@ -1861,37 +1897,38 @@ static DEFINE_CONFIG_PARSE_ENUM_WITH_DEFAULT(config_parse_minimize, minimize_mod
 static int partition_read_definition(Partition *p, const char *path, const char *const *conf_file_dirs) {
 
         ConfigTableItem table[] = {
-                { "Partition", "Type",                     config_parse_type,             0, &p->type                    },
-                { "Partition", "Label",                    config_parse_label,            0, &p->new_label               },
-                { "Partition", "UUID",                     config_parse_uuid,             0, p                           },
-                { "Partition", "Priority",                 config_parse_int32,            0, &p->priority                },
-                { "Partition", "Weight",                   config_parse_weight,           0, &p->weight                  },
-                { "Partition", "PaddingWeight",            config_parse_weight,           0, &p->padding_weight          },
-                { "Partition", "SizeMinBytes",             config_parse_size4096,        -1, &p->size_min                },
-                { "Partition", "SizeMaxBytes",             config_parse_size4096,         1, &p->size_max                },
-                { "Partition", "PaddingMinBytes",          config_parse_size4096,        -1, &p->padding_min             },
-                { "Partition", "PaddingMaxBytes",          config_parse_size4096,         1, &p->padding_max             },
-                { "Partition", "FactoryReset",             config_parse_bool,             0, &p->factory_reset           },
-                { "Partition", "CopyBlocks",               config_parse_copy_blocks,      0, p                           },
-                { "Partition", "Format",                   config_parse_fstype,           0, &p->format                  },
-                { "Partition", "CopyFiles",                config_parse_copy_files,       0, &p->copy_files              },
-                { "Partition", "ExcludeFiles",             config_parse_exclude_files,    0, &p->exclude_files_source    },
-                { "Partition", "ExcludeFilesTarget",       config_parse_exclude_files,    0, &p->exclude_files_target    },
-                { "Partition", "MakeDirectories",          config_parse_make_dirs,        0, &p->make_directories        },
-                { "Partition", "Encrypt",                  config_parse_encrypt,          0, &p->encrypt                 },
-                { "Partition", "Verity",                   config_parse_verity,           0, &p->verity                  },
-                { "Partition", "VerityMatchKey",           config_parse_string,           0, &p->verity_match_key        },
-                { "Partition", "Flags",                    config_parse_gpt_flags,        0, &p->gpt_flags               },
-                { "Partition", "ReadOnly",                 config_parse_tristate,         0, &p->read_only               },
-                { "Partition", "NoAuto",                   config_parse_tristate,         0, &p->no_auto                 },
-                { "Partition", "GrowFileSystem",           config_parse_tristate,         0, &p->growfs                  },
-                { "Partition", "SplitName",                config_parse_string,           0, &p->split_name_format       },
-                { "Partition", "Minimize",                 config_parse_minimize,         0, &p->minimize                },
-                { "Partition", "Subvolumes",               config_parse_make_dirs,        0, &p->subvolumes              },
-                { "Partition", "VerityDataBlockSizeBytes", config_parse_block_size,       0, &p->verity_data_block_size  },
-                { "Partition", "VerityHashBlockSizeBytes", config_parse_block_size,       0, &p->verity_hash_block_size  },
-                { "Partition", "MountPoint",               config_parse_mountpoint,       0, p                           },
-                { "Partition", "EncryptedVolume",          config_parse_encrypted_volume, 0, p                           },
+                { "Partition", "Type",                     config_parse_type,              0, &p->type                    },
+                { "Partition", "Label",                    config_parse_label,             0, &p->new_label               },
+                { "Partition", "UUID",                     config_parse_uuid,              0, p                           },
+                { "Partition", "Priority",                 config_parse_int32,             0, &p->priority                },
+                { "Partition", "Weight",                   config_parse_weight,            0, &p->weight                  },
+                { "Partition", "PaddingWeight",            config_parse_weight,            0, &p->padding_weight          },
+                { "Partition", "SizeMinBytes",             config_parse_size4096,         -1, &p->size_min                },
+                { "Partition", "SizeMaxBytes",             config_parse_size4096,          1, &p->size_max                },
+                { "Partition", "PaddingMinBytes",          config_parse_size4096,         -1, &p->padding_min             },
+                { "Partition", "PaddingMaxBytes",          config_parse_size4096,          1, &p->padding_max             },
+                { "Partition", "FactoryReset",             config_parse_bool,              0, &p->factory_reset           },
+                { "Partition", "CopyBlocks",               config_parse_copy_blocks,       0, p                           },
+                { "Partition", "Format",                   config_parse_fstype,            0, &p->format                  },
+                { "Partition", "CopyFiles",                config_parse_copy_files,        0, &p->copy_files              },
+                { "Partition", "ExcludeFiles",             config_parse_exclude_files,     0, &p->exclude_files_source    },
+                { "Partition", "ExcludeFilesTarget",       config_parse_exclude_files,     0, &p->exclude_files_target    },
+                { "Partition", "MakeDirectories",          config_parse_make_dirs,         0, &p->make_directories        },
+                { "Partition", "Encrypt",                  config_parse_encrypt,           0, &p->encrypt                 },
+                { "Partition", "Verity",                   config_parse_verity,            0, &p->verity                  },
+                { "Partition", "VerityMatchKey",           config_parse_string,            0, &p->verity_match_key        },
+                { "Partition", "Flags",                    config_parse_gpt_flags,         0, &p->gpt_flags               },
+                { "Partition", "ReadOnly",                 config_parse_tristate,          0, &p->read_only               },
+                { "Partition", "NoAuto",                   config_parse_tristate,          0, &p->no_auto                 },
+                { "Partition", "GrowFileSystem",           config_parse_tristate,          0, &p->growfs                  },
+                { "Partition", "SplitName",                config_parse_string,            0, &p->split_name_format       },
+                { "Partition", "Minimize",                 config_parse_minimize,          0, &p->minimize                },
+                { "Partition", "Subvolumes",               config_parse_make_dirs,         0, &p->subvolumes              },
+                { "Partition", "DefaultSubvolume",         config_parse_default_subvolume, 0, &p->default_subvolume       },
+                { "Partition", "VerityDataBlockSizeBytes", config_parse_block_size,        0, &p->verity_data_block_size  },
+                { "Partition", "VerityHashBlockSizeBytes", config_parse_block_size,        0, &p->verity_hash_block_size  },
+                { "Partition", "MountPoint",               config_parse_mountpoint,        0, p                           },
+                { "Partition", "EncryptedVolume",          config_parse_encrypted_volume,  0, p                           },
                 {}
         };
         int r;
@@ -2018,6 +2055,14 @@ static int partition_read_definition(Partition *p, const char *path, const char
                 return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EOPNOTSUPP),
                                   "Subvolumes= cannot be used with --offline=yes");
 
+        if (p->default_subvolume && arg_offline > 0)
+                return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EOPNOTSUPP),
+                                  "DefaultSubvolume= cannot be used with --offline=yes");
+
+        if (p->default_subvolume && !path_strv_contains(p->subvolumes, p->default_subvolume))
+                return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EINVAL),
+                                  "DefaultSubvolume= must be one of the paths in Subvolumes=");
+
         /* Verity partitions are read only, let's imply the RO flag hence, unless explicitly configured otherwise. */
         if ((IN_SET(p->type.designator,
                     PARTITION_ROOT_VERITY,
@@ -3736,6 +3781,11 @@ static int prepare_temporary_file(PartitionTarget *t, uint64_t size) {
         return 0;
 }
 
+static bool loop_device_error_is_fatal(const Partition *p, int r) {
+        assert(p);
+        return arg_offline == 0 || (r != -ENOENT && !ERRNO_IS_PRIVILEGE(r)) || !strv_isempty(p->subvolumes) || p->default_subvolume;
+}
+
 static int partition_target_prepare(
                 Context *context,
                 Partition *p,
@@ -3775,7 +3825,7 @@ static int partition_target_prepare(
 
         if (arg_offline <= 0) {
                 r = loop_device_make(whole_fd, O_RDWR, p->offset, size, context->sector_size, 0, LOCK_EX, &d);
-                if (r < 0 && (arg_offline == 0 || (r != -ENOENT && !ERRNO_IS_PRIVILEGE(r)) || !strv_isempty(p->subvolumes)))
+                if (r < 0 && loop_device_error_is_fatal(p, r))
                         return log_error_errno(r, "Failed to make loopback device of future partition %" PRIu64 ": %m", p->partno);
                 if (r >= 0) {
                         t->loop = TAKE_PTR(d);
@@ -4878,6 +4928,27 @@ static int do_make_directories(Partition *p, const char *root) {
         return 0;
 }
 
+static int set_default_subvolume(Partition *p, const char *root) {
+        _cleanup_free_ char *path = NULL;
+        int r;
+
+        assert(p);
+        assert(root);
+
+        if (!p->default_subvolume)
+                return 0;
+
+        path = path_join(root, p->default_subvolume);
+        if (!path)
+                return log_oom();
+
+        r = btrfs_subvol_make_default(path);
+        if (r < 0)
+                return log_error_errno(r, "Failed to make '%s' the default subvolume: %m", p->default_subvolume);
+
+        return 0;
+}
+
 static bool partition_needs_populate(Partition *p) {
         assert(p);
         return !strv_isempty(p->copy_files) || !strv_isempty(p->make_directories);
@@ -4952,6 +5023,9 @@ static int partition_populate_filesystem(Context *context, Partition *p, const c
                 if (do_make_directories(p, fs) < 0)
                         _exit(EXIT_FAILURE);
 
+                if (set_default_subvolume(p, fs) < 0)
+                        _exit(EXIT_FAILURE);
+
                 r = syncfs_path(AT_FDCWD, fs);
                 if (r < 0) {
                         log_error_errno(r, "Failed to synchronize written files: %m");
@@ -6547,7 +6621,7 @@ static int context_minimize(Context *context) {
 
                         if (arg_offline <= 0) {
                                 r = loop_device_make(fd, O_RDWR, 0, UINT64_MAX, context->sector_size, 0, LOCK_EX, &d);
-                                if (r < 0 && (arg_offline == 0 || (r != -ENOENT && !ERRNO_IS_PRIVILEGE(r)) || !strv_isempty(p->subvolumes)))
+                                if (r < 0 && loop_device_error_is_fatal(p, r))
                                         return log_error_errno(r, "Failed to make loopback device of %s: %m", temp);
                         }
 
index a3bec83f0dddf81c0ed8a8f558cbf05c2561d23c..60dc98c5baf313feca9252a499f37ac85ab5621c 100644 (file)
@@ -1664,9 +1664,8 @@ int portable_attach(
         return 0;
 }
 
-static bool marker_matches_images(const char *marker, const char *name_or_path, char **extension_image_paths) {
+static bool marker_matches_images(const char *marker, const char *name_or_path, char **extension_image_paths, bool match_all) {
         _cleanup_strv_free_ char **root_and_extensions = NULL;
-        const char *a;
         int r;
 
         assert(marker);
@@ -1676,7 +1675,9 @@ static bool marker_matches_images(const char *marker, const char *name_or_path,
          * list of images/paths. We enforce strict 1:1 matching, so that we are sure
          * we are detaching exactly what was attached.
          * For each image, starting with the root, we look for a token in the marker,
-         * and return a negative answer on any non-matching combination. */
+         * and return a negative answer on any non-matching combination.
+         * If a partial match is allowed, then return immediately once it is found, otherwise
+         * ensure that everything matches. */
 
         root_and_extensions = strv_new(name_or_path);
         if (!root_and_extensions)
@@ -1686,70 +1687,33 @@ static bool marker_matches_images(const char *marker, const char *name_or_path,
         if (r < 0)
                 return r;
 
-        STRV_FOREACH(image_name_or_path, root_and_extensions) {
-                _cleanup_free_ char *image = NULL;
+        /* Ensure the number of images passed matches the number of images listed in the marker */
+        while (!isempty(marker))
+                STRV_FOREACH(image_name_or_path, root_and_extensions) {
+                        _cleanup_free_ char *image = NULL, *base_image = NULL, *base_image_name_or_path = NULL;
 
-                r = extract_first_word(&marker, &image, ":", EXTRACT_UNQUOTE|EXTRACT_RETAIN_ESCAPE);
-                if (r < 0)
-                        return log_debug_errno(r, "Failed to parse marker: %s", marker);
-                if (r == 0)
-                        return false;
-
-                a = last_path_component(image);
-
-                if (image_name_is_valid(*image_name_or_path)) {
-                        const char *e, *underscore;
-
-                        /* We shall match against an image name. In that case let's compare the last component, and optionally
-                        * allow either a suffix of ".raw" or a series of "/".
-                        * But allow matching on a different version of the same image, when a "_" is used as a separator. */
-                        underscore = strchr(*image_name_or_path, '_');
-                        if (underscore) {
-                                if (strneq(a, *image_name_or_path, underscore - *image_name_or_path))
-                                        continue;
-                                return false;
-                        }
-
-                        e = startswith(a, *image_name_or_path);
-                        if (!e)
-                                return false;
-
-                        if(!(e[strspn(e, "/")] == 0 || streq(e, ".raw")))
-                                return false;
-                } else {
-                        const char *b, *underscore;
-                        size_t l;
-
-                        /* We shall match against a path. Let's ignore any prefix here though, as often there are many ways to
-                         * reach the same file. However, in this mode, let's validate any file suffix.
-                         * But also ensure that we don't fail if both components don't have a '/' at all
-                         * (strcspn returns the full length of the string in that case, which might not
-                         * match as the versions might differ). */
-
-                        l = strcspn(a, "/");
-                        b = last_path_component(*image_name_or_path);
-
-                        if ((a[l] != '/') != !strchr(b, '/')) /* One is a directory, the other is not */
+                        r = extract_first_word(&marker, &image, ":", EXTRACT_UNQUOTE|EXTRACT_RETAIN_ESCAPE);
+                        if (r < 0)
+                                return log_debug_errno(r, "Failed to parse marker: %s", marker);
+                        if (r == 0)
                                 return false;
 
-                        if (a[l] != 0 && strcspn(b, "/") != l)
-                                return false;
+                        r = path_extract_image_name(image, &base_image);
+                        if (r < 0)
+                                return log_debug_errno(r, "Failed to extract image name from %s, ignoring: %m", image);
 
-                        underscore = strchr(b, '_');
-                        if (underscore)
-                                l = underscore - b;
-                        else { /* Either component could be versioned */
-                                underscore = strchr(a, '_');
-                                if (underscore)
-                                        l = underscore - a;
-                        }
+                        r = path_extract_image_name(*image_name_or_path, &base_image_name_or_path);
+                        if (r < 0)
+                                return log_debug_errno(r, "Failed to extract image name from %s, ignoring: %m", *image_name_or_path);
 
-                        if (!strneq(a, b, l))
-                                return false;
+                        if (!streq(base_image, base_image_name_or_path)) {
+                                if (match_all)
+                                        return false;
+                        } else if (!match_all)
+                                return true;
                 }
-        }
 
-        return true;
+        return match_all;
 }
 
 static int test_chroot_dropin(
@@ -1804,7 +1768,9 @@ static int test_chroot_dropin(
         if (!name_or_path)
                 r = true;
         else
-                r = marker_matches_images(marker, name_or_path, extension_image_paths);
+                /* When detaching we want to match exactly on all images, but when inspecting we only need
+                 * to get the state of one component */
+                r = marker_matches_images(marker, name_or_path, extension_image_paths, ret_marker != NULL);
 
         if (ret_marker)
                 *ret_marker = TAKE_PTR(marker);
index 358a5c7594b887b9805724957541bc8996059987..f792167aff3fd133912352fc5447a218fc87ab50 100644 (file)
@@ -26,12 +26,10 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
         assert_se(f = memstream_init(&m));
         (void) fprintf(f, "%s", strna(dns_resource_record_to_string(rr)));
 
-        if (dns_resource_record_to_json(rr, &v) < 0)
-                return 0;
-
-        (void) json_variant_dump(v, JSON_FORMAT_PRETTY|JSON_FORMAT_COLOR|JSON_FORMAT_SOURCE, f, NULL);
-        (void) dns_resource_record_to_wire_format(rr, false);
-        (void) dns_resource_record_to_wire_format(rr, true);
+        assert_se(dns_resource_record_to_json(rr, &v) >= 0);
+        assert_se(json_variant_dump(v, JSON_FORMAT_PRETTY|JSON_FORMAT_COLOR|JSON_FORMAT_SOURCE, f, NULL) >= 0);
+        assert_se(dns_resource_record_to_wire_format(rr, false) >= 0);
+        assert_se(dns_resource_record_to_wire_format(rr, true) >= 0);
 
         return 0;
 }
index d855ded91a3db87b21898ba3c7eed01621ea8585..d336b2c07bf4e8db844ca850bc4a130aa3c093f1 100644 (file)
@@ -118,7 +118,6 @@ if conf.get('ENABLE_DNS_OVER_TLS') == 1
 endif
 
 link_with = [
-        libbasic_gcrypt,
         libshared,
         libsystemd_resolve_core,
 ]
index a373b610b0eb9608047994e405b2167559df8117..233418affcb08fc6e81eeabaafdc02726f5acee9 100644 (file)
@@ -890,8 +890,11 @@ static int dnssec_rrset_verify_sig(
         _cleanup_(gcry_md_closep) gcry_md_hd_t md = NULL;
         void *hash;
         size_t hash_size;
+        int r;
 
-        initialize_libgcrypt(false);
+        r = initialize_libgcrypt(false);
+        if (r < 0)
+                return r;
 #endif
 
         switch (rrsig->rrsig.algorithm) {
@@ -1334,6 +1337,7 @@ static hash_md_t digest_to_hash_md(uint8_t algorithm) {
 
 int dnssec_verify_dnskey_by_ds(DnsResourceRecord *dnskey, DnsResourceRecord *ds, bool mask_revoke) {
         uint8_t wire_format[DNS_WIRE_FORMAT_HOSTNAME_MAX];
+        size_t encoded_length;
         int r;
 
         assert(dnskey);
@@ -1360,6 +1364,7 @@ int dnssec_verify_dnskey_by_ds(DnsResourceRecord *dnskey, DnsResourceRecord *ds,
         r = dns_name_to_wire_format(dns_resource_key_name(dnskey->key), wire_format, sizeof wire_format, true);
         if (r < 0)
                 return r;
+        encoded_length = r;
 
         hash_md_t md_algorithm = digest_to_hash_md(ds->ds.digest_type);
 
@@ -1383,7 +1388,7 @@ int dnssec_verify_dnskey_by_ds(DnsResourceRecord *dnskey, DnsResourceRecord *ds,
         if (EVP_DigestInit_ex(ctx, md_algorithm, NULL) <= 0)
                 return -EIO;
 
-        if (EVP_DigestUpdate(ctx, wire_format, r) <= 0)
+        if (EVP_DigestUpdate(ctx, wire_format, encoded_length) <= 0)
                 return -EIO;
 
         if (mask_revoke)
@@ -1407,7 +1412,9 @@ int dnssec_verify_dnskey_by_ds(DnsResourceRecord *dnskey, DnsResourceRecord *ds,
         if (md_algorithm < 0)
                 return -EOPNOTSUPP;
 
-        initialize_libgcrypt(false);
+        r = initialize_libgcrypt(false);
+        if (r < 0)
+                return r;
 
         _cleanup_(gcry_md_closep) gcry_md_hd_t md = NULL;
 
@@ -1421,7 +1428,7 @@ int dnssec_verify_dnskey_by_ds(DnsResourceRecord *dnskey, DnsResourceRecord *ds,
         if (gcry_err_code(err) != GPG_ERR_NO_ERROR || !md)
                 return -EIO;
 
-        gcry_md_write(md, wire_format, r);
+        gcry_md_write(md, wire_format, encoded_length);
         if (mask_revoke)
                 md_add_uint16(md, dnskey->dnskey.flags & ~DNSKEY_FLAG_REVOKE);
         else
@@ -1552,8 +1559,11 @@ int dnssec_nsec3_hash(DnsResourceRecord *nsec3, const char *name, void *ret) {
         if (algorithm < 0)
                 return algorithm;
 
-        initialize_libgcrypt(false);
+        r = initialize_libgcrypt(false);
+        if (r < 0)
+                return r;
 
+        size_t encoded_length;
         unsigned hash_size = gcry_md_get_algo_dlen(algorithm);
         assert(hash_size > 0);
 
@@ -1563,13 +1573,14 @@ int dnssec_nsec3_hash(DnsResourceRecord *nsec3, const char *name, void *ret) {
         r = dns_name_to_wire_format(name, wire_format, sizeof(wire_format), true);
         if (r < 0)
                 return r;
+        encoded_length = r;
 
         _cleanup_(gcry_md_closep) gcry_md_hd_t md = NULL;
         gcry_error_t err = gcry_md_open(&md, algorithm, 0);
         if (gcry_err_code(err) != GPG_ERR_NO_ERROR || !md)
                 return -EIO;
 
-        gcry_md_write(md, wire_format, r);
+        gcry_md_write(md, wire_format, encoded_length);
         gcry_md_write(md, nsec3->nsec3.salt, nsec3->nsec3.salt_size);
 
         void *result = gcry_md_read(md, 0);
index 2739bed41a3e56515a9e8971c5ddee492f9a9829..204d4a625ee6f48f51ba500411b9ef7e356c01de 100644 (file)
@@ -196,7 +196,7 @@ bool dns_resource_key_is_dnssd_two_label_ptr(const DnsResourceKey *key) {
         if (dns_name_parent(&name) <= 0)
                 return false;
 
-        return dns_name_equal(name, "_tcp.local") || dns_name_equal(name, "_udp.local");
+        return dns_name_equal(name, "_tcp.local") > 0 || dns_name_equal(name, "_udp.local") > 0;
 }
 
 int dns_resource_key_equal(const DnsResourceKey *a, const DnsResourceKey *b) {
@@ -679,19 +679,24 @@ int dns_resource_record_payload_equal(const DnsResourceRecord *a, const DnsResou
 
         case DNS_TYPE_RRSIG:
                 /* do the fast comparisons first */
-                return a->rrsig.type_covered == b->rrsig.type_covered &&
-                       a->rrsig.algorithm == b->rrsig.algorithm &&
-                       a->rrsig.labels == b->rrsig.labels &&
-                       a->rrsig.original_ttl == b->rrsig.original_ttl &&
-                       a->rrsig.expiration == b->rrsig.expiration &&
-                       a->rrsig.inception == b->rrsig.inception &&
-                       a->rrsig.key_tag == b->rrsig.key_tag &&
-                       FIELD_EQUAL(a->rrsig, b->rrsig, signature) &&
-                       dns_name_equal(a->rrsig.signer, b->rrsig.signer);
+                if (!(a->rrsig.type_covered == b->rrsig.type_covered &&
+                      a->rrsig.algorithm == b->rrsig.algorithm &&
+                      a->rrsig.labels == b->rrsig.labels &&
+                      a->rrsig.original_ttl == b->rrsig.original_ttl &&
+                      a->rrsig.expiration == b->rrsig.expiration &&
+                      a->rrsig.inception == b->rrsig.inception &&
+                      a->rrsig.key_tag == b->rrsig.key_tag &&
+                      FIELD_EQUAL(a->rrsig, b->rrsig, signature)))
+                        return false;
+
+                return dns_name_equal(a->rrsig.signer, b->rrsig.signer);
 
         case DNS_TYPE_NSEC:
-                return dns_name_equal(a->nsec.next_domain_name, b->nsec.next_domain_name) &&
-                       bitmap_equal(a->nsec.types, b->nsec.types);
+                r = dns_name_equal(a->nsec.next_domain_name, b->nsec.next_domain_name);
+                if (r <= 0)
+                        return r;
+
+                return bitmap_equal(a->nsec.types, b->nsec.types);
 
         case DNS_TYPE_NSEC3:
                 return a->nsec3.algorithm == b->nsec3.algorithm &&
@@ -709,9 +714,12 @@ int dns_resource_record_payload_equal(const DnsResourceRecord *a, const DnsResou
 
         case DNS_TYPE_SVCB:
         case DNS_TYPE_HTTPS:
-                return a->svcb.priority == b->svcb.priority &&
-                       dns_name_equal(a->svcb.target_name, b->svcb.target_name) &&
-                       dns_svc_params_equal(a->svcb.params, b->svcb.params);
+
+                if (!(a->svcb.priority == b->svcb.priority &&
+                      dns_svc_params_equal(a->svcb.params, b->svcb.params)))
+                        return false;
+
+                return dns_name_equal(a->svcb.target_name, b->svcb.target_name);
 
         case DNS_TYPE_CAA:
                 return a->caa.flags == b->caa.flags &&
index 392f416c98f63243430f5badc026d9a3fb509a5c..f6ce3e38db451f3a71975d5cdf10f33f72e3983a 100644 (file)
@@ -898,9 +898,7 @@ static int dns_transaction_dnssec_ready(DnsTransaction *t) {
                         /* All good. */
                         break;
 
-                case DNS_TRANSACTION_DNSSEC_FAILED: {
-                        DnsAnswer *empty;
-
+                case DNS_TRANSACTION_DNSSEC_FAILED:
                         /* We handle DNSSEC failures different from other errors, as we care about the DNSSEC
                          * validation result */
 
@@ -921,14 +919,10 @@ static int dns_transaction_dnssec_ready(DnsTransaction *t) {
 
                         /* The answer would normally be replaced by the validated subset, but at this point
                          * we aren't going to bother validating the rest, so just drop it. */
-                        empty = dns_answer_new(0);
-                        if (!empty)
-                                return -ENOMEM;
-                        DNS_ANSWER_REPLACE(t->answer, empty);
+                        t->answer = dns_answer_unref(t->answer);
 
                         dns_transaction_complete(t, DNS_TRANSACTION_DNSSEC_FAILED);
                         return 0;
-                }
 
                 default:
                         log_debug("Auxiliary DNSSEC RR query failed with %s", dns_transaction_state_to_string(dt->state));
@@ -2347,9 +2341,9 @@ static int dns_transaction_request_dnssec_rr_full(DnsTransaction *t, DnsResource
                 r = dns_transaction_go(aux);
                 if (r < 0)
                         return r;
-                if (ret)
-                        *ret = aux;
         }
+        if (ret)
+                *ret = aux;
 
         return 1;
 }
index 7f8f99717c03ac00fc2eeeabf9c47087c4918572..aadac3f10250c539511a98883cbf4212199d3b0f 100644 (file)
@@ -364,7 +364,7 @@ int dnssd_signal_conflict(Manager *manager, const char *name) {
                 if (s->withdrawn)
                         continue;
 
-                if (dns_name_equal(dns_resource_key_name(s->srv_rr->key), name)) {
+                if (dns_name_equal(dns_resource_key_name(s->srv_rr->key), name) > 0) {
                         _cleanup_free_ char *path = NULL;
 
                         s->withdrawn = true;
index 6b835904be148483ae8937169ed778974cf024aa..25f85d82331103e77185f6c8b768b755222f9299 100644 (file)
@@ -1238,7 +1238,7 @@ static int vl_method_subscribe_query_results(Varlink *link, JsonVariant *paramet
 
         /* if the client didn't set the more flag, it is using us incorrectly */
         if (!FLAGS_SET(flags, VARLINK_METHOD_MORE))
-                return varlink_error_invalid_parameter(link, NULL);
+                return varlink_error(link, VARLINK_ERROR_EXPECTED_MORE, NULL);
 
         if (json_variant_elements(parameters) > 0)
                 return varlink_error_invalid_parameter(link, parameters);
index 7f40ed007602c7dd6162f21a7c9517df1358b94a..9d84d063316ee324421853cb7b36fab3c2fbf26b 100644 (file)
@@ -1313,6 +1313,7 @@ static int transient_timer_set_properties(sd_bus_message *m) {
 }
 
 static int make_unit_name(sd_bus *bus, UnitType t, char **ret) {
+        unsigned soft_reboots_count = 0;
         const char *unique, *id;
         char *p;
         int r;
@@ -1320,6 +1321,7 @@ static int make_unit_name(sd_bus *bus, UnitType t, char **ret) {
         assert(bus);
         assert(t >= 0);
         assert(t < _UNIT_TYPE_MAX);
+        assert(ret);
 
         r = sd_bus_get_unique_name(bus, &unique);
         if (r < 0) {
@@ -1351,9 +1353,27 @@ static int make_unit_name(sd_bus *bus, UnitType t, char **ret) {
                                        "Unique name %s has unexpected format.",
                                        unique);
 
-        p = strjoin("run-u", id, ".", unit_type_to_string(t));
-        if (!p)
-                return log_oom();
+        /* The unique D-Bus names are actually unique per D-Bus instance, so on soft-reboot they will wrap
+         * and start over since the D-Bus broker is restarted. If there's a failed unit left behind that
+         * hasn't been garbage collected, we'll conflict. Append the soft-reboot counter to avoid clashing. */
+        if (arg_runtime_scope == RUNTIME_SCOPE_SYSTEM) {
+                _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+                r = bus_get_property_trivial(
+                                bus, bus_systemd_mgr, "SoftRebootsCount", &error, 'u', &soft_reboots_count);
+                if (r < 0)
+                        log_debug_errno(r,
+                                        "Failed to get SoftRebootsCount property, ignoring: %s",
+                                        bus_error_message(&error, r));
+        }
+
+        if (soft_reboots_count > 0) {
+                if (asprintf(&p, "run-u%s-s%u.%s", id, soft_reboots_count, unit_type_to_string(t)) < 0)
+                        return log_oom();
+        } else {
+                p = strjoin("run-u", id, ".", unit_type_to_string(t));
+                if (!p)
+                        return log_oom();
+        }
 
         *ret = p;
         return 0;
index 569ef466c3f2087fa8069b668ada201a741bc898..a4e2dae24541408912f974678ccb1606d2c92bb9 100644 (file)
@@ -120,13 +120,13 @@ static const BaseFilesystem table[] = {
 #  else
 #    error "Unknown RISC-V ABI"
 #  endif
-#elif defined(__s390__)
-        /* s390-linux-gnu */
 #elif defined(__s390x__)
         { "lib64",    0, "usr/lib/"LIB_ARCH_TUPLE"\0"
                          "usr/lib64\0"
                          "usr/lib\0",                "ld-lsb-s390x.so.3" },
 #    define KNOW_LIB64_DIRS 1
+#elif defined(__s390__)
+        /* s390-linux-gnu */
 #elif defined(__sparc__)
 #endif
         /* gcc doesn't allow pragma to be used within constructs, hence log about this separately below */
index 9ccb7d8205e3c16dd152f173b8eaff08cc06a6e2..5a7945cb9bc3e2848ebfeffe7696c0caed26e0de 100644 (file)
@@ -26,6 +26,7 @@ struct bpf_map_create_opts;
  * When removing this file move these back to bpf-dlopen.h */
 extern int (*sym_bpf_map_create)(enum bpf_map_type,  const char *, __u32, __u32, __u32, const struct bpf_map_create_opts *);
 extern int (*sym_libbpf_probe_bpf_prog_type)(enum bpf_prog_type, const void *);
+extern struct bpf_map* (*sym_bpf_object__next_map)(const struct bpf_object *obj, const struct bpf_map *map);
 
 /* compat symbols removed in libbpf 1.0 */
 extern int (*sym_bpf_create_map)(enum bpf_map_type, int key_size, int value_size, int max_entries, __u32 map_flags);
index 70b1ffbf99df001160126aec0b0cc678f9a23f75..de3e40516ebba3a3cd5774d519660ec704b0d08c 100644 (file)
 #define MODERN_LIBBPF 0
 #endif
 
-DLSYM_FUNCTION(bpf_program__attach_cgroup);
-DLSYM_FUNCTION(bpf_program__attach_lsm);
-DLSYM_FUNCTION(bpf_link__fd);
 DLSYM_FUNCTION(bpf_link__destroy);
+DLSYM_FUNCTION(bpf_link__fd);
+DLSYM_FUNCTION(bpf_link__open);
+DLSYM_FUNCTION(bpf_link__pin);
 DLSYM_FUNCTION(bpf_map__fd);
 DLSYM_FUNCTION(bpf_map__name);
+DLSYM_FUNCTION(bpf_map__set_inner_map_fd);
 DLSYM_FUNCTION(bpf_map__set_max_entries);
-DLSYM_FUNCTION(bpf_map_update_elem);
+DLSYM_FUNCTION(bpf_map__set_pin_path);
 DLSYM_FUNCTION(bpf_map_delete_elem);
-DLSYM_FUNCTION(bpf_map__set_inner_map_fd);
-DLSYM_FUNCTION(bpf_object__open_skeleton);
-DLSYM_FUNCTION(bpf_object__load_skeleton);
+DLSYM_FUNCTION(bpf_map_get_fd_by_id);
+DLSYM_FUNCTION(bpf_map_lookup_elem);
+DLSYM_FUNCTION(bpf_map_update_elem);
 DLSYM_FUNCTION(bpf_object__attach_skeleton);
-DLSYM_FUNCTION(bpf_object__detach_skeleton);
 DLSYM_FUNCTION(bpf_object__destroy_skeleton);
+DLSYM_FUNCTION(bpf_object__detach_skeleton);
+DLSYM_FUNCTION(bpf_object__load_skeleton);
+DLSYM_FUNCTION(bpf_object__name);
+DLSYM_FUNCTION(bpf_object__open_skeleton);
+DLSYM_FUNCTION(bpf_object__pin_maps);
+DLSYM_FUNCTION(bpf_program__attach);
+DLSYM_FUNCTION(bpf_program__attach_cgroup);
+DLSYM_FUNCTION(bpf_program__attach_lsm);
 DLSYM_FUNCTION(bpf_program__name);
-DLSYM_FUNCTION(libbpf_set_print);
 DLSYM_FUNCTION(libbpf_get_error);
+DLSYM_FUNCTION(libbpf_set_print);
+DLSYM_FUNCTION(ring_buffer__epoll_fd);
+DLSYM_FUNCTION(ring_buffer__free);
+DLSYM_FUNCTION(ring_buffer__new);
+DLSYM_FUNCTION(ring_buffer__poll);
 
 /* new symbols available from libbpf 0.7.0 */
 int (*sym_bpf_map_create)(enum bpf_map_type,  const char *, __u32, __u32, __u32, const struct bpf_map_create_opts *);
 int (*sym_libbpf_probe_bpf_prog_type)(enum bpf_prog_type, const void *);
+struct bpf_map* (*sym_bpf_object__next_map)(const struct bpf_object *obj, const struct bpf_map *map);
 
 /* compat symbols removed in libbpf 1.0 */
 int (*sym_bpf_create_map)(enum bpf_map_type,  int key_size, int value_size, int max_entries, __u32 map_flags);
@@ -76,58 +89,82 @@ int dlopen_bpf(void) {
                         return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
                                                "neither libbpf.so.1 nor libbpf.so.0 are installed: %s", dlerror());
 
+                log_debug("Loaded 'libbpf.so.0' via dlopen()");
+
                 /* symbols deprecated in 1.0 we use as compat */
                 r = dlsym_many_or_warn(
                                 dl, LOG_DEBUG,
 #if MODERN_LIBBPF
                                 /* Don't exist anymore in new libbpf, hence cannot type check them */
                                 DLSYM_ARG_FORCE(bpf_create_map),
-                                DLSYM_ARG_FORCE(bpf_probe_prog_type));
+                                DLSYM_ARG_FORCE(bpf_probe_prog_type)
 #else
                                 DLSYM_ARG(bpf_create_map),
-                                DLSYM_ARG(bpf_probe_prog_type));
+                                DLSYM_ARG(bpf_probe_prog_type)
 #endif
+                );
+
+                /* NB: we don't try to load bpf_object__next_map() on old versions */
         } else {
+                log_debug("Loaded 'libbpf.so.1' via dlopen()");
+
                 /* symbols available from 0.7.0 */
                 r = dlsym_many_or_warn(
                                 dl, LOG_DEBUG,
 #if MODERN_LIBBPF
                                 DLSYM_ARG(bpf_map_create),
-                                DLSYM_ARG(libbpf_probe_bpf_prog_type)
+                                DLSYM_ARG(libbpf_probe_bpf_prog_type),
+                                DLSYM_ARG(bpf_object__next_map)
 #else
                                 /* These symbols did not exist in old libbpf, hence we cannot type check them */
                                 DLSYM_ARG_FORCE(bpf_map_create),
-                                DLSYM_ARG_FORCE(libbpf_probe_bpf_prog_type)
+                                DLSYM_ARG_FORCE(libbpf_probe_bpf_prog_type),
+                                DLSYM_ARG_FORCE(bpf_object__next_map)
 #endif
                 );
         }
+        if (r < 0)
+                return r;
 
         r = dlsym_many_or_warn(
                         dl, LOG_DEBUG,
                         DLSYM_ARG(bpf_link__destroy),
                         DLSYM_ARG(bpf_link__fd),
+                        DLSYM_ARG(bpf_link__open),
+                        DLSYM_ARG(bpf_link__pin),
                         DLSYM_ARG(bpf_map__fd),
                         DLSYM_ARG(bpf_map__name),
+                        DLSYM_ARG(bpf_map__set_inner_map_fd),
                         DLSYM_ARG(bpf_map__set_max_entries),
-                        DLSYM_ARG(bpf_map_update_elem),
+                        DLSYM_ARG(bpf_map__set_pin_path),
                         DLSYM_ARG(bpf_map_delete_elem),
-                        DLSYM_ARG(bpf_map__set_inner_map_fd),
-                        DLSYM_ARG(bpf_object__open_skeleton),
-                        DLSYM_ARG(bpf_object__load_skeleton),
+                        DLSYM_ARG(bpf_map_get_fd_by_id),
+                        DLSYM_ARG(bpf_map_lookup_elem),
+                        DLSYM_ARG(bpf_map_update_elem),
                         DLSYM_ARG(bpf_object__attach_skeleton),
-                        DLSYM_ARG(bpf_object__detach_skeleton),
                         DLSYM_ARG(bpf_object__destroy_skeleton),
+                        DLSYM_ARG(bpf_object__detach_skeleton),
+                        DLSYM_ARG(bpf_object__load_skeleton),
+                        DLSYM_ARG(bpf_object__name),
+                        DLSYM_ARG(bpf_object__open_skeleton),
+                        DLSYM_ARG(bpf_object__pin_maps),
 #if MODERN_LIBBPF
+                        DLSYM_ARG(bpf_program__attach),
                         DLSYM_ARG(bpf_program__attach_cgroup),
                         DLSYM_ARG(bpf_program__attach_lsm),
 #else
                         /* libbpf added a "const" to function parameters where it should not have, ignore this type incompatibility */
+                        DLSYM_ARG_FORCE(bpf_program__attach),
                         DLSYM_ARG_FORCE(bpf_program__attach_cgroup),
                         DLSYM_ARG_FORCE(bpf_program__attach_lsm),
 #endif
                         DLSYM_ARG(bpf_program__name),
+                        DLSYM_ARG(libbpf_get_error),
                         DLSYM_ARG(libbpf_set_print),
-                        DLSYM_ARG(libbpf_get_error));
+                        DLSYM_ARG(ring_buffer__epoll_fd),
+                        DLSYM_ARG(ring_buffer__free),
+                        DLSYM_ARG(ring_buffer__new),
+                        DLSYM_ARG(ring_buffer__poll));
         if (r < 0)
                 return r;
 
index 859405e3c69640000cecce543b6eef431247502c..6deca829f16b85a312cbd4e525cbf87c87a9595e 100644 (file)
@@ -9,26 +9,38 @@
 #include "bpf-compat.h"
 #include "dlfcn-util.h"
 
-DLSYM_PROTOTYPE(bpf_program__attach_cgroup);
-DLSYM_PROTOTYPE(bpf_program__attach_lsm);
-DLSYM_PROTOTYPE(bpf_link__fd);
 DLSYM_PROTOTYPE(bpf_link__destroy);
+DLSYM_PROTOTYPE(bpf_link__fd);
+DLSYM_PROTOTYPE(bpf_link__open);
+DLSYM_PROTOTYPE(bpf_link__pin);
 DLSYM_PROTOTYPE(bpf_map__fd);
 DLSYM_PROTOTYPE(bpf_map__name);
+DLSYM_PROTOTYPE(bpf_map__set_inner_map_fd);
 DLSYM_PROTOTYPE(bpf_map__set_max_entries);
-DLSYM_PROTOTYPE(bpf_map_update_elem);
+DLSYM_PROTOTYPE(bpf_map__set_pin_path);
 DLSYM_PROTOTYPE(bpf_map_delete_elem);
-DLSYM_PROTOTYPE(bpf_map__set_inner_map_fd);
+DLSYM_PROTOTYPE(bpf_map_get_fd_by_id);
+DLSYM_PROTOTYPE(bpf_map_lookup_elem);
+DLSYM_PROTOTYPE(bpf_map_update_elem);
 /* The *_skeleton APIs are autogenerated by bpftool, the targets can be found
  * in ./build/src/core/bpf/socket_bind/socket-bind.skel.h */
-DLSYM_PROTOTYPE(bpf_object__open_skeleton);
-DLSYM_PROTOTYPE(bpf_object__load_skeleton);
 DLSYM_PROTOTYPE(bpf_object__attach_skeleton);
-DLSYM_PROTOTYPE(bpf_object__detach_skeleton);
 DLSYM_PROTOTYPE(bpf_object__destroy_skeleton);
+DLSYM_PROTOTYPE(bpf_object__detach_skeleton);
+DLSYM_PROTOTYPE(bpf_object__load_skeleton);
+DLSYM_PROTOTYPE(bpf_object__name);
+DLSYM_PROTOTYPE(bpf_object__open_skeleton);
+DLSYM_PROTOTYPE(bpf_object__pin_maps);
+DLSYM_PROTOTYPE(bpf_program__attach);
+DLSYM_PROTOTYPE(bpf_program__attach_cgroup);
+DLSYM_PROTOTYPE(bpf_program__attach_lsm);
 DLSYM_PROTOTYPE(bpf_program__name);
-DLSYM_PROTOTYPE(libbpf_set_print);
 DLSYM_PROTOTYPE(libbpf_get_error);
+DLSYM_PROTOTYPE(libbpf_set_print);
+DLSYM_PROTOTYPE(ring_buffer__epoll_fd);
+DLSYM_PROTOTYPE(ring_buffer__free);
+DLSYM_PROTOTYPE(ring_buffer__new);
+DLSYM_PROTOTYPE(ring_buffer__poll);
 
 #endif
 
index 2ed6bf24a2db0c0ce974546b78a05133f28949b9..5db88221f37f74cf90570256f8bbb1299046cb3e 100644 (file)
@@ -1785,6 +1785,24 @@ int btrfs_subvol_auto_qgroup(const char *path, uint64_t subvol_id, bool create_i
         return btrfs_subvol_auto_qgroup_fd(fd, subvol_id, create_intermediary_qgroup);
 }
 
+int btrfs_subvol_make_default(const char *path) {
+        _cleanup_close_ int fd = -EBADF;
+        uint64_t id;
+        int r;
+
+        assert(path);
+
+        fd = open(path, O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
+        if (fd < 0)
+                return -errno;
+
+        r = btrfs_subvol_get_id_fd(fd, &id);
+        if (r < 0)
+                return r;
+
+        return RET_NERRNO(ioctl(fd, BTRFS_IOC_DEFAULT_SUBVOL, &id));
+}
+
 int btrfs_subvol_get_parent(int fd, uint64_t subvol_id, uint64_t *ret) {
 
         struct btrfs_ioctl_search_args args = {
index cd80903190df35579731b396a9226a2887700bc6..6108a264c0cbfd57c7b767699d8021e121665321 100644 (file)
@@ -108,6 +108,8 @@ int btrfs_subvol_set_subtree_quota_limit_fd(int fd, uint64_t subvol_id, uint64_t
 int btrfs_subvol_auto_qgroup_fd(int fd, uint64_t subvol_id, bool new_qgroup);
 int btrfs_subvol_auto_qgroup(const char *path, uint64_t subvol_id, bool create_intermediary_qgroup);
 
+int btrfs_subvol_make_default(const char *path);
+
 int btrfs_qgroupid_make(uint64_t level, uint64_t id, uint64_t *ret);
 int btrfs_qgroupid_split(uint64_t qgroupid, uint64_t *level, uint64_t *id);
 
index e0e9e879540110035efce97282d7f034b7b0503f..c0752b59bb0c596926d30a53663369a2c716c49e 100644 (file)
@@ -728,8 +728,10 @@ static bool varlink_allow_interactive_authentication(Varlink *link) {
          * always under the same name. */
 
         r = varlink_get_current_parameters(link, &v);
-        if (r < 0)
-                return r;
+        if (r < 0) {
+                log_debug_errno(r, "Unable to query current parameters: %m");
+                return false;
+        }
 
         JsonVariant *b;
         b = json_variant_by_key(v, "allowInteractiveAuthentication");
index d9cca6ced41d74774ba9cfb11ea7d99d1a050283..6896a03da54e1406837fd8b4fd8ba0671eea9529 100644 (file)
@@ -384,6 +384,20 @@ int cg_attach(const char *controller, const char *path, pid_t pid) {
         return 0;
 }
 
+int cg_fd_attach(int fd, pid_t pid) {
+        char c[DECIMAL_STR_MAX(pid_t) + 2];
+
+        assert(fd >= 0);
+        assert(pid >= 0);
+
+        if (pid == 0)
+                pid = getpid_cached();
+
+        xsprintf(c, PID_FMT "\n", pid);
+
+        return write_string_file_at(fd, "cgroup.procs", c, WRITE_STRING_FILE_DISABLE_BUFFER);
+}
+
 int cg_attach_fallback(const char *controller, const char *path, pid_t pid) {
         int r;
 
@@ -584,75 +598,52 @@ int cg_migrate(
         bool done = false;
         _cleanup_set_free_ Set *s = NULL;
         int r, ret = 0;
-        pid_t my_pid;
 
         assert(cfrom);
         assert(pfrom);
         assert(cto);
         assert(pto);
 
-        s = set_new(NULL);
-        if (!s)
-                return -ENOMEM;
-
-        my_pid = getpid_cached();
-
         do {
                 _cleanup_fclose_ FILE *f = NULL;
-                pid_t pid = 0;
+                pid_t pid;
+
                 done = true;
 
                 r = cg_enumerate_processes(cfrom, pfrom, &f);
-                if (r < 0) {
-                        if (ret >= 0 && r != -ENOENT)
-                                return r;
-
-                        return ret;
-                }
+                if (r < 0)
+                        return RET_GATHER(ret, r);
 
                 while ((r = cg_read_pid(f, &pid)) > 0) {
-
-                        /* This might do weird stuff if we aren't a
-                         * single-threaded program. However, we
-                         * luckily know we are not */
-                        if ((flags & CGROUP_IGNORE_SELF) && pid == my_pid)
+                        /* This might do weird stuff if we aren't a single-threaded program. However, we
+                         * luckily know we are. */
+                        if (FLAGS_SET(flags, CGROUP_IGNORE_SELF) && pid == getpid_cached())
                                 continue;
 
-                        if (set_get(s, PID_TO_PTR(pid)) == PID_TO_PTR(pid))
+                        if (set_contains(s, PID_TO_PTR(pid)))
                                 continue;
 
-                        /* Ignore kernel threads. Since they can only
-                         * exist in the root cgroup, we only check for
-                         * them there. */
-                        if (cfrom &&
-                            empty_or_root(pfrom) &&
+                        /* Ignore kernel threads. Since they can only exist in the root cgroup, we only
+                         * check for them there. */
+                        if (cfrom && empty_or_root(pfrom) &&
                             pid_is_kernel_thread(pid) > 0)
                                 continue;
 
                         r = cg_attach(cto, pto, pid);
                         if (r < 0) {
-                                if (ret >= 0 && r != -ESRCH)
-                                        ret = r;
+                                if (r != -ESRCH)
+                                        RET_GATHER(ret, r);
                         } else if (ret == 0)
                                 ret = 1;
 
                         done = false;
 
-                        r = set_put(s, PID_TO_PTR(pid));
-                        if (r < 0) {
-                                if (ret >= 0)
-                                        return r;
-
-                                return ret;
-                        }
-                }
-
-                if (r < 0) {
-                        if (ret >= 0)
-                                return r;
-
-                        return ret;
+                        r = set_ensure_put(&s, /* hash_ops = */ NULL, PID_TO_PTR(pid));
+                        if (r < 0)
+                                return RET_GATHER(ret, r);
                 }
+                if (r < 0)
+                        return RET_GATHER(ret, r);
         } while (!done);
 
         return ret;
index c35884ee4d67a321cc8b1f12f723c756b8f6c473..283ab6757c93649cf25bcbe38f8324461c7bd18e 100644 (file)
@@ -21,6 +21,7 @@ int cg_trim(const char *controller, const char *path, bool delete_root);
 
 int cg_create(const char *controller, const char *path);
 int cg_attach(const char *controller, const char *path, pid_t pid);
+int cg_fd_attach(int fd, pid_t pid);
 int cg_attach_fallback(const char *controller, const char *path, pid_t pid);
 int cg_create_and_attach(const char *controller, const char *path, pid_t pid);
 
index c2ee1c5aef6c7974af89d65c6d8a0ec6c196db47..c7af10687df13b43c71240a06999c73d30e05dd6 100644 (file)
@@ -150,18 +150,9 @@ static int show_cgroup_name(
         delegate = r > 0;
 
         if (FLAGS_SET(flags, OUTPUT_CGROUP_ID)) {
-                cg_file_handle fh = CG_FILE_HANDLE_INIT;
-                int mnt_id = -1;
-
-                if (name_to_handle_at(
-                                    fd,
-                                    "",
-                                    &fh.file_handle,
-                                    &mnt_id,
-                                    AT_EMPTY_PATH) < 0)
+                r = cg_fd_get_cgroupid(fd, &cgroupid);
+                if (r < 0)
                         log_debug_errno(errno, "Failed to determine cgroup ID of %s, ignoring: %m", path);
-                else
-                        cgroupid = CG_FILE_HANDLE_CGROUPID(fh);
         }
 
         r = path_extract_filename(path, &b);
index ba8aee335541aab8add01185327f622b6b5bc8a8..bd2af6d172477df28534fa476871634db3a6d0ca 100644 (file)
@@ -12,6 +12,7 @@
 #include "capability-util.h"
 #include "chattr-util.h"
 #include "constants.h"
+#include "copy.h"
 #include "creds-util.h"
 #include "efi-api.h"
 #include "env-util.h"
 #include "fs-util.h"
 #include "io-util.h"
 #include "memory-util.h"
-#include "mkdir.h"
+#include "mkdir-label.h"
 #include "openssl-util.h"
 #include "parse-util.h"
 #include "path-util.h"
 #include "random-util.h"
+#include "recurse-dir.h"
 #include "sparse-endian.h"
 #include "stat-util.h"
 #include "tmpfile-util.h"
@@ -1652,3 +1654,90 @@ int ipc_decrypt_credential(const char *validate_name, usec_t validate_timestamp,
 
         return 0;
 }
+
+static int pick_up_credential_one(
+                int credential_dir_fd,
+                const char *credential_name,
+                const PickUpCredential *table_entry) {
+
+        _cleanup_free_ char *fn = NULL, *target_path = NULL;
+        const char *e;
+        int r;
+
+        assert(credential_dir_fd >= 0);
+        assert(credential_name);
+        assert(table_entry);
+
+        e = startswith(credential_name, table_entry->credential_prefix);
+        if (!e)
+                return 0; /* unmatched */
+
+        fn = strjoin(e, table_entry->filename_suffix);
+        if (!fn)
+                return log_oom();
+
+        if (!filename_is_valid(fn))
+                return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
+                                         "Passed credential '%s' would result in invalid filename '%s'.",
+                                         credential_name, fn);
+
+        r = mkdir_p_label(table_entry->target_dir, 0755);
+        if (r < 0)
+                return log_warning_errno(r, "Failed to create '%s': %m", table_entry->target_dir);
+
+        target_path = path_join(table_entry->target_dir, fn);
+        if (!target_path)
+                return log_oom();
+
+        r = copy_file_at(
+                        credential_dir_fd, credential_name,
+                        AT_FDCWD, target_path,
+                        /* open_flags= */ 0,
+                        0644,
+                        /* flags= */ 0);
+        if (r < 0)
+                return log_warning_errno(r, "Failed to copy credential %s → file %s: %m",
+                                         credential_name, target_path);
+
+        log_info("Installed %s from credential.", target_path);
+        return 1; /* done */
+}
+
+int pick_up_credentials(const PickUpCredential *table, size_t n_table_entry) {
+        _cleanup_close_ int credential_dir_fd = -EBADF;
+        int r, ret = 0;
+
+        assert(table);
+        assert(n_table_entry > 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. */
+                log_debug("No credentials found.");
+                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) {
+                struct dirent *de = *i;
+
+                if (de->d_type != DT_REG)
+                        continue;
+
+                FOREACH_ARRAY(t, table, n_table_entry) {
+                        r = pick_up_credential_one(credential_dir_fd, de->d_name, t);
+                        if (r != 0) {
+                                RET_GATHER(ret, r);
+                                break; /* Done, or failed. Let's move to the next credential. */
+                        }
+                }
+        }
+
+        return ret;
+}
index e56292c79588617b1253876fc1a4f5b3c064c546..b80755b7d7b04a01ef2e4335d63e08fdb703bbb1 100644 (file)
@@ -93,3 +93,11 @@ int decrypt_credential_and_warn(const char *validate_name, usec_t validate_times
 
 int ipc_encrypt_credential(const char *name, usec_t timestamp, usec_t not_after, uid_t uid, const struct iovec *input, CredentialFlags flags, struct iovec *ret);
 int ipc_decrypt_credential(const char *validate_name, usec_t validate_timestamp, uid_t uid, const struct iovec *input, CredentialFlags flags, struct iovec *ret);
+
+typedef struct PickUpCredential {
+        const char *credential_prefix;
+        const char *target_dir;
+        const char *filename_suffix;
+} PickUpCredential;
+
+int pick_up_credentials(const PickUpCredential *table, size_t n_table_entry);
index 00d32a9c87673ad74ee25e350839157d9c54ebd2..b7a97f2b63aa475e20d3b7cae2d5b69ae45772bb 100644 (file)
@@ -48,7 +48,7 @@
 #include "vpick.h"
 #include "xattr-util.h"
 
-static const char* const image_search_path[_IMAGE_CLASS_MAX] = {
+const char* const image_search_path[_IMAGE_CLASS_MAX] = {
         [IMAGE_MACHINE] =   "/etc/machines\0"              /* only place symlinks here */
                             "/run/machines\0"              /* and here too */
                             "/var/lib/machines\0"          /* the main place for images */
@@ -1295,7 +1295,12 @@ static void make_lock_dir(void) {
         (void) mkdir("/run/systemd/nspawn/locks", 0700);
 }
 
-int image_path_lock(const char *path, int operation, LockFile *global, LockFile *local) {
+int image_path_lock(
+                const char *path,
+                int operation,
+                LockFile *ret_global,
+                LockFile *ret_local) {
+
         _cleanup_free_ char *p = NULL;
         LockFile t = LOCK_FILE_INIT;
         struct stat st;
@@ -1303,8 +1308,7 @@ int image_path_lock(const char *path, int operation, LockFile *global, LockFile
         int r;
 
         assert(path);
-        assert(global);
-        assert(local);
+        assert(ret_local);
 
         /* Locks an image path. This actually creates two locks: one "local" one, next to the image path
          * itself, which might be shared via NFS. And another "global" one, in /run, that uses the
@@ -1326,7 +1330,9 @@ int image_path_lock(const char *path, int operation, LockFile *global, LockFile
         }
 
         if (getenv_bool("SYSTEMD_NSPAWN_LOCK") == 0) {
-                *local = *global = (LockFile) LOCK_FILE_INIT;
+                *ret_local = LOCK_FILE_INIT;
+                if (ret_global)
+                        *ret_global = LOCK_FILE_INIT;
                 return 0;
         }
 
@@ -1342,19 +1348,23 @@ int image_path_lock(const char *path, int operation, LockFile *global, LockFile
                 if (exclusive)
                         return -EBUSY;
 
-                *local = *global = (LockFile) LOCK_FILE_INIT;
+                *ret_local = LOCK_FILE_INIT;
+                if (ret_global)
+                        *ret_global = LOCK_FILE_INIT;
                 return 0;
         }
 
-        if (stat(path, &st) >= 0) {
-                if (S_ISBLK(st.st_mode))
-                        r = asprintf(&p, "/run/systemd/nspawn/locks/block-%u:%u", major(st.st_rdev), minor(st.st_rdev));
-                else if (S_ISDIR(st.st_mode) || S_ISREG(st.st_mode))
-                        r = asprintf(&p, "/run/systemd/nspawn/locks/inode-%lu:%lu", (unsigned long) st.st_dev, (unsigned long) st.st_ino);
-                else
-                        return -ENOTTY;
-                if (r < 0)
-                        return -ENOMEM;
+        if (ret_global) {
+                if (stat(path, &st) >= 0) {
+                        if (S_ISBLK(st.st_mode))
+                                r = asprintf(&p, "/run/systemd/nspawn/locks/block-%u:%u", major(st.st_rdev), minor(st.st_rdev));
+                        else if (S_ISDIR(st.st_mode) || S_ISREG(st.st_mode))
+                                r = asprintf(&p, "/run/systemd/nspawn/locks/inode-%lu:%lu", (unsigned long) st.st_dev, (unsigned long) st.st_ino);
+                        else
+                                return -ENOTTY;
+                        if (r < 0)
+                                return -ENOMEM;
+                }
         }
 
         /* For block devices we don't need the "local" lock, as the major/minor lock above should be
@@ -1372,15 +1382,15 @@ int image_path_lock(const char *path, int operation, LockFile *global, LockFile
         if (p) {
                 make_lock_dir();
 
-                r = make_lock_file(p, operation, global);
+                r = make_lock_file(p, operation, ret_global);
                 if (r < 0) {
                         release_lock_file(&t);
                         return r;
                 }
-        } else
-                *global = (LockFile) LOCK_FILE_INIT;
+        } else if (ret_global)
+                *ret_global = LOCK_FILE_INIT;
 
-        *local = t;
+        *ret_local = t;
         return 0;
 }
 
@@ -1512,7 +1522,10 @@ int image_read_metadata(Image *i, const ImagePolicy *image_policy) {
                 if (r < 0)
                         return r;
 
-                r = dissected_image_acquire_metadata(m, flags);
+                r = dissected_image_acquire_metadata(
+                                m,
+                                /* userns_fd= */ -EBADF,
+                                flags);
                 if (r < 0)
                         return r;
 
index 350c3a22cc0af6b22e77ab02080a5bacbfd6139f..6491cec8866b1610223aae02caadd2bc227cfb0f 100644 (file)
@@ -122,3 +122,5 @@ int image_to_json(const struct Image *i, JsonVariant **ret);
 const char *image_root_to_string(ImageClass c) _const_;
 
 extern const struct hash_ops image_hash_ops;
+
+extern const char* const image_search_path[_IMAGE_CLASS_MAX];
index a0832c98281faffdbd3dbbc9ac24141b86d6f0b4..da3b00260fbbf91a5162b58a723fa506d4d16b83 100644 (file)
@@ -32,6 +32,7 @@
 #include "copy.h"
 #include "cryptsetup-util.h"
 #include "device-nodes.h"
+#include "device-private.h"
 #include "device-util.h"
 #include "devnum-util.h"
 #include "discover-image.h"
@@ -74,6 +75,7 @@
 #include "tmpfile-util.h"
 #include "udev-util.h"
 #include "user-util.h"
+#include "varlink.h"
 #include "xattr-util.h"
 
 /* how many times to wait for the device nodes to appear */
@@ -515,6 +517,38 @@ static void dissected_partition_done(DissectedPartition *p) {
 }
 
 #if HAVE_BLKID
+static int diskseq_should_be_used(
+                const char *whole_devname,
+                uint64_t diskseq,
+                DissectImageFlags flags) {
+
+        int r;
+
+        assert(whole_devname);
+
+        /* No diskseq. We cannot use by-diskseq symlink. */
+        if (diskseq == 0)
+                return false;
+
+        /* Do not use by-diskseq link unless DISSECT_IMAGE_DISKSEQ_DEVNODE flag is explicitly set. */
+        if (!FLAGS_SET(flags, DISSECT_IMAGE_DISKSEQ_DEVNODE))
+                return false;
+
+        _cleanup_(sd_device_unrefp) sd_device *dev = NULL;
+        r = sd_device_new_from_devname(&dev, whole_devname);
+        if (r < 0)
+                return r;
+
+        /* When ID_IGNORE_DISKSEQ udev property is set, the by-diskseq symlink will not be created. */
+        r = device_get_property_bool(dev, "ID_IGNORE_DISKSEQ");
+        if (r >= 0)
+                return !r; /* If explicitly specified, use it. */
+        if (r != -ENOENT)
+                return r;
+
+        return true;
+}
+
 static int make_partition_devname(
                 const char *whole_devname,
                 uint64_t diskseq,
@@ -529,8 +563,10 @@ static int make_partition_devname(
         assert(nr != 0); /* zero is not a valid partition nr */
         assert(ret);
 
-        if (!FLAGS_SET(flags, DISSECT_IMAGE_DISKSEQ_DEVNODE) || diskseq == 0) {
-
+        r = diskseq_should_be_used(whole_devname, diskseq, flags);
+        if (r < 0)
+                log_debug_errno(r, "Failed to determine if diskseq should be used for %s, assuming no, ignoring: %m", whole_devname);
+        if (r <= 0) {
                 /* Given a whole block device node name (e.g. /dev/sda or /dev/loop7) generate a partition
                  * device name (e.g. /dev/sda7 or /dev/loop7p5). The rule the kernel uses is simple: if whole
                  * block device node name ends in a digit, then suffix a 'p', followed by the partition
@@ -1643,6 +1679,20 @@ int dissect_image_file_and_warn(
                         verity);
 }
 
+void dissected_image_close(DissectedImage *m) {
+        if (!m)
+                return;
+
+        /* Closes all fds we keep open associated with this, but nothing else */
+
+        FOREACH_ARRAY(p, m->partitions, _PARTITION_DESIGNATOR_MAX) {
+                p->mount_node_fd = safe_close(p->mount_node_fd);
+                p->fsmount_fd = safe_close(p->fsmount_fd);
+        }
+
+        m->loop = loop_device_unref(m->loop);
+}
+
 DissectedImage* dissected_image_unref(DissectedImage *m) {
         if (!m)
                 return NULL;
@@ -2937,6 +2987,7 @@ int dissected_image_decrypt(
          *      > 0           → Decrypted successfully
          *      -ENOKEY       → There's something to decrypt but no key was supplied
          *      -EKEYREJECTED → Passed key was not correct
+         *      -EBUSY        → Generic Verity error (kernel is not very explanatory)
          */
 
         if (verity && verity->root_hash && verity->root_hash_size < sizeof(sd_id128_t))
@@ -3386,7 +3437,10 @@ int dissected_image_load_verity_sig_partition(
         return 1;
 }
 
-int dissected_image_acquire_metadata(DissectedImage *m, DissectImageFlags extra_flags) {
+int dissected_image_acquire_metadata(
+                DissectedImage *m,
+                int userns_fd,
+                DissectImageFlags extra_flags) {
 
         enum {
                 META_HOSTNAME,
@@ -3445,13 +3499,22 @@ int dissected_image_acquire_metadata(DissectedImage *m, DissectImageFlags extra_
                 goto finish;
         }
 
-        r = safe_fork("(sd-dissect)", FORK_RESET_SIGNALS|FORK_DEATHSIG_SIGTERM|FORK_NEW_MOUNTNS|FORK_MOUNTNS_SLAVE, &child);
+        r = safe_fork("(sd-dissect)", FORK_RESET_SIGNALS|FORK_DEATHSIG_SIGTERM, &child);
         if (r < 0)
                 goto finish;
         if (r == 0) {
-                /* Child in a new mount namespace */
+                /* Child */
                 error_pipe[0] = safe_close(error_pipe[0]);
 
+                if (userns_fd < 0)
+                        r = detach_mount_namespace_harder(0, 0);
+                else
+                        r = detach_mount_namespace_userns(userns_fd);
+                if (r < 0) {
+                        log_debug_errno(r, "Failed to detach mount namespace: %m");
+                        goto inner_fail;
+                }
+
                 r = dissected_image_mount(
                                 m,
                                 t,
@@ -4129,3 +4192,210 @@ int get_common_dissect_directory(char **ret) {
 
         return 0;
 }
+
+#if HAVE_BLKID
+
+static JSON_DISPATCH_ENUM_DEFINE(dispatch_architecture, Architecture, architecture_from_string);
+static JSON_DISPATCH_ENUM_DEFINE(dispatch_partition_designator, PartitionDesignator, partition_designator_from_string);
+
+typedef struct PartitionFields {
+        PartitionDesignator designator;
+        bool rw;
+        bool growfs;
+        unsigned partno;
+        Architecture architecture;
+        sd_id128_t uuid;
+        char *fstype;
+        char *label;
+        uint64_t size;
+        uint64_t offset;
+        unsigned fsmount_fd_idx;
+} PartitionFields;
+
+static void partition_fields_done(PartitionFields *f) {
+        assert(f);
+
+        f->fstype = mfree(f->fstype);
+        f->label = mfree(f->label);
+}
+
+typedef struct ReplyParameters {
+        JsonVariant *partitions;
+        char *image_policy;
+        uint64_t image_size;
+        uint32_t sector_size;
+        sd_id128_t image_uuid;
+} ReplyParameters;
+
+static void reply_parameters_done(ReplyParameters *p) {
+        assert(p);
+
+        p->image_policy = mfree(p->image_policy);
+        p->partitions = json_variant_unref(p->partitions);
+}
+
+#endif
+
+int mountfsd_mount_image(
+                const char *path,
+                int userns_fd,
+                const ImagePolicy *image_policy,
+                DissectImageFlags flags,
+                DissectedImage **ret) {
+
+#if HAVE_BLKID
+        _cleanup_(reply_parameters_done) ReplyParameters p = {};
+
+        static const JsonDispatch dispatch_table[] = {
+                { "partitions",  JSON_VARIANT_ARRAY,         json_dispatch_variant,  offsetof(struct ReplyParameters, partitions),   JSON_MANDATORY },
+                { "imagePolicy", JSON_VARIANT_STRING,        json_dispatch_string,   offsetof(struct ReplyParameters, image_policy), 0              },
+                { "imageSize",   _JSON_VARIANT_TYPE_INVALID, json_dispatch_uint64,   offsetof(struct ReplyParameters, image_size),   JSON_MANDATORY },
+                { "sectorSize",  _JSON_VARIANT_TYPE_INVALID, json_dispatch_uint32,   offsetof(struct ReplyParameters, sector_size),  JSON_MANDATORY },
+                { "imageUuid",   JSON_VARIANT_STRING,        json_dispatch_id128,    offsetof(struct ReplyParameters, image_uuid),   0              },
+                {}
+        };
+
+        _cleanup_(dissected_image_unrefp) DissectedImage *di = NULL;
+        _cleanup_close_ int image_fd = -EBADF;
+        _cleanup_(varlink_unrefp) Varlink *vl = NULL;
+        _cleanup_free_ char *ps = NULL;
+        unsigned max_fd = UINT_MAX;
+        const char *error_id;
+        int r;
+
+        assert(path);
+        assert(ret);
+
+        r = varlink_connect_address(&vl, "/run/systemd/io.systemd.MountFileSystem");
+        if (r < 0)
+                return log_error_errno(r, "Failed to connect to mountfsd: %m");
+
+        r = varlink_set_allow_fd_passing_input(vl, true);
+        if (r < 0)
+                return log_error_errno(r, "Failed to enable varlink fd passing for read: %m");
+
+        r = varlink_set_allow_fd_passing_output(vl, true);
+        if (r < 0)
+                return log_error_errno(r, "Failed to enable varlink fd passing for write: %m");
+
+        image_fd = open(path, O_RDONLY|O_CLOEXEC);
+        if (image_fd < 0)
+                return log_error_errno(errno, "Failed to open '%s': %m", path);
+
+        r = varlink_push_dup_fd(vl, image_fd);
+        if (r < 0)
+                return log_error_errno(r, "Failed to push image fd into varlink connection: %m");
+
+        if (userns_fd >= 0) {
+                r = varlink_push_dup_fd(vl, userns_fd);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to push image fd into varlink connection: %m");
+        }
+
+        if (image_policy) {
+                r = image_policy_to_string(image_policy, /* simplify= */ false, &ps);
+                if (r < 0)
+                        return log_error_errno(r, "Failed format image policy to string: %m");
+        }
+
+        JsonVariant *reply = NULL;
+        r = varlink_callb(
+                        vl,
+                        "io.systemd.MountFileSystem.MountImage",
+                        &reply,
+                        &error_id,
+                        JSON_BUILD_OBJECT(
+                                        JSON_BUILD_PAIR("imageFileDescriptor", JSON_BUILD_UNSIGNED(0)),
+                                        JSON_BUILD_PAIR_CONDITION(userns_fd >= 0, "userNamespaceFileDescriptor", JSON_BUILD_UNSIGNED(1)),
+                                        JSON_BUILD_PAIR("readOnly", JSON_BUILD_BOOLEAN(FLAGS_SET(flags, DISSECT_IMAGE_MOUNT_READ_ONLY))),
+                                        JSON_BUILD_PAIR("growFileSystems", JSON_BUILD_BOOLEAN(FLAGS_SET(flags, DISSECT_IMAGE_GROWFS))),
+                                        JSON_BUILD_PAIR_CONDITION(ps, "imagePolicy", JSON_BUILD_STRING(ps)),
+                                        JSON_BUILD_PAIR("allowInteractiveAuthentication", JSON_BUILD_BOOLEAN(FLAGS_SET(flags, DISSECT_IMAGE_ALLOW_INTERACTIVE_AUTH)))));
+        if (r < 0)
+                return log_error_errno(r, "Failed to call MountImage() varlink call: %m");
+        if (!isempty(error_id))
+                return log_error_errno(varlink_error_to_errno(error_id, reply), "Failed to call MountImage() varlink call: %s", error_id);
+
+        r = json_dispatch(reply, dispatch_table, JSON_ALLOW_EXTENSIONS, &p);
+        if (r < 0)
+                return log_error_errno(r, "Failed to parse MountImage() reply: %m");
+
+        log_debug("Effective image policy: %s", p.image_policy);
+
+        JsonVariant *i;
+        JSON_VARIANT_ARRAY_FOREACH(i, p.partitions) {
+                _cleanup_close_ int fsmount_fd = -EBADF;
+
+                _cleanup_(partition_fields_done) PartitionFields pp = {
+                        .designator = _PARTITION_DESIGNATOR_INVALID,
+                        .architecture = _ARCHITECTURE_INVALID,
+                        .size = UINT64_MAX,
+                        .offset = UINT64_MAX,
+                        .fsmount_fd_idx = UINT_MAX,
+                };
+
+                static const JsonDispatch partition_dispatch_table[] = {
+                        { "designator",          JSON_VARIANT_STRING,        dispatch_partition_designator, offsetof(struct PartitionFields, designator),       JSON_MANDATORY },
+                        { "writable",            JSON_VARIANT_BOOLEAN,       json_dispatch_boolean,         offsetof(struct PartitionFields, rw),               JSON_MANDATORY },
+                        { "growFileSystem",      JSON_VARIANT_BOOLEAN,       json_dispatch_boolean,         offsetof(struct PartitionFields, growfs),           JSON_MANDATORY },
+                        { "partitionNumber",     _JSON_VARIANT_TYPE_INVALID, json_dispatch_uint,            offsetof(struct PartitionFields, partno),           0              },
+                        { "architecture",        JSON_VARIANT_STRING,        dispatch_architecture,         offsetof(struct PartitionFields, architecture),     0              },
+                        { "partitionUuid",       JSON_VARIANT_STRING,        json_dispatch_id128,           offsetof(struct PartitionFields, uuid),             0              },
+                        { "fileSystemType",      JSON_VARIANT_STRING,        json_dispatch_string,          offsetof(struct PartitionFields, fstype),           JSON_MANDATORY },
+                        { "partitionLabel",      JSON_VARIANT_STRING,        json_dispatch_string,          offsetof(struct PartitionFields, label),            0              },
+                        { "size",                _JSON_VARIANT_TYPE_INVALID, json_dispatch_uint64,          offsetof(struct PartitionFields, size),             JSON_MANDATORY },
+                        { "offset",              _JSON_VARIANT_TYPE_INVALID, json_dispatch_uint64,          offsetof(struct PartitionFields, offset),           JSON_MANDATORY },
+                        { "mountFileDescriptor", _JSON_VARIANT_TYPE_INVALID, json_dispatch_uint,            offsetof(struct PartitionFields, fsmount_fd_idx),   JSON_MANDATORY },
+                        {}
+                };
+
+                r = json_dispatch(i, partition_dispatch_table, JSON_ALLOW_EXTENSIONS, &pp);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to parse partition data: %m");
+
+                if (pp.fsmount_fd_idx != UINT_MAX) {
+                        if (max_fd == UINT_MAX || pp.fsmount_fd_idx > max_fd)
+                                max_fd = pp.fsmount_fd_idx;
+
+                        fsmount_fd = varlink_take_fd(vl, pp.fsmount_fd_idx);
+                        if (fsmount_fd < 0)
+                                return fsmount_fd;
+                }
+
+                assert(pp.designator >= 0);
+
+                if (!di) {
+                        r = dissected_image_new(path, &di);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to allocated new dissected image structure: %m");
+                }
+
+                if (di->partitions[pp.designator].found)
+                        return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Duplicate partition data for '%s'.", partition_designator_to_string(pp.designator));
+
+                di->partitions[pp.designator] = (DissectedPartition) {
+                        .found = true,
+                        .rw = pp.rw,
+                        .growfs = pp.growfs,
+                        .partno = pp.partno,
+                        .architecture = pp.architecture,
+                        .uuid = pp.uuid,
+                        .fstype = TAKE_PTR(pp.fstype),
+                        .label = TAKE_PTR(pp.label),
+                        .mount_node_fd = -EBADF,
+                        .size = pp.size,
+                        .offset = pp.offset,
+                        .fsmount_fd = TAKE_FD(fsmount_fd),
+                };
+        }
+
+        di->image_size = p.image_size;
+        di->sector_size = p.sector_size;
+        di->image_uuid = p.image_uuid;
+
+        *ret = TAKE_PTR(di);
+        return 0;
+#else
+        return -EOPNOTSUPP;
+#endif
+}
index 559d5b861d9288eef6397fed4dddacb2a3d0cad2..e31fd54f844869266a250489071a2247423f914e 100644 (file)
@@ -88,6 +88,7 @@ typedef enum DissectImageFlags {
         DISSECT_IMAGE_ALLOW_EMPTY               = 1 << 24, /* Allow that no usable partitions is present */
         DISSECT_IMAGE_TRY_ATOMIC_MOUNT_EXCHANGE = 1 << 25, /* Try to mount the image beneath the specified mountpoint, rather than on top of it, and then umount the top */
         DISSECT_IMAGE_ALLOW_USERSPACE_VERITY    = 1 << 26, /* Allow userspace verity keyring in /etc/verity.d/ and related dirs */
+        DISSECT_IMAGE_ALLOW_INTERACTIVE_AUTH    = 1 << 27, /* Allow interactive authorization when going through mountfsd */
 } DissectImageFlags;
 
 struct DissectedImage {
@@ -163,6 +164,7 @@ int dissect_image_file_and_warn(const char *path, const VeritySettings *verity,
 int dissect_loop_device(LoopDevice *loop, const VeritySettings *verity, const MountOptions *mount_options, const ImagePolicy *image_policy, DissectImageFlags flags, DissectedImage **ret);
 int dissect_loop_device_and_warn(LoopDevice *loop, const VeritySettings *verity, const MountOptions *mount_options, const ImagePolicy *image_policy, DissectImageFlags flags, DissectedImage **ret);
 
+void dissected_image_close(DissectedImage *m);
 DissectedImage* dissected_image_unref(DissectedImage *m);
 DEFINE_TRIVIAL_CLEANUP_FUNC(DissectedImage*, dissected_image_unref);
 
@@ -171,7 +173,7 @@ int dissected_image_decrypt_interactively(DissectedImage *m, const char *passphr
 int dissected_image_mount(DissectedImage *m, const char *dest, uid_t uid_shift, uid_t uid_range, int userns_fd, DissectImageFlags flags);
 int dissected_image_mount_and_warn(DissectedImage *m, const char *where, uid_t uid_shift, uid_t uid_range, int userns_fd, DissectImageFlags flags);
 
-int dissected_image_acquire_metadata(DissectedImage *m, DissectImageFlags extra_flags);
+int dissected_image_acquire_metadata(DissectedImage *m, int userns_fd, DissectImageFlags extra_flags);
 
 Architecture dissected_image_architecture(DissectedImage *m);
 
@@ -198,6 +200,14 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(DecryptedImage*, decrypted_image_unref);
 int dissected_image_relinquish(DissectedImage *m);
 
 int verity_settings_load(VeritySettings *verity, const char *image, const char *root_hash_path, const char *root_hash_sig_path);
+
+static inline bool verity_settings_set(const VeritySettings *settings) {
+        return settings &&
+                (settings->root_hash_size > 0 ||
+                 (settings->root_hash_sig_size > 0 ||
+                  settings->data_path));
+}
+
 void verity_settings_done(VeritySettings *verity);
 
 static inline bool verity_settings_data_covers(const VeritySettings *verity, PartitionDesignator d) {
@@ -232,3 +242,5 @@ static inline const char *dissected_partition_fstype(const DissectedPartition *m
 }
 
 int get_common_dissect_directory(char **ret);
+
+int mountfsd_mount_image(const char *path, int userns_fd, const ImagePolicy *image_policy, DissectImageFlags flags, DissectedImage **ret);
index b70b74074bf203df04b54eb0ec6e8b5d03dc9682..044d2d074474c91891e4458e075c19477908a4aa 100644 (file)
@@ -1,19 +1,12 @@
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 
-/* Temporary work-around for broken glibc vs. linux kernel header definitions
- * This is already fixed upstream, remove this when distributions have updated.
- */
-#define _NET_IF_H 1
-
+/* Make sure the net/if.h header is included before any linux/ one */
+#include <net/if.h>
 #include <arpa/inet.h>
 #include <endian.h>
 #include <errno.h>
 #include <stddef.h>
 #include <string.h>
-#include <net/if.h>
-#ifndef IFNAMSIZ
-#define IFNAMSIZ 16
-#endif
 #include <linux/if.h>
 #include <linux/netfilter_ipv4/ip_tables.h>
 #include <linux/netfilter/nf_nat.h>
index 3547476c85141522c0fc130d0ed55f7306caa0dc..cb26a9ab272ec798e718b974a1e165e0a3bcc928 100644 (file)
@@ -23,6 +23,7 @@
 #include "log.h"
 #include "parse-util.h"
 #include "path-util.h"
+#include "proc-cmdline.h"
 #include "stat-util.h"
 #include "string-util.h"
 #include "strv.h"
@@ -129,6 +130,13 @@ static int read_resume_config(dev_t *ret_devno, uint64_t *ret_offset) {
         assert(ret_devno);
         assert(ret_offset);
 
+        r = proc_cmdline_get_key("noresume", /* flags = */ 0, /* ret_value = */ NULL);
+        if (r < 0)
+                return log_debug_errno(r, "Failed to check if 'noresume' kernel command line option is set: %m");
+        if (r > 0)
+                return log_debug_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
+                                       "'noresume' kernel command line option is set, refusing hibernation device lookup.");
+
         r = read_one_line_file("/sys/power/resume", &devno_str);
         if (r < 0)
                 return log_debug_errno(r, "Failed to read /sys/power/resume: %m");
@@ -513,13 +521,17 @@ int write_resume_config(dev_t devno, uint64_t offset, const char *device) {
         return 0;
 }
 
-void clear_efi_hibernate_location_and_warn(void) {
+int clear_efi_hibernate_location_and_warn(void) {
         int r;
 
         if (!is_efi_boot())
-                return;
+                return 0;
 
         r = efi_set_variable(EFI_SYSTEMD_VARIABLE(HibernateLocation), NULL, 0);
+        if (r == -ENOENT)
+                return 0;
         if (r < 0)
-                log_warning_errno(r, "Failed to clear EFI variable HibernateLocation, ignoring: %m");
+                return log_warning_errno(r, "Failed to clear HibernateLocation EFI variable: %m");
+
+        return 1;
 }
index 1064d2b4dd4ac30716053a8cd7cdafbc24760b4f..394d0b41766b42bd730578ecad84e03cbea81a48 100644 (file)
@@ -22,7 +22,7 @@ int hibernation_is_safe(void);
 
 int write_resume_config(dev_t devno, uint64_t offset, const char *device);
 
-void clear_efi_hibernate_location_and_warn(void);
+int clear_efi_hibernate_location_and_warn(void);
 
 /* Only for test-fiemap */
 int read_fiemap(int fd, struct fiemap **ret);
index 70f039f82525d33d1920551929d790f9857373c2..6b26f2064a1bd20a494855e2a0e04823428e4e57 100644 (file)
@@ -50,7 +50,10 @@ int dlopen_idn(void) {
                 if (!dl)
                         return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
                                                "libidn support is not installed: %s", dlerror());
-        }
+                log_debug("Loaded 'libidn.so.11' via dlopen()");
+        } else
+                log_debug("Loaded 'libidn.so.12' via dlopen()");
+
 
         r = dlsym_many_or_warn(
                         dl,
index 47ca62c7313446193df09fad2bf4f846b8749cb2..e7bd84e144164f28a1d0a5c323fc8c4bc562f23d 100644 (file)
@@ -50,6 +50,20 @@ PartitionPolicyFlags partition_policy_flags_extend(PartitionPolicyFlags flags) {
         return flags;
 }
 
+PartitionPolicyFlags partition_policy_flags_reduce(PartitionPolicyFlags flags) {
+        /* The reverse of partition_policy_flags_extend(): if some parts of the flags field allow all
+         * possible options, let's remove it from the flags to make them shorter */
+
+        if (FLAGS_SET(flags, _PARTITION_POLICY_USE_MASK))
+                flags &= ~_PARTITION_POLICY_USE_MASK;
+        if (FLAGS_SET(flags, _PARTITION_POLICY_READ_ONLY_MASK))
+                flags &= ~_PARTITION_POLICY_READ_ONLY_MASK;
+        if (FLAGS_SET(flags, _PARTITION_POLICY_GROWFS_MASK))
+                flags &= ~_PARTITION_POLICY_GROWFS_MASK;
+
+        return flags;
+}
+
 static PartitionPolicyFlags partition_policy_normalized_flags(const PartitionPolicy *policy) {
         PartitionPolicyFlags flags = ASSERT_PTR(policy)->flags;
 
@@ -676,6 +690,90 @@ int parse_image_policy_argument(const char *s, ImagePolicy **policy) {
         return free_and_replace_full(*policy, np, image_policy_free);
 }
 
+static bool partition_policy_flags_has_unspecified(PartitionPolicyFlags flags) {
+
+        if ((flags & _PARTITION_POLICY_USE_MASK) == 0)
+                return true;
+        if ((flags & _PARTITION_POLICY_READ_ONLY_MASK) == 0)
+                return true;
+        if ((flags & _PARTITION_POLICY_GROWFS_MASK) == 0)
+                return true;
+
+        return false;
+}
+
+int image_policy_intersect(const ImagePolicy *a, const ImagePolicy *b, ImagePolicy **ret) {
+        _cleanup_(image_policy_freep) ImagePolicy *p = NULL;
+
+        /* Calculates the intersection of the specified policies, i.e. only what is permitted in both. This
+         * might fail with -ENAVAIL if the intersection is an "impossible policy". For example, if a root
+         * partition my neither be used, nor be absent, nor be unused then this is considered
+         * "impossible".  */
+
+        p = image_policy_new(_PARTITION_DESIGNATOR_MAX);
+        if (!p)
+                return -ENOMEM;
+
+        p->default_flags =
+                partition_policy_flags_extend(image_policy_default(a)) &
+                partition_policy_flags_extend(image_policy_default(b));
+
+        if (partition_policy_flags_has_unspecified(p->default_flags)) /* Intersection empty? */
+                return -ENAVAIL;
+
+        p->default_flags = partition_policy_flags_reduce(p->default_flags);
+
+        for (PartitionDesignator d = 0; d < _PARTITION_DESIGNATOR_MAX; d++) {
+                PartitionPolicyFlags x, y, z, df;
+
+                /* If this designator has no entry in either policy we don't need to include it in the intersection either. */
+                if (!image_policy_bsearch(a, d) && !image_policy_bsearch(b, d))
+                        continue;
+
+                /* Expand this policy flags field to the "long" form, i.e. for each part of the flags that
+                 * are left unspcified add in all possible options */
+                x = image_policy_get_exhaustively(a, d);
+                if (x < 0)
+                        return x;
+
+                y = image_policy_get_exhaustively(b, d);
+                if (y < 0)
+                        return y;
+
+                /* Mask it */
+                z = x & y;
+
+                /* Check if the intersection is empty for this partition. If so, generate a clear error */
+                if (partition_policy_flags_has_unspecified(z))
+                        return -ENAVAIL;
+
+                df = partition_policy_normalized_flags(
+                                &(const PartitionPolicy) {
+                                        .flags = image_policy_default(p),
+                                        .designator = d,
+                                });
+                if (df < 0)
+                        return df;
+                if (df == z) /* Same as default? then let's skip this */
+                        continue;
+
+                /* image_policy_get_exhaustively() may have extended the flags mask to include all
+                 * read-only/growfs flags if not set. Let's remove them again, if they are both set to
+                 * minimize the policy again. */
+                z = partition_policy_flags_reduce(z);
+
+                p->policies[p->n_policies++] = (struct PartitionPolicy) {
+                        .designator = d,
+                        .flags = z,
+                };
+        }
+
+        if (ret)
+                *ret = TAKE_PTR(p);
+
+        return 0;
+}
+
 const ImagePolicy image_policy_allow = {
         /* Allow policy */
         .n_policies = 0,
index c2a0a5ac40f7957d947f07445293c7c46a75a314..a1a6afa345b861cce582af5a13206987c0c5f465 100644 (file)
@@ -80,6 +80,7 @@ static inline size_t image_policy_n_entries(const ImagePolicy *policy) {
 }
 
 PartitionPolicyFlags partition_policy_flags_extend(PartitionPolicyFlags flags);
+PartitionPolicyFlags partition_policy_flags_reduce(PartitionPolicyFlags flags);
 
 PartitionPolicyFlags partition_policy_flags_from_string(const char *s);
 int partition_policy_flags_to_string(PartitionPolicyFlags flags, bool simplify, char **ret);
@@ -95,6 +96,8 @@ bool image_policy_equiv_deny(const ImagePolicy *policy);
 bool image_policy_equal(const ImagePolicy *a, const ImagePolicy *b);       /* checks if defined the same way, i.e. has literally the same ruleset */
 int image_policy_equivalent(const ImagePolicy *a, const ImagePolicy *b);   /* checks if the outcome is the same, i.e. for all partitions results in the same decisions. */
 
+int image_policy_intersect(const ImagePolicy *a, const ImagePolicy *b, ImagePolicy **ret);
+
 static inline ImagePolicy* image_policy_free(ImagePolicy *p) {
         return mfree(p);
 }
index da9783c09d42f08fc6ad81d8662e26be397e007a..c6a105080592a745afe108f9bb887267a91e4b2e 100644 (file)
@@ -1,14 +1,6 @@
 /* SPDX-License-Identifier: LGPL-2.0-or-later */
 /*
- * initreq.h   Interface to talk to init through /dev/initctl.
- *
- *             Copyright (C) 1995-2004 Miquel van Smoorenburg
- *
- *             This library 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 of the License, or (at your option) any later version.
- *
+ * Copyright (C) 1995-2004 Miquel van Smoorenburg
  * Version:     @(#)initreq.h  1.28  31-Mar-2004 MvS
  */
 
index 917b7732665ff0635040ffd8ea230304ba9ebefa..a08736480effb6be4f1e406d394888230bff48ba 100644 (file)
@@ -116,18 +116,18 @@ static bool ignore_proc(const PidRef *pid, bool warn_rootfs) {
         return true;
 }
 
-static void log_children_no_yet_killed(Set *pids) {
+static void log_children_not_yet_killed(Set *pids) {
         _cleanup_free_ char *lst_child = NULL;
-        void *p;
         int r;
 
+        void *p;
         SET_FOREACH(p, pids) {
                 _cleanup_free_ char *s = NULL;
 
                 if (pid_get_comm(PTR_TO_PID(p), &s) >= 0)
-                        r = strextendf(&lst_child, ", " PID_FMT " (%s)", PTR_TO_PID(p), s);
+                        r = strextendf_with_separator(&lst_child, ", ", PID_FMT " (%s)", PTR_TO_PID(p), s);
                 else
-                        r = strextendf(&lst_child, ", " PID_FMT, PTR_TO_PID(p));
+                        r = strextendf_with_separator(&lst_child, ", ", PID_FMT, PTR_TO_PID(p));
                 if (r < 0)
                         return (void) log_oom_warning();
         }
@@ -135,7 +135,7 @@ static void log_children_no_yet_killed(Set *pids) {
         if (isempty(lst_child))
                 return;
 
-        log_warning("Waiting for process: %s", lst_child + 2);
+        log_warning("Waiting for process: %s", lst_child);
 }
 
 static int wait_for_children(Set *pids, sigset_t *mask, usec_t timeout) {
@@ -200,7 +200,7 @@ static int wait_for_children(Set *pids, sigset_t *mask, usec_t timeout) {
 
                 n = now(CLOCK_MONOTONIC);
                 if (date_log_child > 0 && n >= date_log_child) {
-                        log_children_no_yet_killed(pids);
+                        log_children_not_yet_killed(pids);
                         /* Log the children not yet killed only once */
                         date_log_child = 0;
                 }
@@ -234,14 +234,14 @@ static int killall(int sig, Set *pids, bool send_sighup) {
 
         r = proc_dir_open(&dir);
         if (r < 0)
-                return log_warning_errno(r, "opendir(/proc) failed: %m");
+                return log_warning_errno(r, "Failed to open /proc/: %m");
 
         for (;;) {
                 _cleanup_(pidref_done) PidRef pidref = PIDREF_NULL;
 
                 r = proc_dir_read_pidref(dir, &pidref);
                 if (r < 0)
-                        return log_warning_errno(r, "Failed to enumerate /proc: %m");
+                        return log_warning_errno(r, "Failed to enumerate /proc/: %m");
                 if (r == 0)
                         break;
 
index 15b7e42c8480b0750a60c59e8d4cfa8614e1f39e..1cc81a6c6101b557f483151fb8f401d89a8c0257 100644 (file)
@@ -40,6 +40,7 @@ DLSYM_FUNCTION(fido_cred_id_ptr);
 DLSYM_FUNCTION(fido_cred_new);
 DLSYM_FUNCTION(fido_cred_set_clientdata_hash);
 DLSYM_FUNCTION(fido_cred_set_extensions);
+DLSYM_FUNCTION(fido_cred_set_prot);
 DLSYM_FUNCTION(fido_cred_set_rk);
 DLSYM_FUNCTION(fido_cred_set_rp);
 DLSYM_FUNCTION(fido_cred_set_type);
@@ -97,6 +98,7 @@ int dlopen_libfido2(void) {
                         DLSYM_ARG(fido_cred_new),
                         DLSYM_ARG(fido_cred_set_clientdata_hash),
                         DLSYM_ARG(fido_cred_set_extensions),
+                        DLSYM_ARG(fido_cred_set_prot),
                         DLSYM_ARG(fido_cred_set_rk),
                         DLSYM_ARG(fido_cred_set_rp),
                         DLSYM_ARG(fido_cred_set_type),
@@ -776,10 +778,21 @@ int fido2_generate_hmac_hash(
         if (!c)
                 return log_oom();
 
-        r = sym_fido_cred_set_extensions(c, FIDO_EXT_HMAC_SECRET);
+        int extensions = FIDO_EXT_HMAC_SECRET;
+        if (FLAGS_SET(lock_with, FIDO2ENROLL_PIN) || FLAGS_SET(lock_with, FIDO2ENROLL_UV)) {
+                /* Attempt to use the "cred protect" extension, requiring user verification (UV) for this
+                 * credential. If the authenticator doesn't support the extension, it will be ignored. */
+                extensions |= FIDO_EXT_CRED_PROTECT;
+
+                r = sym_fido_cred_set_prot(c, FIDO_CRED_PROT_UV_REQUIRED);
+                if (r != FIDO_OK)
+                        log_warning("Failed to set protection level on FIDO2 credential, ignoring: %s", sym_fido_strerr(r));
+        }
+
+        r = sym_fido_cred_set_extensions(c, extensions);
         if (r != FIDO_OK)
                 return log_error_errno(SYNTHETIC_ERRNO(EIO),
-                                       "Failed to enable HMAC-SECRET extension on FIDO2 credential: %s", sym_fido_strerr(r));
+                                       "Failed to enable extensions on FIDO2 credential: %s", sym_fido_strerr(r));
 
         r = sym_fido_cred_set_rp(c, rp_id, rp_name);
         if (r != FIDO_OK)
@@ -830,7 +843,17 @@ int fido2_generate_hmac_hash(
                            emoji_enabled() ? special_glyph(SPECIAL_GLYPH_TOUCH) : "",
                            emoji_enabled() ? " " : "");
 
-        r = sym_fido_dev_make_cred(d, c, NULL);
+        /* If we are using the user PIN, then we must pass that PIN to the get_assertion call below, or
+         * the authenticator will use the non-user-verification HMAC secret (which differs from the one when
+         * the PIN is passed).
+         *
+         * Rather than potentially trying and failing to create the credential, just collect the PIN first
+         * and then pass it to both the make_credential and the get_assertion operations. */
+        if (FLAGS_SET(lock_with, FIDO2ENROLL_PIN))
+                r = FIDO_ERR_PIN_REQUIRED;
+        else
+                r = sym_fido_dev_make_cred(d, c, NULL);
+
         if (r == FIDO_ERR_PIN_REQUIRED) {
 
                 if (!has_client_pin)
index 198408fdaf33ba77b1ba467f704212deb99bcc63..5e40b2aafc47fb97a004faf58d297308a094e0eb 100644 (file)
@@ -43,6 +43,7 @@ DLSYM_PROTOTYPE(fido_cred_id_ptr);
 DLSYM_PROTOTYPE(fido_cred_new);
 DLSYM_PROTOTYPE(fido_cred_set_clientdata_hash);
 DLSYM_PROTOTYPE(fido_cred_set_extensions);
+DLSYM_PROTOTYPE(fido_cred_set_prot);
 DLSYM_PROTOTYPE(fido_cred_set_rk);
 DLSYM_PROTOTYPE(fido_cred_set_rp);
 DLSYM_PROTOTYPE(fido_cred_set_type);
index 85dd3db278958054f653127e59fbd7d341e08108..e63be5002c8f066f4fc3854f0cbd5956dc5234bf 100644 (file)
@@ -5,7 +5,6 @@
 #include <signal.h>
 #include <stdint.h>
 #include <stdlib.h>
-#include <sys/socket.h>
 #include <syslog.h>
 #include <unistd.h>
 
 #include "log.h"
 #include "logs-show.h"
 #include "macro.h"
-#include "namespace-util.h"
 #include "output-mode.h"
 #include "parse-util.h"
 #include "pretty-print.h"
-#include "process-util.h"
 #include "sparse-endian.h"
 #include "stdio-util.h"
 #include "string-table.h"
@@ -1573,18 +1570,18 @@ int add_matches_for_unit(sd_journal *j, const char *unit) {
 
             /* Look for coredumps of the service */
             (r = sd_journal_add_disjunction(j)) ||
-            (r = sd_journal_add_match(j, "MESSAGE_ID=fc2e22bc6ee647b6b90729ab34a250b1", 0)) ||
-            (r = sd_journal_add_match(j, "_UID=0", 0)) ||
+            (r = sd_journal_add_match(j, "MESSAGE_ID=fc2e22bc6ee647b6b90729ab34a250b1", SIZE_MAX)) ||
+            (r = sd_journal_add_match(j, "_UID=0", SIZE_MAX)) ||
             (r = journal_add_match_pair(j, "COREDUMP_UNIT", unit)) ||
 
              /* Look for messages from PID 1 about this service */
             (r = sd_journal_add_disjunction(j)) ||
-            (r = sd_journal_add_match(j, "_PID=1", 0)) ||
+            (r = sd_journal_add_match(j, "_PID=1", SIZE_MAX)) ||
             (r = journal_add_match_pair(j, "UNIT", unit)) ||
 
             /* Look for messages from authorized daemons about this service */
             (r = sd_journal_add_disjunction(j)) ||
-            (r = sd_journal_add_match(j, "_UID=0", 0)) ||
+            (r = sd_journal_add_match(j, "_UID=0", SIZE_MAX)) ||
             (r = journal_add_match_pair(j, "OBJECT_SYSTEMD_UNIT", unit))
         );
 
@@ -1618,13 +1615,13 @@ int add_matches_for_user_unit(sd_journal *j, const char *unit, uid_t uid) {
                 (r = sd_journal_add_disjunction(j)) ||
                 (r = journal_add_match_pair(j, "COREDUMP_USER_UNIT", unit)) ||
                 (r = journal_add_matchf(j, "_UID="UID_FMT, uid)) ||
-                (r = sd_journal_add_match(j, "_UID=0", 0)) ||
+                (r = sd_journal_add_match(j, "_UID=0", SIZE_MAX)) ||
 
                 /* Look for messages from authorized daemons about this service */
                 (r = sd_journal_add_disjunction(j)) ||
                 (r = journal_add_match_pair(j, "OBJECT_SYSTEMD_USER_UNIT", unit)) ||
                 (r = journal_add_matchf(j, "_UID="UID_FMT, uid)) ||
-                (r = sd_journal_add_match(j, "_UID=0", 0))
+                (r = sd_journal_add_match(j, "_UID=0", SIZE_MAX))
         );
 
         if (r == 0 && endswith(unit, ".slice"))
@@ -1638,99 +1635,38 @@ int add_matches_for_user_unit(sd_journal *j, const char *unit, uid_t uid) {
         return r;
 }
 
-static int get_boot_id_for_machine(const char *machine, sd_id128_t *boot_id) {
-        _cleanup_close_pair_ int pair[2] = EBADF_PAIR;
-        _cleanup_close_ int pidnsfd = -EBADF, mntnsfd = -EBADF, rootfd = -EBADF;
-        char buf[SD_ID128_UUID_STRING_MAX];
-        pid_t pid, child;
-        ssize_t k;
+int add_match_boot_id(sd_journal *j, sd_id128_t id) {
         int r;
 
-        assert(machine);
-        assert(boot_id);
-
-        r = container_get_leader(machine, &pid);
-        if (r < 0)
-                return r;
-
-        r = namespace_open(pid, &pidnsfd, &mntnsfd, /* ret_netns_fd = */ NULL, /* ret_userns_fd = */ NULL, &rootfd);
-        if (r < 0)
-                return r;
-
-        if (socketpair(AF_UNIX, SOCK_DGRAM, 0, pair) < 0)
-                return -errno;
-
-        r = namespace_fork("(sd-bootidns)", "(sd-bootid)", NULL, 0, FORK_RESET_SIGNALS|FORK_DEATHSIG_SIGKILL,
-                           pidnsfd, mntnsfd, -1, -1, rootfd, &child);
-        if (r < 0)
-                return r;
-        if (r == 0) {
-                int fd;
-
-                pair[0] = safe_close(pair[0]);
-
-                fd = open("/proc/sys/kernel/random/boot_id", O_RDONLY|O_CLOEXEC|O_NOCTTY);
-                if (fd < 0)
-                        _exit(EXIT_FAILURE);
+        assert(j);
 
-                r = loop_read_exact(fd, buf, 36, false);
-                safe_close(fd);
+        if (sd_id128_is_null(id)) {
+                r = sd_id128_get_boot(&id);
                 if (r < 0)
-                        _exit(EXIT_FAILURE);
-
-                k = send(pair[1], buf, 36, MSG_NOSIGNAL);
-                if (k != 36)
-                        _exit(EXIT_FAILURE);
-
-                _exit(EXIT_SUCCESS);
+                        return log_error_errno(r, "Failed to get boot ID: %m");
         }
 
-        pair[1] = safe_close(pair[1]);
-
-        r = wait_for_terminate_and_check("(sd-bootidns)", child, 0);
-        if (r < 0)
-                return r;
-        if (r != EXIT_SUCCESS)
-                return -EIO;
-
-        k = recv(pair[0], buf, 36, 0);
-        if (k != 36)
-                return -EIO;
-
-        buf[36] = 0;
-        r = sd_id128_from_string(buf, boot_id);
+        r = journal_add_match_pair(j, "_BOOT_ID", SD_ID128_TO_STRING(id));
         if (r < 0)
-                return r;
+                return log_error_errno(r, "Failed to add match: %m");
 
         return 0;
 }
 
-int add_match_boot_id(sd_journal *j, sd_id128_t id) {
-        assert(j);
-        assert(!sd_id128_is_null(id));
-
-        return journal_add_match_pair(j, "_BOOT_ID", SD_ID128_TO_STRING(id));
-}
-
 int add_match_this_boot(sd_journal *j, const char *machine) {
         sd_id128_t boot_id;
         int r;
 
         assert(j);
 
-        if (machine) {
-                r = get_boot_id_for_machine(machine, &boot_id);
-                if (r < 0)
-                        return log_error_errno(r, "Failed to get boot id of container %s: %m", machine);
-        } else {
-                r = sd_id128_get_boot(&boot_id);
-                if (r < 0)
-                        return log_error_errno(r, "Failed to get boot id: %m");
-        }
+        r = id128_get_boot_for_machine(machine, &boot_id);
+        if (r < 0)
+                return log_error_errno(r, "Failed to get boot ID%s%s: %m",
+                                       isempty(machine) ? "" : " of container ", machine);
 
         r = add_match_boot_id(j, boot_id);
         if (r < 0)
-                return log_error_errno(r, "Failed to add match: %m");
+                return r;
 
         r = sd_journal_add_conjunction(j);
         if (r < 0)
index 11f2b5ade91b89d1a4c13f98dd1ebe10a07f1e0a..17313aefedc4cc939605ea88e7db44cf29aafd7d 100644 (file)
@@ -118,6 +118,7 @@ shared_sources = files(
         'macvlan-util.c',
         'mkdir-label.c',
         'mkfs-util.c',
+        'module-util.c',
         'mount-setup.c',
         'mount-util.c',
         'net-condition.c',
@@ -125,6 +126,7 @@ shared_sources = files(
         'netif-sriov.c',
         'netif-util.c',
         'nsflags.c',
+        'nsresource.c',
         'numa-util.c',
         'open-file.c',
         'openssl-util.c',
@@ -179,6 +181,8 @@ shared_sources = files(
         'varlink-io.systemd.Hostname.c',
         'varlink-io.systemd.Journal.c',
         'varlink-io.systemd.ManagedOOM.c',
+        'varlink-io.systemd.MountFileSystem.c',
+        'varlink-io.systemd.NamespaceResource.c',
         'varlink-io.systemd.Network.c',
         'varlink-io.systemd.PCRExtend.c',
         'varlink-io.systemd.PCRLock.c',
@@ -236,10 +240,6 @@ if conf.get('HAVE_LIBBPF') == 1
         shared_sources += files('bpf-link.c')
 endif
 
-if conf.get('HAVE_KMOD') == 1
-        shared_sources += files('module-util.c')
-endif
-
 if conf.get('HAVE_PAM') == 1
         shared_sources += files('pam-util.c')
 endif
@@ -324,7 +324,7 @@ libshared_deps = [threads,
                   libdl,
                   libgcrypt,
                   libiptc_cflags,
-                  libkmod,
+                  libkmod_cflags,
                   liblz4_cflags,
                   libmount,
                   libopenssl,
@@ -358,7 +358,6 @@ libshared = shared_library(
         link_depends : libshared_sym_path,
         link_whole : [libshared_static,
                       libbasic,
-                      libbasic_gcrypt,
                       libsystemd_static],
         dependencies : [libshared_deps,
                         userspace],
index 907990d96e4d0a573f8b340ede06fbc40cd1b8f6..612ab9c9c5f4bfac816cb478a728fd5f80b561fd 100644 (file)
@@ -6,6 +6,44 @@
 #include "proc-cmdline.h"
 #include "strv.h"
 
+#if HAVE_KMOD
+
+static void *libkmod_dl = NULL;
+
+DLSYM_FUNCTION(kmod_list_next);
+DLSYM_FUNCTION(kmod_load_resources);
+DLSYM_FUNCTION(kmod_module_get_initstate);
+DLSYM_FUNCTION(kmod_module_get_module);
+DLSYM_FUNCTION(kmod_module_get_name);
+DLSYM_FUNCTION(kmod_module_new_from_lookup);
+DLSYM_FUNCTION(kmod_module_probe_insert_module);
+DLSYM_FUNCTION(kmod_module_unref);
+DLSYM_FUNCTION(kmod_module_unref_list);
+DLSYM_FUNCTION(kmod_new);
+DLSYM_FUNCTION(kmod_set_log_fn);
+DLSYM_FUNCTION(kmod_unref);
+DLSYM_FUNCTION(kmod_validate_resources);
+
+int dlopen_libkmod(void) {
+        return dlopen_many_sym_or_warn(
+                        &libkmod_dl,
+                        "libkmod.so.2",
+                        LOG_DEBUG,
+                        DLSYM_ARG(kmod_list_next),
+                        DLSYM_ARG(kmod_load_resources),
+                        DLSYM_ARG(kmod_module_get_initstate),
+                        DLSYM_ARG(kmod_module_get_module),
+                        DLSYM_ARG(kmod_module_get_name),
+                        DLSYM_ARG(kmod_module_new_from_lookup),
+                        DLSYM_ARG(kmod_module_probe_insert_module),
+                        DLSYM_ARG(kmod_module_unref),
+                        DLSYM_ARG(kmod_module_unref_list),
+                        DLSYM_ARG(kmod_new),
+                        DLSYM_ARG(kmod_set_log_fn),
+                        DLSYM_ARG(kmod_unref),
+                        DLSYM_ARG(kmod_validate_resources));
+}
+
 static int denylist_modules(const char *p, char ***denylist) {
         _cleanup_strv_free_ char **k = NULL;
         int r;
@@ -41,19 +79,21 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
 }
 
 int module_load_and_warn(struct kmod_ctx *ctx, const char *module, bool verbose) {
-        const int probe_flags = KMOD_PROBE_APPLY_BLACKLIST;
-        struct kmod_list *itr;
-        _cleanup_(kmod_module_unref_listp) struct kmod_list *modlist = NULL;
+        _cleanup_(sym_kmod_module_unref_listp) struct kmod_list *modlist = NULL;
         _cleanup_strv_free_ char **denylist = NULL;
         bool denylist_parsed = false;
+        struct kmod_list *itr;
         int r;
 
+        assert(ctx);
+        assert(module);
+
         /* verbose==true means we should log at non-debug level if we
          * fail to find or load the module. */
 
         log_debug("Loading module: %s", module);
 
-        r = kmod_module_new_from_lookup(ctx, module, &modlist);
+        r = sym_kmod_module_new_from_lookup(ctx, module, &modlist);
         if (r < 0)
                 return log_full_errno(verbose ? LOG_ERR : LOG_DEBUG, r,
                                       "Failed to look up module alias '%s': %m", module);
@@ -63,32 +103,37 @@ int module_load_and_warn(struct kmod_ctx *ctx, const char *module, bool verbose)
                                       SYNTHETIC_ERRNO(ENOENT),
                                       "Failed to find module '%s'", module);
 
-        kmod_list_foreach(itr, modlist) {
-                _cleanup_(kmod_module_unrefp) struct kmod_module *mod = NULL;
+        sym_kmod_list_foreach(itr, modlist) {
+                _cleanup_(sym_kmod_module_unrefp) struct kmod_module *mod = NULL;
                 int state, err;
 
-                mod = kmod_module_get_module(itr);
-                state = kmod_module_get_initstate(mod);
+                mod = sym_kmod_module_get_module(itr);
+                state = sym_kmod_module_get_initstate(mod);
 
                 switch (state) {
                 case KMOD_MODULE_BUILTIN:
                         log_full(verbose ? LOG_INFO : LOG_DEBUG,
-                                 "Module '%s' is built in", kmod_module_get_name(mod));
+                                 "Module '%s' is built in", sym_kmod_module_get_name(mod));
                         break;
 
                 case KMOD_MODULE_LIVE:
-                        log_debug("Module '%s' is already loaded", kmod_module_get_name(mod));
+                        log_debug("Module '%s' is already loaded", sym_kmod_module_get_name(mod));
                         break;
 
                 default:
-                        err = kmod_module_probe_insert_module(mod, probe_flags,
-                                                              NULL, NULL, NULL, NULL);
+                        err = sym_kmod_module_probe_insert_module(
+                                        mod,
+                                        KMOD_PROBE_APPLY_BLACKLIST,
+                                        /* extra_options= */ NULL,
+                                        /* run_install= */ NULL,
+                                        /* data= */ NULL,
+                                        /* print_action= */ NULL);
                         if (err == 0)
                                 log_full(verbose ? LOG_INFO : LOG_DEBUG,
-                                         "Inserted module '%s'", kmod_module_get_name(mod));
+                                         "Inserted module '%s'", sym_kmod_module_get_name(mod));
                         else if (err == KMOD_PROBE_APPLY_BLACKLIST)
                                 log_full(verbose ? LOG_INFO : LOG_DEBUG,
-                                         "Module '%s' is deny-listed (by kmod)", kmod_module_get_name(mod));
+                                         "Module '%s' is deny-listed (by kmod)", sym_kmod_module_get_name(mod));
                         else {
                                 assert(err < 0);
 
@@ -102,9 +147,9 @@ int module_load_and_warn(struct kmod_ctx *ctx, const char *module, bool verbose)
 
                                                 denylist_parsed = true;
                                         }
-                                        if (strv_contains(denylist, kmod_module_get_name(mod))) {
+                                        if (strv_contains(denylist, sym_kmod_module_get_name(mod))) {
                                                 log_full(verbose ? LOG_INFO : LOG_DEBUG,
-                                                         "Module '%s' is deny-listed (by kernel)", kmod_module_get_name(mod));
+                                                         "Module '%s' is deny-listed (by kernel)", sym_kmod_module_get_name(mod));
                                                 continue;
                                         }
                                 }
@@ -115,7 +160,7 @@ int module_load_and_warn(struct kmod_ctx *ctx, const char *module, bool verbose)
                                                                 LOG_ERR,
                                                err,
                                                "Failed to insert module '%s': %m",
-                                               kmod_module_get_name(mod));
+                                               sym_kmod_module_get_name(mod));
                                 if (!IN_SET(err, -ENODEV, -ENOENT))
                                         r = err;
                         }
@@ -124,3 +169,38 @@ int module_load_and_warn(struct kmod_ctx *ctx, const char *module, bool verbose)
 
         return r;
 }
+
+_printf_(6,0) static void systemd_kmod_log(
+                void *data,
+                int priority,
+                const char *file,
+                int line,
+                const char *fn,
+                const char *format,
+                va_list args) {
+
+        log_internalv(priority, 0, file, line, fn, format, args);
+}
+
+int module_setup_context(struct kmod_ctx **ret) {
+        _cleanup_(sym_kmod_unrefp) struct kmod_ctx *ctx = NULL;
+        int r;
+
+        assert(ret);
+
+        r = dlopen_libkmod();
+        if (r < 0)
+                return r;
+
+        ctx = sym_kmod_new(NULL, NULL);
+        if (!ctx)
+                return -ENOMEM;
+
+        (void) sym_kmod_load_resources(ctx);
+        sym_kmod_set_log_fn(ctx, systemd_kmod_log, NULL);
+
+        *ret = TAKE_PTR(ctx);
+        return 0;
+}
+
+#endif
index 8ca6a06a070cd9d0768f797f982509086f749b46..081973802b12e741f9f9a03322b3ab8bac4f03fc 100644 (file)
@@ -1,12 +1,56 @@
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 #pragma once
 
+#include "dlfcn-util.h"
+
+#if HAVE_KMOD
+
 #include <libkmod.h>
 
 #include "macro.h"
 
-DEFINE_TRIVIAL_CLEANUP_FUNC(struct kmod_ctx*, kmod_unref);
-DEFINE_TRIVIAL_CLEANUP_FUNC(struct kmod_module*, kmod_module_unref);
-DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(struct kmod_list*, kmod_module_unref_list, NULL);
+DLSYM_PROTOTYPE(kmod_list_next);
+DLSYM_PROTOTYPE(kmod_load_resources);
+DLSYM_PROTOTYPE(kmod_module_get_initstate);
+DLSYM_PROTOTYPE(kmod_module_get_module);
+DLSYM_PROTOTYPE(kmod_module_get_name);
+DLSYM_PROTOTYPE(kmod_module_new_from_lookup);
+DLSYM_PROTOTYPE(kmod_module_probe_insert_module);
+DLSYM_PROTOTYPE(kmod_module_unref);
+DLSYM_PROTOTYPE(kmod_module_unref_list);
+DLSYM_PROTOTYPE(kmod_new);
+DLSYM_PROTOTYPE(kmod_set_log_fn);
+DLSYM_PROTOTYPE(kmod_unref);
+DLSYM_PROTOTYPE(kmod_validate_resources);
+
+int dlopen_libkmod(void);
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(struct kmod_ctx*, sym_kmod_unref);
+DEFINE_TRIVIAL_CLEANUP_FUNC(struct kmod_module*, sym_kmod_module_unref);
+DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(struct kmod_list*, sym_kmod_module_unref_list, NULL);
+
+#define sym_kmod_list_foreach(list_entry, first_entry) \
+        for (list_entry = first_entry; \
+                list_entry != NULL; \
+                list_entry = sym_kmod_list_next(first_entry, list_entry))
 
 int module_load_and_warn(struct kmod_ctx *ctx, const char *module, bool verbose);
+int module_setup_context(struct kmod_ctx **ret);
+
+#else
+
+struct kmod_ctx;
+
+static inline int dlopen_libkmod(void) {
+        return -EOPNOTSUPP;
+}
+
+static inline int module_setup_context(struct kmod_ctx **ret) {
+        return -EOPNOTSUPP;
+}
+
+static inline int module_load_and_warn(struct kmod_ctx *ctx, const char *module, bool verbose) {
+        return -EOPNOTSUPP;
+}
+
+#endif
diff --git a/src/shared/nsresource.c b/src/shared/nsresource.c
new file mode 100644 (file)
index 0000000..bc0a352
--- /dev/null
@@ -0,0 +1,330 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <sys/prctl.h>
+
+#include "fd-util.h"
+#include "format-util.h"
+#include "missing_sched.h"
+#include "namespace-util.h"
+#include "nsresource.h"
+#include "process-util.h"
+#include "varlink.h"
+
+static int make_pid_name(char **ret) {
+        char comm[TASK_COMM_LEN];
+
+        assert(ret);
+
+        if (prctl(PR_GET_NAME, comm) < 0)
+                return -errno;
+
+        /* So the namespace name should be 16 chars at max (because we want that it is usable in usernames,
+         * which have a limit of 31 chars effectively, and the nsresourced service wants to prefix/suffix
+         * some bits). But it also should be unique if we are called multiple times in a row. Hence we take
+         * the "comm" name (which is 15 chars), and suffix it with the UID, possibly overriding the end. */
+        assert_cc(TASK_COMM_LEN == 15 + 1);
+
+        char spid[DECIMAL_STR_MAX(pid_t)];
+        xsprintf(spid, PID_FMT, getpid_cached());
+
+        assert(strlen(spid) <= 16);
+        strshorten(comm, 16 - strlen(spid));
+
+        _cleanup_free_ char *s = strjoin(comm, spid);
+        if (!s)
+                return -ENOMEM;
+
+        *ret = TAKE_PTR(s);
+        return 0;
+}
+
+int nsresource_allocate_userns(const char *name, uint64_t size) {
+        _cleanup_(varlink_unrefp) Varlink *vl = NULL;
+        _cleanup_close_ int userns_fd = -EBADF;
+        _cleanup_free_ char *_name = NULL;
+        const char *error_id;
+        int r, userns_fd_idx;
+
+        /* Allocate a new dynamic user namespace via the userdb registry logic */
+
+        if (!name) {
+                r = make_pid_name(&_name);
+                if (r < 0)
+                        return r;
+
+                name = _name;
+        }
+
+        if (size <= 0 || size > UINT64_C(0x100000000)) /* Note: the server actually only allows allocating 1 or 64K right now */
+                return -EINVAL;
+
+        r = varlink_connect_address(&vl, "/run/systemd/io.systemd.NamespaceResource");
+        if (r < 0)
+                return log_debug_errno(r, "Failed to connect to namespace resource manager: %m");
+
+        r = varlink_set_allow_fd_passing_output(vl, true);
+        if (r < 0)
+                return log_debug_errno(r, "Failed to enable varlink fd passing for write: %m");
+
+        userns_fd = userns_acquire_empty();
+        if (userns_fd < 0)
+                return log_debug_errno(userns_fd, "Failed to acquire empty user namespace: %m");
+
+        userns_fd_idx = varlink_push_dup_fd(vl, userns_fd);
+        if (userns_fd_idx < 0)
+                return log_debug_errno(userns_fd_idx, "Failed to push userns fd into varlink connection: %m");
+
+        JsonVariant *reply = NULL;
+        r = varlink_callb(vl,
+                          "io.systemd.NamespaceResource.AllocateUserRange",
+                          &reply,
+                          &error_id,
+                          JSON_BUILD_OBJECT(
+                                          JSON_BUILD_PAIR("name", JSON_BUILD_STRING(name)),
+                                          JSON_BUILD_PAIR("size", JSON_BUILD_UNSIGNED(size)),
+                                          JSON_BUILD_PAIR("userNamespaceFileDescriptor", JSON_BUILD_UNSIGNED(userns_fd_idx))));
+        if (r < 0)
+                return log_debug_errno(r, "Failed to call AllocateUserRange() varlink call: %m");
+        if (error_id)
+                return log_debug_errno(varlink_error_to_errno(error_id, reply), "Failed to allocate user namespace with %" PRIu64 " users: %s", size, error_id);
+
+        return TAKE_FD(userns_fd);
+}
+
+int nsresource_register_userns(const char *name, int userns_fd) {
+        _cleanup_(varlink_unrefp) Varlink *vl = NULL;
+        _cleanup_close_ int _userns_fd = -EBADF;
+        _cleanup_free_ char *_name = NULL;
+        const char *error_id;
+        int r, userns_fd_idx;
+
+        /* Register the specified user namespace with userbd. */
+
+        if (!name) {
+                r = make_pid_name(&_name);
+                if (r < 0)
+                        return r;
+
+                name = _name;
+        }
+
+        if (userns_fd < 0) {
+                _userns_fd = namespace_open_by_type(NAMESPACE_USER);
+                if (_userns_fd < 0)
+                        return -errno;
+
+                userns_fd = _userns_fd;
+        }
+
+        r = varlink_connect_address(&vl, "/run/systemd/io.systemd.NamespaceResource");
+        if (r < 0)
+                return log_debug_errno(r, "Failed to connect to namespace resource manager: %m");
+
+        r = varlink_set_allow_fd_passing_output(vl, true);
+        if (r < 0)
+                return log_debug_errno(r, "Failed to enable varlink fd passing for write: %m");
+
+        userns_fd_idx = varlink_push_dup_fd(vl, userns_fd);
+        if (userns_fd_idx < 0)
+                return log_debug_errno(userns_fd_idx, "Failed to push userns fd into varlink connection: %m");
+
+        JsonVariant *reply = NULL;
+        r = varlink_callb(vl,
+                          "io.systemd.NamespaceResource.RegisterUserNamespace",
+                          &reply,
+                          &error_id,
+                          JSON_BUILD_OBJECT(
+                                          JSON_BUILD_PAIR("name", JSON_BUILD_STRING(name)),
+                                          JSON_BUILD_PAIR("userNamespaceFileDescriptor", JSON_BUILD_UNSIGNED(userns_fd_idx))));
+        if (r < 0)
+                return log_debug_errno(r, "Failed to call RegisterUserNamespace() varlink call: %m");
+        if (error_id)
+                return log_debug_errno(varlink_error_to_errno(error_id, reply), "Failed to register user namespace: %s", error_id);
+
+        return 0;
+}
+
+int nsresource_add_mount(int userns_fd, int mount_fd) {
+        _cleanup_(varlink_unrefp) Varlink *vl = NULL;
+        _cleanup_close_ int _userns_fd = -EBADF;
+        int r, userns_fd_idx, mount_fd_idx;
+        const char *error_id;
+
+        assert(mount_fd >= 0);
+
+        if (userns_fd < 0) {
+                _userns_fd = namespace_open_by_type(NAMESPACE_USER);
+                if (_userns_fd < 0)
+                        return _userns_fd;
+
+                userns_fd = _userns_fd;
+        }
+
+        r = varlink_connect_address(&vl, "/run/systemd/io.systemd.NamespaceResource");
+        if (r < 0)
+                return log_error_errno(r, "Failed to connect to namespace resource manager: %m");
+
+        r = varlink_set_allow_fd_passing_output(vl, true);
+        if (r < 0)
+                return log_error_errno(r, "Failed to enable varlink fd passing for write: %m");
+
+        userns_fd_idx = varlink_push_dup_fd(vl, userns_fd);
+        if (userns_fd_idx < 0)
+                return log_error_errno(userns_fd_idx, "Failed to push userns fd into varlink connection: %m");
+
+        mount_fd_idx = varlink_push_dup_fd(vl, mount_fd);
+        if (mount_fd_idx < 0)
+                return log_error_errno(mount_fd_idx, "Failed to push mount fd into varlink connection: %m");
+
+        JsonVariant *reply = NULL;
+        r = varlink_callb(vl,
+                          "io.systemd.NamespaceResource.AddMountToUserNamespace",
+                          &reply,
+                          &error_id,
+                          JSON_BUILD_OBJECT(
+                                          JSON_BUILD_PAIR("userNamespaceFileDescriptor", JSON_BUILD_UNSIGNED(userns_fd_idx)),
+                                          JSON_BUILD_PAIR("mountFileDescriptor", JSON_BUILD_UNSIGNED(mount_fd_idx))));
+        if (r < 0)
+                return log_error_errno(r, "Failed to call AddMountToUserNamespace() varlink call: %m");
+        if (streq_ptr(error_id, "io.systemd.NamespaceResource.UserNamespaceNotRegistered")) {
+                log_notice("User namespace has not been allocated via namespace resource registry, not adding mount to registration.");
+                return 0;
+        }
+        if (error_id)
+                return log_error_errno(varlink_error_to_errno(error_id, reply), "Failed to mount image: %s", error_id);
+
+        return 1;
+}
+
+int nsresource_add_cgroup(int userns_fd, int cgroup_fd) {
+        _cleanup_(varlink_unrefp) Varlink *vl = NULL;
+        _cleanup_close_ int _userns_fd = -EBADF;
+        int r, userns_fd_idx, cgroup_fd_idx;
+        const char *error_id;
+
+        assert(cgroup_fd >= 0);
+
+        if (userns_fd < 0) {
+                _userns_fd = namespace_open_by_type(NAMESPACE_USER);
+                if (_userns_fd < 0)
+                        return -errno;
+
+                userns_fd = _userns_fd;
+        }
+
+        r = varlink_connect_address(&vl, "/run/systemd/io.systemd.NamespaceResource");
+        if (r < 0)
+                return log_debug_errno(r, "Failed to connect to namespace resource manager: %m");
+
+        r = varlink_set_allow_fd_passing_output(vl, true);
+        if (r < 0)
+                return log_debug_errno(r, "Failed to enable varlink fd passing for write: %m");
+
+        userns_fd_idx = varlink_push_dup_fd(vl, userns_fd);
+        if (userns_fd_idx < 0)
+                return log_debug_errno(userns_fd_idx, "Failed to push userns fd into varlink connection: %m");
+
+        cgroup_fd_idx = varlink_push_dup_fd(vl, cgroup_fd);
+        if (cgroup_fd_idx < 0)
+                return log_debug_errno(userns_fd_idx, "Failed to push cgroup fd into varlink connection: %m");
+
+        JsonVariant *reply = NULL;
+        r = varlink_callb(vl,
+                          "io.systemd.NamespaceResource.AddControlGroupToUserNamespace",
+                          &reply,
+                          &error_id,
+                          JSON_BUILD_OBJECT(
+                                          JSON_BUILD_PAIR("userNamespaceFileDescriptor", JSON_BUILD_UNSIGNED(userns_fd_idx)),
+                                          JSON_BUILD_PAIR("controlGroupFileDescriptor", JSON_BUILD_UNSIGNED(cgroup_fd_idx))));
+        if (r < 0)
+                return log_debug_errno(r, "Failed to call AddControlGroupToUserNamespace() varlink call: %m");
+        if (streq_ptr(error_id, "io.systemd.NamespaceResource.UserNamespaceNotRegistered")) {
+                log_notice("User namespace has not been allocated via namespace resource registry, not adding cgroup to registration.");
+                return 0;
+        }
+        if (error_id)
+                return log_debug_errno(varlink_error_to_errno(error_id, reply), "Failed to add cgroup to user namespace: %s", error_id);
+
+        return 1;
+}
+
+int nsresource_add_netif(
+                int userns_fd,
+                int netns_fd,
+                const char *namespace_ifname,
+                char **ret_host_ifname,
+                char **ret_namespace_ifname) {
+
+        _cleanup_close_ int _userns_fd = -EBADF, _netns_fd = -EBADF;
+        _cleanup_(varlink_unrefp) Varlink *vl = NULL;
+        int r, userns_fd_idx, netns_fd_idx;
+        const char *error_id;
+
+        if (userns_fd < 0) {
+                _userns_fd = namespace_open_by_type(NAMESPACE_USER);
+                if (_userns_fd < 0)
+                        return -errno;
+
+                userns_fd = _userns_fd;
+        }
+
+        if (netns_fd < 0) {
+                _netns_fd = namespace_open_by_type(NAMESPACE_NET);
+                if (_netns_fd < 0)
+                        return -errno;
+
+                netns_fd = _netns_fd;
+        }
+
+        r = varlink_connect_address(&vl, "/run/systemd/io.systemd.NamespaceResource");
+        if (r < 0)
+                return log_debug_errno(r, "Failed to connect to namespace resource manager: %m");
+
+        r = varlink_set_allow_fd_passing_output(vl, true);
+        if (r < 0)
+                return log_debug_errno(r, "Failed to enable varlink fd passing for write: %m");
+
+        userns_fd_idx = varlink_push_dup_fd(vl, userns_fd);
+        if (userns_fd_idx < 0)
+                return log_debug_errno(userns_fd_idx, "Failed to push userns fd into varlink connection: %m");
+
+        netns_fd_idx = varlink_push_dup_fd(vl, netns_fd);
+        if (netns_fd_idx < 0)
+                return log_debug_errno(netns_fd_idx, "Failed to push netns fd into varlink connection: %m");
+
+        JsonVariant *reply = NULL;
+        r = varlink_callb(vl,
+                          "io.systemd.NamespaceResource.AddNetworkToUserNamespace",
+                          &reply,
+                          &error_id,
+                          JSON_BUILD_OBJECT(
+                                          JSON_BUILD_PAIR("userNamespaceFileDescriptor", JSON_BUILD_UNSIGNED(userns_fd_idx)),
+                                          JSON_BUILD_PAIR("networkNamespaceFileDescriptor", JSON_BUILD_UNSIGNED(netns_fd_idx)),
+                                          JSON_BUILD_PAIR("mode", JSON_BUILD_CONST_STRING("veth")),
+                                          JSON_BUILD_PAIR_CONDITION(namespace_ifname, "namespaceInterfaceName", JSON_BUILD_STRING(namespace_ifname))));
+        if (r < 0)
+                return log_debug_errno(r, "Failed to call AddNetworkToUserNamespace() varlink call: %m");
+        if (streq_ptr(error_id, "io.systemd.NamespaceResource.UserNamespaceNotRegistered")) {
+                log_notice("User namespace has not been allocated via namespace resource registry, not adding network to registration.");
+                return 0;
+        }
+        if (error_id)
+                return log_debug_errno(varlink_error_to_errno(error_id, reply), "Failed to add network to user namespace: %s", error_id);
+
+        _cleanup_free_ char *host_interface_name = NULL, *namespace_interface_name = NULL;
+        r = json_dispatch(
+                        reply,
+                        (const JsonDispatch[]) {
+                                { "hostInterfaceName",      JSON_VARIANT_STRING, json_dispatch_string, PTR_TO_SIZE(&host_interface_name)      },
+                                { "namespaceInterfaceName", JSON_VARIANT_STRING, json_dispatch_string, PTR_TO_SIZE(&namespace_interface_name) },
+                        },
+                        JSON_ALLOW_EXTENSIONS,
+                        /* userdata= */ NULL);
+
+        if (ret_host_ifname)
+                *ret_host_ifname = TAKE_PTR(host_interface_name);
+        if (ret_namespace_ifname)
+                *ret_namespace_ifname = TAKE_PTR(namespace_interface_name);
+
+        return 1;
+}
diff --git a/src/shared/nsresource.h b/src/shared/nsresource.h
new file mode 100644 (file)
index 0000000..6b807b3
--- /dev/null
@@ -0,0 +1,10 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include <inttypes.h>
+
+int nsresource_allocate_userns(const char *name, uint64_t size);
+int nsresource_register_userns(const char *name, int userns_fd);
+int nsresource_add_mount(int userns_fd, int mount_fd);
+int nsresource_add_cgroup(int userns_fd, int cgroup_fd);
+int nsresource_add_netif(int userns_fd, int netns_fd, const char *namespace_ifname, char **ret_host_ifname, char **ret_namespace_ifname);
index beebb74cff4287957d4ec2f80e05ba3c2efa4554..a9c2a11032742eb617f3e372608deaf1ac4b5313 100644 (file)
@@ -1,4 +1,3 @@
-
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 
 #include <fcntl.h>
@@ -96,7 +95,7 @@ int open_file_to_string(const OpenFile *of, char **ret) {
         assert(of);
         assert(ret);
 
-        s = shell_escape(of->path, ":");
+        s = xescape(of->path, ":");
         if (!s)
                 return -ENOMEM;
 
@@ -122,19 +121,14 @@ int open_file_to_string(const OpenFile *of, char **ret) {
         return 0;
 }
 
-OpenFile *open_file_free(OpenFile *of) {
+OpenFileopen_file_free(OpenFile *of) {
         if (!of)
                 return NULL;
 
         free(of->path);
         free(of->fdname);
-        return mfree(of);
-}
 
-void open_file_free_many(OpenFile **head) {
-        assert(head);
-
-        LIST_CLEAR(open_files, *head, open_file_free);
+        return mfree(of);
 }
 
 static const char * const open_file_flags_table[_OPENFILE_MAX] = {
index bb63ec8f9c3fffe141612aa3a3a8036112e1bf18..4999c96ae7f427bca9c3f9d852113b0711a62edf 100644 (file)
@@ -1,8 +1,8 @@
-
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 #pragma once
 
 #include "list.h"
+#include "macro.h"
 
 typedef enum OpenFileFlag {
         OPENFILE_READ_ONLY = 1 << 0,
@@ -27,10 +27,12 @@ int open_file_validate(const OpenFile *of);
 
 int open_file_to_string(const OpenFile *of, char **ret);
 
-OpenFile *open_file_free(OpenFile *of);
+OpenFileopen_file_free(OpenFile *of);
 DEFINE_TRIVIAL_CLEANUP_FUNC(OpenFile*, open_file_free);
 
-void open_file_free_many(OpenFile **head);
+static inline void open_file_free_many(OpenFile **head) {
+        LIST_CLEAR(open_files, *ASSERT_PTR(head), open_file_free);
+}
 
-const char *open_file_flags_to_string(OpenFileFlag t) _const_;
+const charopen_file_flags_to_string(OpenFileFlag t) _const_;
 OpenFileFlag open_file_flags_from_string(const char *t) _pure_;
index e6feba44bdf559e7058909253ee5ca042586de64..8727fe248454c01718a340cddedd84b2831cc89b 100644 (file)
@@ -1711,7 +1711,7 @@ static int pkcs11_acquire_public_key_callback(
                 switch (attributes[i].type) {
                 case CKA_CLASS: {
                         CK_OBJECT_CLASS requested_class = *((CK_OBJECT_CLASS*) attributes[i].pValue);
-                        if (requested_class != CKO_PUBLIC_KEY && requested_class != CKO_CERTIFICATE)
+                        if (!IN_SET(requested_class, CKO_PUBLIC_KEY, CKO_CERTIFICATE))
                                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
                                                        "Selected PKCS#11 object is not a public key or certificate, refusing.");
                         break;
@@ -1743,7 +1743,7 @@ static int pkcs11_acquire_public_key_callback(
                 candidate_attributes[0].ulValueLen = sizeof(class);
                 candidate_attributes[1].ulValueLen = sizeof(type);
                 rv = m->C_GetAttributeValue(session, candidate, candidate_attributes, ELEMENTSOF(candidate_attributes));
-                if (rv != CKR_OK && rv != CKR_ATTRIBUTE_TYPE_INVALID)
+                if (!IN_SET(rv, CKR_OK, CKR_ATTRIBUTE_TYPE_INVALID))
                         return log_error_errno(SYNTHETIC_ERRNO(EIO),
                                 "Failed to get attributes of a selected candidate: %s", sym_p11_kit_strerror(rv));
 
index bdbd79087ca27238f0ec412d20d83342b4983c0d..7bd8dd6d89889850cb720106dbcbed184f345ac3 100644 (file)
@@ -478,7 +478,7 @@ static int pty_forward_ansi_process(PTYForward *f, size_t offset) {
                                  * since we cannot lookahead to see if the Esc is followed by a \
                                  * we cut a corner here and assume it will be \. */
 
-                                if (c == '\x07' || c == '\x1b') {
+                                if (IN_SET(c, '\x07', '\x1b')) {
                                         r = insert_window_title_fix(f, i+1);
                                         if (r < 0)
                                                 return r;
index 178aefac214d3d6a31b23bd25c6f71a1ffb3d2ad..b1c4a49da1a353fa39ec6bea20e083424084d105 100644 (file)
@@ -61,7 +61,7 @@ int resize_fs(int fd, uint64_t sz, uint64_t *ret_size) {
                 if (ret_size)
                         *ret_size = sz;
 
-        } else if (is_fs_type(&sfs, XFS_SB_MAGIC)) {
+        } else if (is_fs_type(&sfs, XFS_SUPER_MAGIC)) {
                 xfs_fsop_geom_t geo;
                 xfs_growfs_data_t d;
 
@@ -95,7 +95,7 @@ uint64_t minimal_size_by_fs_magic(statfs_f_type_t magic) {
         case (statfs_f_type_t) EXT4_SUPER_MAGIC:
                 return EXT4_MINIMAL_SIZE;
 
-        case (statfs_f_type_t) XFS_SB_MAGIC:
+        case (statfs_f_type_t) XFS_SUPER_MAGIC:
                 return XFS_MINIMAL_SIZE;
 
         case (statfs_f_type_t) BTRFS_SUPER_MAGIC:
index 483cbc7419b25e55364fd928de94226e3ae19040..735caf4978f1afeef77ac8ee3bb660d1d124361f 100644 (file)
@@ -180,7 +180,7 @@ int serialize_strv(FILE *f, const char *key, char **l) {
 }
 
 int serialize_pidref(FILE *f, FDSet *fds, const char *key, PidRef *pidref) {
-        int copy;
+        int r;
 
         assert(f);
         assert(fds);
@@ -188,17 +188,23 @@ int serialize_pidref(FILE *f, FDSet *fds, const char *key, PidRef *pidref) {
         if (!pidref_is_set(pidref))
                 return 0;
 
-        /* If we have a pidfd we serialize the fd and encode the fd number prefixed by "@" in the
-         * serialization. Otherwise we serialize the numeric PID as it is. */
+        /* We always serialize the pid separately, to keep downgrades mostly working (older versions will
+         * deserialize the pid and silently fail to deserialize the pidfd). If we also have a pidfd, we
+         * serialize both the pid and pidfd, so that we can construct the exact same pidref after
+         * deserialization (this doesn't work with only the pidfd, as we can't retrieve the original pid
+         * from the pidfd anymore if the process is reaped). */
 
-        if (pidref->fd < 0)
-                return serialize_item_format(f, key, PID_FMT, pidref->pid);
+        if (pidref->fd >= 0) {
+                int copy = fdset_put_dup(fds, pidref->fd);
+                if (copy < 0)
+                        return log_error_errno(copy, "Failed to add file descriptor to serialization set: %m");
 
-        copy = fdset_put_dup(fds, pidref->fd);
-        if (copy < 0)
-                return log_error_errno(copy, "Failed to add file descriptor to serialization set: %m");
+                r = serialize_item_format(f, key, "@%i:" PID_FMT, copy, pidref->pid);
+                if (r < 0)
+                        return r;
+        }
 
-        return serialize_item_format(f, key, "@%i", copy);
+        return serialize_item_format(f, key, PID_FMT, pidref->pid);
 }
 
 int serialize_ratelimit(FILE *f, const char *key, const RateLimit *rl) {
@@ -476,12 +482,39 @@ int deserialize_pidref(FDSet *fds, const char *value, PidRef *ret) {
 
         e = startswith(value, "@");
         if (e) {
-                int fd = deserialize_fd(fds, e);
+                _cleanup_free_ char *fdstr = NULL, *pidstr = NULL;
+                _cleanup_close_ int fd = -EBADF;
+
+                r = extract_many_words(&e, ":", /* flags = */ 0, &fdstr, &pidstr);
+                if (r < 0)
+                        return log_debug_errno(r, "Failed to deserialize pidref '%s': %m", e);
+                if (r == 0)
+                        return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Cannot deserialize pidref from empty string.");
+
+                assert(r <= 2);
 
+                fd = deserialize_fd(fds, fdstr);
                 if (fd < 0)
                         return fd;
 
-                r = pidref_set_pidfd_consume(ret, fd);
+                /* The serialization format changed after 255.4. In systemd <= 255.4 only pidfd is
+                 * serialized, but that causes problems when reconstructing pidref (see serialize_pidref for
+                 * details). After 255.4 the pid is serialized as well even if we have a pidfd, but we still
+                 * need to support older format as we might be upgrading from a version that still uses the
+                 * old format. */
+                if (pidstr) {
+                        pid_t pid;
+
+                        r = parse_pid(pidstr, &pid);
+                        if (r < 0)
+                                return log_debug_errno(r, "Failed to parse PID: %s", pidstr);
+
+                        *ret = (PidRef) {
+                                .pid = pid,
+                                .fd = TAKE_FD(fd),
+                        };
+                } else
+                        r = pidref_set_pidfd_consume(ret, TAKE_FD(fd));
         } else {
                 pid_t pid;
 
index c96f8485ddd5433b66536468f2f1198af84f6598..b8cd83c7c1efb752be93de450b3b947f270cf4ce 100644 (file)
@@ -54,6 +54,8 @@ SleepConfig* sleep_config_free(SleepConfig *sc) {
                 strv_free(sc->modes[i]);
         }
 
+        strv_free(sc->mem_modes);
+
         return mfree(sc);
 }
 
@@ -69,8 +71,8 @@ static int config_parse_sleep_mode(
                 void *data,
                 void *userdata) {
 
-        _cleanup_strv_free_ char **modes = NULL;
         char ***sv = ASSERT_PTR(data);
+        _cleanup_strv_free_ char **modes = NULL;
         int r;
 
         assert(filename);
@@ -87,7 +89,7 @@ static int config_parse_sleep_mode(
                         return log_oom();
         }
 
-        return free_and_replace(*sv, modes);
+        return strv_free_and_replace(*sv, modes);
 }
 
 static void sleep_config_validate_state_and_mode(SleepConfig *sc) {
@@ -140,6 +142,8 @@ int parse_sleep_config(SleepConfig **ret) {
                 { "Sleep", "HybridSleepState",          config_parse_warn_compat, DISABLED_LEGACY, NULL                         },
                 { "Sleep", "HybridSleepMode",           config_parse_warn_compat, DISABLED_LEGACY, NULL                         },
 
+                { "Sleep", "MemorySleepMode",           config_parse_sleep_mode,  0,               &sc->mem_modes               },
+
                 { "Sleep", "HibernateDelaySec",         config_parse_sec,         0,               &sc->hibernate_delay_usec    },
                 { "Sleep", "SuspendEstimationSec",      config_parse_sec,         0,               &sc->suspend_estimation_usec },
                 {}
@@ -183,7 +187,7 @@ int parse_sleep_config(SleepConfig **ret) {
         return 0;
 }
 
-int sleep_state_supported(char **states) {
+int sleep_state_supported(char * const *states) {
         _cleanup_free_ char *supported_sysfs = NULL;
         const char *found;
         int r;
@@ -213,22 +217,24 @@ int sleep_state_supported(char **states) {
         return false;
 }
 
-int sleep_mode_supported(char **modes) {
+int sleep_mode_supported(const char *path, char * const *modes) {
         _cleanup_free_ char *supported_sysfs = NULL;
         int r;
 
+        assert(path);
+
         /* Unlike state, kernel has its own default choice if not configured */
         if (strv_isempty(modes)) {
-                log_debug("No sleep mode configured, using kernel default.");
+                log_debug("No sleep mode configured, using kernel default for %s.", path);
                 return true;
         }
 
-        if (access("/sys/power/disk", W_OK) < 0)
-                return log_debug_errno(errno, "/sys/power/disk is not writable: %m");
+        if (access(path, W_OK) < 0)
+                return log_debug_errno(errno, "%s is not writable: %m", path);
 
-        r = read_one_line_file("/sys/power/disk", &supported_sysfs);
+        r = read_one_line_file(path, &supported_sysfs);
         if (r < 0)
-                return log_debug_errno(r, "Failed to read /sys/power/disk: %m");
+                return log_debug_errno(r, "Failed to read %s: %m", path);
 
         for (const char *p = supported_sysfs;;) {
                 _cleanup_free_ char *word = NULL;
@@ -237,7 +243,7 @@ int sleep_mode_supported(char **modes) {
 
                 r = extract_first_word(&p, &word, NULL, 0);
                 if (r < 0)
-                        return log_debug_errno(r, "Failed to parse /sys/power/disk: %m");
+                        return log_debug_errno(r, "Failed to parse %s: %m", path);
                 if (r == 0)
                         break;
 
@@ -250,14 +256,15 @@ int sleep_mode_supported(char **modes) {
                 }
 
                 if (strv_contains(modes, mode)) {
-                        log_debug("Disk sleep mode '%s' is supported by kernel.", mode);
+                        log_debug("Sleep mode '%s' is supported by kernel (%s).", mode, path);
                         return true;
                 }
         }
 
         if (DEBUG_LOGGING) {
                 _cleanup_free_ char *joined = strv_join(modes, " ");
-                log_debug("None of the configured hibernation power modes are supported by kernel: %s", strnull(joined));
+                log_debug("None of the configured modes are supported by kernel (%s): %s",
+                          path, strnull(joined));
         }
         return false;
 }
@@ -341,8 +348,18 @@ static int sleep_supported_internal(
                 return false;
         }
 
-        if (sleep_operation_is_hibernation(operation)) {
-                r = sleep_mode_supported(sleep_config->modes[operation]);
+        if (SLEEP_NEEDS_MEM_SLEEP(sleep_config, operation)) {
+                r = sleep_mode_supported("/sys/power/mem_sleep", sleep_config->mem_modes);
+                if (r < 0)
+                        return r;
+                if (r == 0) {
+                        *ret_support = SLEEP_STATE_OR_MODE_NOT_SUPPORTED;
+                        return false;
+                }
+        }
+
+        if (SLEEP_OPERATION_IS_HIBERNATION(operation)) {
+                r = sleep_mode_supported("/sys/power/disk", sleep_config->modes[operation]);
                 if (r < 0)
                         return r;
                 if (r == 0) {
index bc5aeb91bd62f78d80cadf43c3a4c58ae0b66320..75d9c4a6221efdda50733993875a2a0392a3393e 100644 (file)
@@ -1,6 +1,7 @@
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 #pragma once
 
+#include "strv.h"
 #include "time-util.h"
 
 typedef enum SleepOperation {
@@ -20,7 +21,7 @@ typedef enum SleepOperation {
 const char* sleep_operation_to_string(SleepOperation s) _const_;
 SleepOperation sleep_operation_from_string(const char *s) _pure_;
 
-static inline bool sleep_operation_is_hibernation(SleepOperation operation) {
+static inline bool SLEEP_OPERATION_IS_HIBERNATION(SleepOperation operation) {
         return IN_SET(operation, SLEEP_HIBERNATE, SLEEP_HYBRID_SLEEP);
 }
 
@@ -28,7 +29,8 @@ typedef struct SleepConfig {
         bool allow[_SLEEP_OPERATION_MAX];
 
         char **states[_SLEEP_OPERATION_CONFIG_MAX];
-        char **modes[_SLEEP_OPERATION_CONFIG_MAX]; /* Power mode after writing hibernation image */
+        char **modes[_SLEEP_OPERATION_CONFIG_MAX];  /* Power mode after writing hibernation image (/sys/power/disk) */
+        char **mem_modes;                           /* /sys/power/mem_sleep */
 
         usec_t hibernate_delay_usec;
         usec_t suspend_estimation_usec;
@@ -39,6 +41,18 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(SleepConfig*, sleep_config_free);
 
 int parse_sleep_config(SleepConfig **sleep_config);
 
+static inline bool SLEEP_NEEDS_MEM_SLEEP(const SleepConfig *sc, SleepOperation operation) {
+        assert(sc);
+        assert(operation >= 0 && operation < _SLEEP_OPERATION_CONFIG_MAX);
+
+        /* As per https://docs.kernel.org/admin-guide/pm/sleep-states.html#basic-sysfs-interfaces-for-system-suspend-and-hibernation,
+         * /sys/power/mem_sleep is honored if /sys/power/state is set to "mem" (common for suspend)
+         * or /sys/power/disk is set to "suspend" (hybrid-sleep). */
+
+        return strv_contains(sc->states[operation], "mem") ||
+               strv_contains(sc->modes[operation], "suspend");
+}
+
 typedef enum SleepSupport {
         SLEEP_SUPPORTED,
         SLEEP_DISABLED,                    /* Disabled in SleepConfig.allow */
@@ -55,5 +69,5 @@ static inline int sleep_supported(SleepOperation operation) {
 }
 
 /* Only for test-sleep-config */
-int sleep_state_supported(char **states);
-int sleep_mode_supported(char **modes);
+int sleep_state_supported(char * const *states);
+int sleep_mode_supported(const char *path, char * const *modes);
index 6865b045b5e9172837d207bea420f8f6668f7d1f..a126d5d73b1eac40a1321ebbb786223f07570f4f 100644 (file)
@@ -1,9 +1,10 @@
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 
+/* Make sure the net/if.h header is included before any linux/ one */
+#include <net/if.h>
 #include <arpa/inet.h>
 #include <errno.h>
 #include <linux/net_namespace.h>
-#include <net/if.h>
 #include <string.h>
 
 #include "alloc-util.h"
index 85f9463f9b5c5f1a2bcc69c4e6d8eabe4c2244a7..784748545c28cb3b81860df2ea2ce1a339819f38 100644 (file)
@@ -2,11 +2,16 @@
 #pragma once
 
 #include <stdbool.h>
+#include <sys/prctl.h>
+#include <unistd.h>
 
 #include "sd-daemon.h"
 
 #include "argv-util.h"
 #include "macro.h"
+#include "process-util.h"
+#include "rlimit-util.h"
+#include "signal-util.h"
 #include "static-destruct.h"
 #include "strv.h"
 
@@ -205,12 +210,22 @@ static inline int run_test_table(void) {
         ({                                                                                                      \
                 typeof(expr) _result = (expr);                                                                  \
                 if (_result < 0) {                                                                              \
-                        log_error_errno(_result, "%s:%i: Assertion failed: %s: %m",                             \
+                        log_error_errno(_result, "%s:%i: Assertion failed: expected \"%s\" to succeed but got the following error: %m", \
                                         PROJECT_FILE, __LINE__, #expr);                                         \
                         abort();                                                                                \
                 }                                                                                               \
          })
 
+#define ASSERT_OK_ERRNO(expr)                                                                                   \
+        ({                                                                                                      \
+                typeof(expr) _result = (expr);                                                                  \
+                if (_result < 0) {                                                                              \
+                        log_error_errno(errno, "%s:%i: Assertion failed: expected \"%s\" to succeed but got the following error: %m", \
+                                        PROJECT_FILE, __LINE__, #expr);                                         \
+                        abort();                                                                                \
+                }                                                                                               \
+        })
+
 #define ASSERT_TRUE(expr)                                                                                       \
         ({                                                                                                      \
                 if (!(expr)) {                                                                                  \
@@ -231,9 +246,10 @@ static inline int run_test_table(void) {
 
 #define ASSERT_NULL(expr)                                                                                       \
         ({                                                                                                      \
-                if ((expr) != NULL) {                                                                           \
-                        log_error("%s:%i: Assertion failed: expected \"%s\" to be NULL",                        \
-                                  PROJECT_FILE, __LINE__, #expr);                                               \
+                typeof(expr) _result = (expr);                                                                  \
+                if (_result != NULL) {                                                                          \
+                        log_error("%s:%i: Assertion failed: expected \"%s\" to be NULL, but \"%p\" != NULL",    \
+                                  PROJECT_FILE, __LINE__, #expr, _result);                                      \
                         abort();                                                                                \
                 }                                                                                               \
         })
@@ -249,11 +265,10 @@ static inline int run_test_table(void) {
 
 #define ASSERT_STREQ(expr1, expr2)                                                                              \
         ({                                                                                                      \
-                const char* _expr1 = (expr1);                                                                   \
-                const char* _expr2 = (expr2);                                                                   \
-                if (strcmp(_expr1, _expr2) != 0) {                                                              \
+                const char *_expr1 = (expr1), *_expr2 = (expr2);                                                \
+                if (!streq_ptr(_expr1, _expr2)) {                                                               \
                         log_error("%s:%i: Assertion failed: expected \"%s == %s\", but \"%s != %s\"",           \
-                                  PROJECT_FILE, __LINE__, #expr1, #expr2, _expr1, _expr2);                      \
+                                  PROJECT_FILE, __LINE__, #expr1, #expr2, strnull(_expr1), strnull(_expr2));    \
                         abort();                                                                                \
                 }                                                                                               \
         })
@@ -350,3 +365,26 @@ static inline int run_test_table(void) {
                         abort();                                                                                \
                 }                                                                                               \
         })
+
+#define ASSERT_SIGNAL(expr, signal)                                                                             \
+        ({                                                                                                      \
+                ASSERT_TRUE(SIGNAL_VALID(signal));                                                              \
+                siginfo_t _siginfo = {};                                                                        \
+                int _pid = fork();                                                                              \
+                ASSERT_OK(_pid);                                                                                \
+                if (_pid == 0) {                                                                                \
+                        /* Speed things up by never even attempting to generate a coredump */                   \
+                        (void) prctl(PR_SET_DUMPABLE, 0);                                                       \
+                        /* But still set an rlimit just in case */                                              \
+                        (void) setrlimit(RLIMIT_CORE, &RLIMIT_MAKE_CONST(0));                                   \
+                        expr;                                                                                   \
+                        _exit(EXIT_SUCCESS);                                                                    \
+                }                                                                                               \
+                (void) wait_for_terminate(_pid, &_siginfo);                                                     \
+                if (_siginfo.si_status != signal) {                                                             \
+                        log_error("%s:%i: Assertion failed: \"%s\" died with signal %s, but %s was expected",   \
+                                  PROJECT_FILE, __LINE__, #expr, signal_to_string(_siginfo.si_status),          \
+                                  signal_to_string(signal));                                                    \
+                        abort();                                                                                \
+                }                                                                                               \
+        })
index 203cf5fb3adcd5a42eafb5b221c577399df212ed..42975cdb970e46bed36a055c51aedf6dcc181639 100644 (file)
@@ -668,6 +668,8 @@ int tpm2_context_new(const char *device, Tpm2Context **ret_context) {
                 if (!context->tcti_dl)
                         return log_debug_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "Failed to load %s: %s", fn, dlerror());
 
+                log_debug("Loaded '%s' via dlopen()", fn);
+
                 func = dlsym(context->tcti_dl, TSS2_TCTI_INFO_SYMBOL);
                 if (!func)
                         return log_debug_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
index 0014b7236f5a566c9cfcf0cd9293c8923afc5f9c..15996ca7247428e035133f313b152c768125afe9 100644 (file)
@@ -139,7 +139,7 @@ static int device_wait_for_initialization_internal(
         }
 
         if (device) {
-                if (sd_device_get_is_initialized(device) > 0) {
+                if (device_is_processed(device) > 0) {
                         if (ret)
                                 *ret = sd_device_ref(device);
                         return 0;
@@ -202,7 +202,7 @@ static int device_wait_for_initialization_internal(
                 if (r < 0 && !ERRNO_IS_DEVICE_ABSENT(r))
                         return log_error_errno(r, "Failed to create sd-device object from %s: %m", devlink);
         }
-        if (device && sd_device_get_is_initialized(device) > 0) {
+        if (device && device_is_processed(device) > 0) {
                 if (ret)
                         *ret = sd_device_ref(device);
                 return 0;
@@ -237,16 +237,27 @@ int device_is_renaming(sd_device *dev) {
         return r;
 }
 
-int device_is_processing(sd_device *dev) {
+int device_is_processed(sd_device *dev) {
         int r;
 
         assert(dev);
 
+        /* sd_device_get_is_initialized() only checks if the udev database file exists. However, even if the
+         * database file exist, systemd-udevd may be still processing the device, e.g. when the udev rules
+         * for the device have RUN tokens. See issue #30056. Hence, to check if the device is really
+         * processed by systemd-udevd, we also need to read ID_PROCESSING property. */
+
+        r = sd_device_get_is_initialized(dev);
+        if (r <= 0)
+                return r;
+
         r = device_get_property_bool(dev, "ID_PROCESSING");
         if (r == -ENOENT)
-                return false; /* defaults to false */
+                return true; /* If the property does not exist, then it means that the device is processed. */
+        if (r < 0)
+                return r;
 
-        return r;
+        return !r;
 }
 
 bool device_for_action(sd_device *dev, sd_device_action_t a) {
index 13710a3ec1f51e9ab8bac768ef49907c2d544c67..c21c4c158caa7c3d08dea13ce0bab0a1c1517a3a 100644 (file)
@@ -13,7 +13,7 @@ int udev_parse_config(void);
 int device_wait_for_initialization(sd_device *device, const char *subsystem, usec_t timeout_usec, sd_device **ret);
 int device_wait_for_devlink(const char *path, const char *subsystem, usec_t timeout_usec, sd_device **ret);
 int device_is_renaming(sd_device *dev);
-int device_is_processing(sd_device *dev);
+int device_is_processed(sd_device *dev);
 
 bool device_for_action(sd_device *dev, sd_device_action_t action);
 
index d262c82d63ba3be9eebb222a1148f63391a2b8a7..75dece3442935ae71f658f23105ff8f31d2e03c0 100644 (file)
@@ -1455,6 +1455,8 @@ int userdb_block_nss_systemd(int b) {
                 return 0;
         }
 
+        log_debug("Loaded '%s' via dlopen()", LIBDIR "/libnss_systemd.so.2");
+
         call = dlsym(dl, "_nss_systemd_block");
         if (!call)
                 /* If the file is installed but lacks the symbol we expect, things are weird, let's complain */
diff --git a/src/shared/varlink-io.systemd.MountFileSystem.c b/src/shared/varlink-io.systemd.MountFileSystem.c
new file mode 100644 (file)
index 0000000..4a33578
--- /dev/null
@@ -0,0 +1,69 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "varlink-io.systemd.MountFileSystem.h"
+
+static VARLINK_DEFINE_ENUM_TYPE(
+                PartitionDesignator,
+                VARLINK_DEFINE_ENUM_VALUE(root),
+                VARLINK_DEFINE_ENUM_VALUE(usr),
+                VARLINK_DEFINE_ENUM_VALUE(home),
+                VARLINK_DEFINE_ENUM_VALUE(srv),
+                VARLINK_DEFINE_ENUM_VALUE(esp),
+                VARLINK_DEFINE_ENUM_VALUE(xbootldr),
+                VARLINK_DEFINE_ENUM_VALUE(swap),
+                VARLINK_DEFINE_ENUM_VALUE(root_verity),
+                VARLINK_DEFINE_ENUM_VALUE(usr_verity),
+                VARLINK_DEFINE_ENUM_VALUE(root_verity_sig),
+                VARLINK_DEFINE_ENUM_VALUE(usr_verity_sig),
+                VARLINK_DEFINE_ENUM_VALUE(tmp),
+                VARLINK_DEFINE_ENUM_VALUE(var));
+
+static VARLINK_DEFINE_STRUCT_TYPE(
+                PartitionInfo,
+                VARLINK_DEFINE_FIELD(designator, VARLINK_STRING, 0),
+                VARLINK_DEFINE_FIELD(writable, VARLINK_BOOL, 0),
+                VARLINK_DEFINE_FIELD(growFileSystem, VARLINK_BOOL, 0),
+                VARLINK_DEFINE_FIELD(partitionNumber, VARLINK_INT, VARLINK_NULLABLE),
+                VARLINK_DEFINE_FIELD(architecture, VARLINK_STRING, VARLINK_NULLABLE),
+                VARLINK_DEFINE_FIELD(partitionUuid, VARLINK_STRING, VARLINK_NULLABLE),
+                VARLINK_DEFINE_FIELD(fileSystemType, VARLINK_STRING, 0),
+                VARLINK_DEFINE_FIELD(partitionLabel, VARLINK_STRING, VARLINK_NULLABLE),
+                VARLINK_DEFINE_FIELD(size, VARLINK_INT, 0),
+                VARLINK_DEFINE_FIELD(offset, VARLINK_INT, 0),
+                VARLINK_DEFINE_FIELD(mountFileDescriptor, VARLINK_INT, 0));
+
+static VARLINK_DEFINE_METHOD(
+                MountImage,
+                VARLINK_DEFINE_INPUT(imageFileDescriptor, VARLINK_INT, 0),
+                VARLINK_DEFINE_INPUT(userNamespaceFileDescriptor, VARLINK_INT, VARLINK_NULLABLE),
+                VARLINK_DEFINE_INPUT(readOnly, VARLINK_BOOL, VARLINK_NULLABLE),
+                VARLINK_DEFINE_INPUT(growFileSystems, VARLINK_BOOL, VARLINK_NULLABLE),
+                VARLINK_DEFINE_INPUT(password, VARLINK_STRING, VARLINK_NULLABLE),
+                VARLINK_DEFINE_INPUT(imagePolicy, VARLINK_STRING, VARLINK_NULLABLE),
+                VARLINK_DEFINE_INPUT(allowInteractiveAuthentication, VARLINK_BOOL, VARLINK_NULLABLE),
+                VARLINK_DEFINE_OUTPUT_BY_TYPE(partitions, PartitionInfo, VARLINK_ARRAY),
+                VARLINK_DEFINE_OUTPUT(imagePolicy, VARLINK_STRING, 0),
+                VARLINK_DEFINE_OUTPUT(imageSize, VARLINK_INT, 0),
+                VARLINK_DEFINE_OUTPUT(sectorSize, VARLINK_INT, 0),
+                VARLINK_DEFINE_OUTPUT(imageName, VARLINK_STRING, VARLINK_NULLABLE),
+                VARLINK_DEFINE_OUTPUT(imageUuid, VARLINK_STRING, VARLINK_NULLABLE));
+
+static VARLINK_DEFINE_ERROR(IncompatibleImage);
+static VARLINK_DEFINE_ERROR(MultipleRootPartitionsFound);
+static VARLINK_DEFINE_ERROR(RootPartitionNotFound);
+static VARLINK_DEFINE_ERROR(DeniedByImagePolicy);
+static VARLINK_DEFINE_ERROR(KeyNotFound);
+static VARLINK_DEFINE_ERROR(VerityFailure);
+
+VARLINK_DEFINE_INTERFACE(
+                io_systemd_MountFileSystem,
+                "io.systemd.MountFileSystem",
+                &vl_type_PartitionDesignator,
+                &vl_type_PartitionInfo,
+                &vl_method_MountImage,
+                &vl_error_IncompatibleImage,
+                &vl_error_MultipleRootPartitionsFound,
+                &vl_error_RootPartitionNotFound,
+                &vl_error_DeniedByImagePolicy,
+                &vl_error_KeyNotFound,
+                &vl_error_VerityFailure);
diff --git a/src/shared/varlink-io.systemd.MountFileSystem.h b/src/shared/varlink-io.systemd.MountFileSystem.h
new file mode 100644 (file)
index 0000000..dc75957
--- /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_MountFileSystem;
diff --git a/src/shared/varlink-io.systemd.NamespaceResource.c b/src/shared/varlink-io.systemd.NamespaceResource.c
new file mode 100644 (file)
index 0000000..e98c6c6
--- /dev/null
@@ -0,0 +1,62 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "varlink-io.systemd.NamespaceResource.h"
+
+static VARLINK_DEFINE_METHOD(
+                AllocateUserRange,
+                VARLINK_DEFINE_INPUT(name, VARLINK_STRING, 0),
+                VARLINK_DEFINE_INPUT(size, VARLINK_INT, 0),
+                VARLINK_DEFINE_INPUT(target, VARLINK_INT, VARLINK_NULLABLE),
+                VARLINK_DEFINE_INPUT(userNamespaceFileDescriptor, VARLINK_INT, 0));
+
+static VARLINK_DEFINE_METHOD(
+                RegisterUserNamespace,
+                VARLINK_DEFINE_INPUT(name, VARLINK_STRING, 0),
+                VARLINK_DEFINE_INPUT(userNamespaceFileDescriptor, VARLINK_INT, 0));
+
+static VARLINK_DEFINE_METHOD(
+                AddMountToUserNamespace,
+                VARLINK_DEFINE_INPUT(userNamespaceFileDescriptor, VARLINK_INT, 0),
+                VARLINK_DEFINE_INPUT(mountFileDescriptor, VARLINK_INT, 0));
+
+static VARLINK_DEFINE_METHOD(
+                AddControlGroupToUserNamespace,
+                VARLINK_DEFINE_INPUT(userNamespaceFileDescriptor, VARLINK_INT, 0),
+                VARLINK_DEFINE_INPUT(controlGroupFileDescriptor, VARLINK_INT, 0));
+
+static VARLINK_DEFINE_METHOD(
+                AddNetworkToUserNamespace,
+                VARLINK_DEFINE_INPUT(userNamespaceFileDescriptor, VARLINK_INT, 0),
+                VARLINK_DEFINE_INPUT(networkNamespaceFileDescriptor, VARLINK_INT, 0),
+                VARLINK_DEFINE_INPUT(namespaceInterfaceName, VARLINK_STRING, VARLINK_NULLABLE),
+                VARLINK_DEFINE_INPUT(mode, VARLINK_STRING, 0),
+                VARLINK_DEFINE_OUTPUT(hostInterfaceName, VARLINK_STRING, 0),
+                VARLINK_DEFINE_OUTPUT(namespaceInterfaceName, VARLINK_STRING, 0));
+
+static VARLINK_DEFINE_ERROR(UserNamespaceInterfaceNotSupported);
+static VARLINK_DEFINE_ERROR(NameExists);
+static VARLINK_DEFINE_ERROR(UserNamespaceExists);
+static VARLINK_DEFINE_ERROR(DynamicRangeUnavailable);
+static VARLINK_DEFINE_ERROR(NoDynamicRange);
+static VARLINK_DEFINE_ERROR(UserNamespaceNotRegistered);
+static VARLINK_DEFINE_ERROR(UserNamespaceWithoutUserRange);
+static VARLINK_DEFINE_ERROR(TooManyControlGroups);
+static VARLINK_DEFINE_ERROR(ControlGroupAlreadyAdded);
+
+VARLINK_DEFINE_INTERFACE(
+                io_systemd_NamespaceResource,
+                "io.systemd.NamespaceResource",
+                &vl_method_AllocateUserRange,
+                &vl_method_RegisterUserNamespace,
+                &vl_method_AddMountToUserNamespace,
+                &vl_method_AddControlGroupToUserNamespace,
+                &vl_method_AddNetworkToUserNamespace,
+                &vl_error_UserNamespaceInterfaceNotSupported,
+                &vl_error_NameExists,
+                &vl_error_UserNamespaceExists,
+                &vl_error_DynamicRangeUnavailable,
+                &vl_error_NoDynamicRange,
+                &vl_error_UserNamespaceNotRegistered,
+                &vl_error_UserNamespaceWithoutUserRange,
+                &vl_error_TooManyControlGroups,
+                &vl_error_ControlGroupAlreadyAdded);
diff --git a/src/shared/varlink-io.systemd.NamespaceResource.h b/src/shared/varlink-io.systemd.NamespaceResource.h
new file mode 100644 (file)
index 0000000..443cb97
--- /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_NamespaceResource;
index d4072ec398281508392fd0b31905f835a72cff8a..713aff289597929523a85a810f17c4653eed2950 100644 (file)
@@ -2807,12 +2807,29 @@ int varlink_get_peer_uid(Varlink *v, uid_t *ret) {
                 return varlink_log_errno(v, r, "Failed to acquire credentials: %m");
 
         if (!uid_is_valid(v->ucred.uid))
-                return varlink_log_errno(v, SYNTHETIC_ERRNO(ENODATA), "Peer uid is invalid.");
+                return varlink_log_errno(v, SYNTHETIC_ERRNO(ENODATA), "Peer UID is invalid.");
 
         *ret = v->ucred.uid;
         return 0;
 }
 
+int varlink_get_peer_gid(Varlink *v, gid_t *ret) {
+        int r;
+
+        assert_return(v, -EINVAL);
+        assert_return(ret, -EINVAL);
+
+        r = varlink_acquire_ucred(v);
+        if (r < 0)
+                return varlink_log_errno(v, r, "Failed to acquire credentials: %m");
+
+        if (!gid_is_valid(v->ucred.gid))
+                return varlink_log_errno(v, SYNTHETIC_ERRNO(ENODATA), "Peer GID is invalid.");
+
+        *ret = v->ucred.gid;
+        return 0;
+}
+
 int varlink_get_peer_pid(Varlink *v, pid_t *ret) {
         int r;
 
@@ -3076,7 +3093,7 @@ int varlink_push_fd(Varlink *v, int fd) {
         return i;
 }
 
-int varlink_dup_fd(Varlink *v, int fd) {
+int varlink_push_dup_fd(Varlink *v, int fd) {
         _cleanup_close_ int dp = -1;
         int r;
 
@@ -3124,6 +3141,16 @@ int varlink_peek_fd(Varlink *v, size_t i) {
         return v->input_fds[i];
 }
 
+int varlink_peek_dup_fd(Varlink *v, size_t i) {
+        int fd;
+
+        fd = varlink_peek_fd(v, i);
+        if (fd < 0)
+                return fd;
+
+        return RET_NERRNO(fcntl(fd, F_DUPFD_CLOEXEC, 3));
+}
+
 int varlink_take_fd(Varlink *v, size_t i) {
         assert_return(v, -EINVAL);
 
index db7227b215472a03c439855af8fb26da6f4724c5..f6dda635494aca152585f72cc338d2f480b71995 100644 (file)
@@ -156,11 +156,12 @@ int varlink_dispatch(Varlink *v, JsonVariant *parameters, const JsonDispatch tab
 
 /* Write outgoing fds into the socket (to be associated with the next enqueued message) */
 int varlink_push_fd(Varlink *v, int fd);
-int varlink_dup_fd(Varlink *v, int fd);
+int varlink_push_dup_fd(Varlink *v, int fd);
 int varlink_reset_fds(Varlink *v);
 
 /* Read incoming fds from the socket (associated with the currently handled message) */
 int varlink_peek_fd(Varlink *v, size_t i);
+int varlink_peek_dup_fd(Varlink *v, size_t i);
 int varlink_take_fd(Varlink *v, size_t i);
 
 int varlink_set_allow_fd_passing_input(Varlink *v, bool b);
@@ -173,6 +174,7 @@ void* varlink_set_userdata(Varlink *v, void *userdata);
 void* varlink_get_userdata(Varlink *v);
 
 int varlink_get_peer_uid(Varlink *v, uid_t *ret);
+int varlink_get_peer_gid(Varlink *v, gid_t *ret);
 int varlink_get_peer_pid(Varlink *v, pid_t *ret);
 int varlink_get_peer_pidref(Varlink *v, PidRef *ret);
 
index 6f15a7070d176463a4065f292dac43534f7e1ba4..1870fb97ef7ed6aad4ea5306680d423f6ac48fb9 100644 (file)
@@ -141,8 +141,7 @@ static int pin_choice(
         assert(toplevel_fd >= 0 || toplevel_fd == AT_FDCWD);
         assert(inode_path);
         assert(filter);
-
-        toplevel_path = strempty(toplevel_path);
+        assert(ret);
 
         if (inode_fd < 0 || FLAGS_SET(flags, PICK_RESOLVE)) {
                 r = chaseat(toplevel_fd,
@@ -271,8 +270,7 @@ static int make_choice(
         assert(toplevel_fd >= 0 || toplevel_fd == AT_FDCWD);
         assert(inode_path);
         assert(filter);
-
-        toplevel_path = strempty(toplevel_path);
+        assert(ret);
 
         if (inode_fd < 0) {
                 r = chaseat(toplevel_fd, inode_path, CHASE_AT_RESOLVE_IN_ROOT, NULL, &inode_fd);
@@ -486,12 +484,13 @@ static int make_choice(
                         ret);
 }
 
-int path_pick(const char *toplevel_path,
-              int toplevel_fd,
-              const char *path,
-              const PickFilter *filter,
-              PickFlags flags,
-              PickResult *ret) {
+int path_pick(
+                const char *toplevel_path,
+                int toplevel_fd,
+                const char *path,
+                const PickFilter *filter,
+                PickFlags flags,
+                PickResult *ret) {
 
         _cleanup_free_ char *filter_bname = NULL, *dir = NULL, *parent = NULL, *fname = NULL;
         const char *filter_suffix, *enumeration_path;
@@ -500,8 +499,8 @@ int path_pick(const char *toplevel_path,
 
         assert(toplevel_fd >= 0 || toplevel_fd == AT_FDCWD);
         assert(path);
-
-        toplevel_path = strempty(toplevel_path);
+        assert(filter);
+        assert(ret);
 
         /* Given a path, resolve .v/ subdir logic (if used!), and returns the choice made. This supports
          * three ways to be called:
@@ -647,6 +646,7 @@ int path_pick_update_warn(
 
         assert(path);
         assert(*path);
+        assert(filter);
 
         /* This updates the first argument if needed! */
 
@@ -658,7 +658,9 @@ int path_pick_update_warn(
                       &result);
         if (r == -ENOENT) {
                 log_debug("Path '%s' doesn't exist, leaving as is.", *path);
-                *ret_result = PICK_RESULT_NULL;
+
+                if (ret_result)
+                        *ret_result = PICK_RESULT_NULL;
                 return 0;
         }
         if (r < 0)
index 591852e986fc27299865eec5c2a83b33f8358497..21ce6684041efcaa02e58b744fde5432bf47801f 100644 (file)
@@ -42,12 +42,13 @@ typedef struct PickResult {
 
 void pick_result_done(PickResult *p);
 
-int path_pick(const char *toplevel_path,
-              int toplevel_fd,
-              const char *path,
-              const PickFilter *filter,
-              PickFlags flags,
-              PickResult *ret);
+int path_pick(
+                const char *toplevel_path,
+                int toplevel_fd,
+                const char *path,
+                const PickFilter *filter,
+                PickFlags flags,
+                PickResult *ret);
 
 int path_pick_update_warn(
                 char **path,
index 2d79f7147a8cef9750045ade46a558191d4154a6..99ccefb2273ea42e920f8b15b9b9685439d3b460 100644 (file)
@@ -95,7 +95,7 @@ static int set_pretimeout_governor(const char *governor) {
                               governor,
                               WRITE_STRING_FILE_DISABLE_BUFFER | WRITE_STRING_FILE_VERIFY_ON_FAILURE | WRITE_STRING_FILE_VERIFY_IGNORE_NEWLINE);
         if (r < 0)
-                return log_error_errno(r, "Failed to set pretimeout_governor to '%s': %m", governor);
+                return log_error_errno(r, "Failed to set watchdog pretimeout_governor to '%s': %m", governor);
 
         return r;
 }
@@ -157,7 +157,7 @@ static int watchdog_read_pretimeout(void) {
 
         if (ioctl(watchdog_fd, WDIOC_GETPRETIMEOUT, &sec) < 0) {
                 watchdog_pretimeout = 0;
-                return log_full_errno(ERRNO_IS_NOT_SUPPORTED(errno) ? LOG_DEBUG : LOG_WARNING, errno, "Failed to get pretimeout value, ignoring: %m");
+                return log_full_errno(ERRNO_IS_NOT_SUPPORTED(errno) ? LOG_DEBUG : LOG_WARNING, errno, "Failed to get watchdog pretimeout value, ignoring: %m");
         }
 
         watchdog_pretimeout = sec * USEC_PER_SEC;
@@ -181,7 +181,7 @@ static int watchdog_set_pretimeout(void) {
                         return 0;
                 }
 
-                return log_error_errno(errno, "Failed to set pretimeout to %s: %m", FORMAT_TIMESPAN(sec, USEC_PER_SEC));
+                return log_error_errno(errno, "Failed to set watchdog pretimeout to %s: %m", FORMAT_TIMESPAN(sec, USEC_PER_SEC));
         }
 
         /* The set ioctl does not return the actual value set so get it now. */
@@ -274,10 +274,10 @@ static int update_timeout(void) {
                 r = watchdog_set_timeout();
                 if (r < 0) {
                         if (!ERRNO_IS_NOT_SUPPORTED(r))
-                                return log_error_errno(r, "Failed to set timeout to %s: %m",
+                                return log_error_errno(r, "Failed to set watchdog hardware timeout to %s: %m",
                                                        FORMAT_TIMESPAN(watchdog_timeout, 0));
 
-                        log_info("Modifying watchdog timeout is not supported, reusing the programmed timeout.");
+                        log_info("Modifying watchdog hardware timeout is not supported, reusing the programmed timeout.");
                         watchdog_timeout = USEC_INFINITY;
                 }
         }
@@ -286,8 +286,8 @@ static int update_timeout(void) {
                 r = watchdog_read_timeout();
                 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.");
+                                return log_error_errno(r, "Failed to query watchdog hardware timeout: %m");
+                        log_info("Reading watchdog hardware timeout is not supported, reusing the configured timeout.");
                         watchdog_timeout = previous_timeout;
                 }
         }
@@ -302,7 +302,7 @@ static int update_timeout(void) {
         if (r < 0)
                 return r;
 
-        log_info("Watchdog running with a timeout of %s.", FORMAT_TIMESPAN(watchdog_timeout, 0));
+        log_info("Watchdog running with a hardware timeout of %s.", FORMAT_TIMESPAN(watchdog_timeout, 0));
 
         return watchdog_ping_now();
 }
index 0f619ce0f5fc22d7ef233c2dcc89f1146aba5012..c96207428dc5da10eca9a924cc6493a90f422d93 100644 (file)
@@ -151,22 +151,22 @@ static int write_state(int fd, char * const *states) {
         return r;
 }
 
-static int write_mode(char * const *modes) {
-        int r = 0;
+static int write_mode(const char *path, char * const *modes) {
+        int r, ret = 0;
 
-        STRV_FOREACH(mode, modes) {
-                int k;
+        assert(path);
 
-                k = write_string_file("/sys/power/disk", *mode, WRITE_STRING_FILE_DISABLE_BUFFER);
-                if (k >= 0) {
-                        log_debug("Using sleep disk mode '%s'.", *mode);
+        STRV_FOREACH(mode, modes) {
+                r = write_string_file(path, *mode, WRITE_STRING_FILE_DISABLE_BUFFER);
+                if (r >= 0) {
+                        log_debug("Using sleep mode '%s' for %s.", *mode, path);
                         return 0;
                 }
 
-                RET_GATHER(r, log_debug_errno(k, "Failed to write '%s' to /sys/power/disk: %m", *mode));
+                RET_GATHER(ret, log_debug_errno(r, "Failed to write '%s' to %s: %m", *mode, path));
         }
 
-        return r;
+        return ret;
 }
 
 static int lock_all_homes(void) {
@@ -237,8 +237,14 @@ static int execute(
         if (state_fd < 0)
                 return log_error_errno(errno, "Failed to open /sys/power/state: %m");
 
+        if (SLEEP_NEEDS_MEM_SLEEP(sleep_config, operation)) {
+                r = write_mode("/sys/power/mem_sleep", sleep_config->mem_modes);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to write mode to /sys/power/mem_sleep: %m");
+        }
+
         /* Configure hibernation settings if we are supposed to hibernate */
-        if (sleep_operation_is_hibernation(operation)) {
+        if (SLEEP_OPERATION_IS_HIBERNATION(operation)) {
                 _cleanup_(hibernation_device_done) HibernationDevice hibernation_device = {};
                 bool resume_set;
 
@@ -259,7 +265,7 @@ static int execute(
                                 goto fail;
                 }
 
-                r = write_mode(sleep_config->modes[operation]);
+                r = write_mode("/sys/power/disk", sleep_config->modes[operation]);
                 if (r < 0) {
                         log_error_errno(r, "Failed to write mode to /sys/power/disk: %m");
                         goto fail;
@@ -301,8 +307,8 @@ static int execute(
                 return 0;
 
 fail:
-        if (sleep_operation_is_hibernation(operation))
-                clear_efi_hibernate_location_and_warn();
+        if (SLEEP_OPERATION_IS_HIBERNATION(operation))
+                (void) clear_efi_hibernate_location_and_warn();
 
         return r;
 }
index fad95b389779fbbddf3f246cd60b5907a8248277..98430348a7bf7fddf1cf4cbe78abc2e52a68ccf4 100644 (file)
@@ -23,5 +23,6 @@
 #AllowHybridSleep=yes
 #SuspendState=mem standby freeze
 #HibernateMode=platform shutdown
+#MemorySleepMode=
 #HibernateDelaySec=
 #SuspendEstimationSec=60min
index 5e037835435501060e8292b01b11ffa984e64800..3b9987b581f65210f73bf5e7ef556a1c0da34cf3 100644 (file)
@@ -401,10 +401,8 @@ static int parse_credentials(void) {
         int r;
 
         r = read_credential_with_decryption("ssh.listen", (void*) &b, &sz);
-        if (r < 0)
+        if (r <= 0)
                 return r;
-        if (r == 0)
-                return 0;
 
         _cleanup_fclose_ FILE *f = NULL;
         f = fmemopen_unlocked(b, sz, "r");
index 62b78a350fa049deb54a0ba4e0477ee8b651c415..e0609988293705c21d368d413089c770c8efd723 100644 (file)
@@ -1214,8 +1214,11 @@ static int run(int argc, char* argv[]) {
                 if (r < 0)
                         return log_error_errno(r, "Failed to exclude loop devices: %m");
 
-                FOREACH_DEVICE(enumerator, device)
+                FOREACH_DEVICE(enumerator, device) {
+                        if (device_is_processed(device) <= 0)
+                                continue;
                         device_added(&context, device);
+                }
         }
 
         _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
index b984e9b9a0742238c6b8ab65903af9a57fd4ef0e..79b016f9c3a517766be9db1fd96156aeabe08d8c 100644 (file)
@@ -748,8 +748,7 @@ static OverlayFSPaths *overlayfs_paths_free(OverlayFSPaths *op) {
         free(op->work_dir);
         strv_free(op->lower_dirs);
 
-        free(op);
-        return NULL;
+        return mfree(op);
 }
 DEFINE_TRIVIAL_CLEANUP_FUNC(OverlayFSPaths *, overlayfs_paths_free);
 
index 257c362cea47656fd9e8cc424b4241d768658a97..88f73bf502a7f79431a2e484c75f4dc7d7505e88 100644 (file)
@@ -44,8 +44,7 @@ if get_option('link-systemctl-shared')
         systemctl_link_with = [libshared]
 else
         systemctl_link_with = [libsystemd_static,
-                               libshared_static,
-                               libbasic_gcrypt]
+                               libshared_static]
 endif
 
 executables += [
index 2e35413b5fd2ccacbb6f3affa196a1700bcf2f27..1147c96683d8f0fe7b9b835fd9b3381c64304583 100644 (file)
@@ -7,6 +7,7 @@
 #include "bus-error.h"
 #include "bus-locator.h"
 #include "login-util.h"
+#include "mountpoint-util.h"
 #include "process-util.h"
 #include "systemctl-logind.h"
 #include "systemctl-start-unit.h"
@@ -84,9 +85,12 @@ int logind_reboot(enum action a) {
         SET_FLAG(flags,
                  SD_LOGIND_REBOOT_VIA_KEXEC,
                  a == ACTION_KEXEC || (a == ACTION_REBOOT && getenv_bool("SYSTEMCTL_SKIP_AUTO_KEXEC") <= 0));
+        /* Try to soft-reboot if /run/nextroot/ is a valid OS tree, but only if it's also a mount point.
+         * Otherwise, if people store new rootfs directly on /run/ tmpfs, 'systemctl reboot' would always
+         * soft-reboot, as /run/nextroot/ can never go away. */
         SET_FLAG(flags,
                  SD_LOGIND_SOFT_REBOOT_IF_NEXTROOT_SET_UP,
-                 a == ACTION_REBOOT && getenv_bool("SYSTEMCTL_SKIP_AUTO_SOFT_REBOOT") <= 0);
+                 a == ACTION_REBOOT && getenv_bool("SYSTEMCTL_SKIP_AUTO_SOFT_REBOOT") <= 0 && path_is_mount_point("/run/nextroot") > 0);
         SET_FLAG(flags, SD_LOGIND_SOFT_REBOOT, a == ACTION_SOFT_REBOOT);
 
         r = bus_call_method(bus, bus_login_mgr, method_with_flags, &error, NULL, "t", flags);
index a91d7064bbef970cb2bbf776f683ec3ff92777f3..de58bff334c51789e5e276557ad8ecbe8a7f6fd7 100644 (file)
@@ -38,8 +38,11 @@ _not_installed_headers = [
         'sd-lldp-tx.h',
         'sd-lldp.h',
         'sd-ndisc.h',
+        'sd-ndisc-neighbor.h',
         'sd-ndisc-protocol.h',
+        'sd-ndisc-redirect.h',
         'sd-ndisc-router.h',
+        'sd-ndisc-router-solicit.h',
         'sd-netlink.h',
         'sd-network.h',
         'sd-radv.h',
index 5e80ea895d4eba6941e80f256cf480f17a9918e5..d06c5c30155465ed301554fb7a78b3c35005325a 100644 (file)
@@ -208,6 +208,7 @@ struct sd_bus_vtable {
                                 _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, \
                                 NAME, ...) NAME
 
+#define _SD_VARARGS_FOREACH_EVEN_00(FN)
 #define _SD_VARARGS_FOREACH_EVEN_01(FN, X)         FN(X)
 #define _SD_VARARGS_FOREACH_EVEN_02(FN, X, Y)      FN(X)
 #define _SD_VARARGS_FOREACH_EVEN_04(FN, X, Y, ...) FN(X) _SD_VARARGS_FOREACH_EVEN_02(FN, __VA_ARGS__)
@@ -261,9 +262,11 @@ struct sd_bus_vtable {
                                 _SD_VARARGS_FOREACH_EVEN_08, _SD_VARARGS_FOREACH_EVEN_07, \
                                 _SD_VARARGS_FOREACH_EVEN_06, _SD_VARARGS_FOREACH_EVEN_05, \
                                 _SD_VARARGS_FOREACH_EVEN_04, _SD_VARARGS_FOREACH_EVEN_03, \
-                                _SD_VARARGS_FOREACH_EVEN_02, _SD_VARARGS_FOREACH_EVEN_01) \
+                                _SD_VARARGS_FOREACH_EVEN_02, _SD_VARARGS_FOREACH_EVEN_01, \
+                                _SD_VARARGS_FOREACH_EVEN_00)                              \
                                 (FN, __VA_ARGS__)
 
+#define _SD_VARARGS_FOREACH_ODD_00(FN)
 #define _SD_VARARGS_FOREACH_ODD_01(FN, X)
 #define _SD_VARARGS_FOREACH_ODD_02(FN, X, Y)      FN(Y)
 #define _SD_VARARGS_FOREACH_ODD_04(FN, X, Y, ...) FN(Y) _SD_VARARGS_FOREACH_ODD_02(FN, __VA_ARGS__)
@@ -317,7 +320,8 @@ struct sd_bus_vtable {
                                 _SD_VARARGS_FOREACH_ODD_08, _SD_VARARGS_FOREACH_ODD_07, \
                                 _SD_VARARGS_FOREACH_ODD_06, _SD_VARARGS_FOREACH_ODD_05, \
                                 _SD_VARARGS_FOREACH_ODD_04, _SD_VARARGS_FOREACH_ODD_03, \
-                                _SD_VARARGS_FOREACH_ODD_02, _SD_VARARGS_FOREACH_ODD_01) \
+                                _SD_VARARGS_FOREACH_ODD_02, _SD_VARARGS_FOREACH_ODD_01, \
+                                _SD_VARARGS_FOREACH_ODD_00)                             \
                                 (FN, __VA_ARGS__)
 
 #define SD_BUS_ARGS(...) __VA_ARGS__
diff --git a/src/systemd/sd-ndisc-neighbor.h b/src/systemd/sd-ndisc-neighbor.h
new file mode 100644 (file)
index 0000000..2ea0337
--- /dev/null
@@ -0,0 +1,50 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#ifndef foosdndiscneighborfoo
+#define foosdndiscneighborfoo
+
+/***
+  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.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <https://www.gnu.org/licenses/>.
+***/
+
+#include <inttypes.h>
+#include <net/ethernet.h>
+#include <netinet/in.h>
+#include <sys/types.h>
+#include <time.h>
+
+#include "_sd-common.h"
+
+_SD_BEGIN_DECLARATIONS;
+
+typedef struct sd_ndisc_neighbor sd_ndisc_neighbor;
+
+sd_ndisc_neighbor *sd_ndisc_neighbor_ref(sd_ndisc_neighbor *na);
+sd_ndisc_neighbor *sd_ndisc_neighbor_unref(sd_ndisc_neighbor *na);
+_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_ndisc_neighbor, sd_ndisc_neighbor_unref);
+
+int sd_ndisc_neighbor_get_sender_address(sd_ndisc_neighbor *na, struct in6_addr *ret);
+/* RFC 4861 section 4.4:
+ * For solicited advertisements, the Target Address field in the Neighbor Solicitation message that prompted
+ * this advertisement. For an unsolicited advertisement, the address whose link-layer address has changed.
+ * The Target Address MUST NOT be a multicast address. */
+int sd_ndisc_neighbor_get_target_address(sd_ndisc_neighbor *na, struct in6_addr *ret);
+int sd_ndisc_neighbor_get_target_mac(sd_ndisc_neighbor *na, struct ether_addr *ret);
+int sd_ndisc_neighbor_get_flags(sd_ndisc_neighbor *na, uint32_t *ret);
+int sd_ndisc_neighbor_is_router(sd_ndisc_neighbor *na);
+int sd_ndisc_neighbor_is_solicited(sd_ndisc_neighbor *na);
+int sd_ndisc_neighbor_is_override(sd_ndisc_neighbor *na);
+
+_SD_END_DECLARATIONS;
+
+#endif
diff --git a/src/systemd/sd-ndisc-redirect.h b/src/systemd/sd-ndisc-redirect.h
new file mode 100644 (file)
index 0000000..60b43f7
--- /dev/null
@@ -0,0 +1,46 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#ifndef foosdndiscredirectfoo
+#define foosdndiscredirectfoo
+
+/***
+  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.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <https://www.gnu.org/licenses/>.
+***/
+
+#include <inttypes.h>
+#include <net/ethernet.h>
+#include <netinet/in.h>
+#include <netinet/ip6.h>
+#include <sys/types.h>
+#include <time.h>
+
+#include "_sd-common.h"
+
+_SD_BEGIN_DECLARATIONS;
+
+typedef struct sd_ndisc_redirect sd_ndisc_redirect;
+
+sd_ndisc_redirect* sd_ndisc_redirect_ref(sd_ndisc_redirect *na);
+sd_ndisc_redirect* sd_ndisc_redirect_unref(sd_ndisc_redirect *na);
+_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_ndisc_redirect, sd_ndisc_redirect_unref);
+
+int sd_ndisc_redirect_set_sender_address(sd_ndisc_redirect *rd, const struct in6_addr *addr);
+int sd_ndisc_redirect_get_sender_address(sd_ndisc_redirect *na, struct in6_addr *ret);
+int sd_ndisc_redirect_get_target_address(sd_ndisc_redirect *na, struct in6_addr *ret);
+int sd_ndisc_redirect_get_destination_address(sd_ndisc_redirect *na, struct in6_addr *ret);
+int sd_ndisc_redirect_get_target_mac(sd_ndisc_redirect *na, struct ether_addr *ret);
+int sd_ndisc_redirect_get_redirected_header(sd_ndisc_redirect *na, struct ip6_hdr *ret);
+
+_SD_END_DECLARATIONS;
+
+#endif
diff --git a/src/systemd/sd-ndisc-router-solicit.h b/src/systemd/sd-ndisc-router-solicit.h
new file mode 100644 (file)
index 0000000..ff8c903
--- /dev/null
@@ -0,0 +1,40 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#ifndef foosdndiscroutersolicitfoo
+#define foosdndiscroutersolicitfoo
+
+/***
+  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.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <https://www.gnu.org/licenses/>.
+***/
+
+#include <inttypes.h>
+#include <net/ethernet.h>
+#include <netinet/in.h>
+#include <sys/types.h>
+
+#include "_sd-common.h"
+
+_SD_BEGIN_DECLARATIONS;
+
+typedef struct sd_ndisc_router_solicit sd_ndisc_router_solicit;
+
+sd_ndisc_router_solicit *sd_ndisc_router_solicit_ref(sd_ndisc_router_solicit *rs);
+sd_ndisc_router_solicit *sd_ndisc_router_solicit_unref(sd_ndisc_router_solicit *rs);
+_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_ndisc_router_solicit, sd_ndisc_router_solicit_unref);
+
+int sd_ndisc_router_solicit_get_sender_address(sd_ndisc_router_solicit *rs, struct in6_addr *ret);
+int sd_ndisc_router_solicit_get_sender_mac(sd_ndisc_router_solicit *rs, struct ether_addr *ret);
+
+_SD_END_DECLARATIONS;
+
+#endif
index 32ee5796e634e04a84d018539c05f26c84d1edc1..b07fefba3b9e4415b913c74d2590afd7a7627210 100644 (file)
@@ -33,6 +33,7 @@ sd_ndisc_router *sd_ndisc_router_ref(sd_ndisc_router *rt);
 sd_ndisc_router *sd_ndisc_router_unref(sd_ndisc_router *rt);
 _SD_DEFINE_POINTER_CLEANUP_FUNC(sd_ndisc_router, sd_ndisc_router_unref);
 
+int sd_ndisc_router_set_sender_address(sd_ndisc_router *rt, const struct in6_addr *addr);
 int sd_ndisc_router_get_sender_address(sd_ndisc_router *rt, struct in6_addr *ret);
 int sd_ndisc_router_get_timestamp(sd_ndisc_router *rt, clockid_t clock, uint64_t *ret);
 
index 5f4f6caf8d2e5259c260dc5ef9f2c09947c4b0ee..cce592f9e38d2bda1c85617c925e00a21782e0c9 100644 (file)
@@ -26,7 +26,9 @@
 #include <sys/types.h>
 
 #include "sd-event.h"
+#include "sd-ndisc-neighbor.h"
 #include "sd-ndisc-protocol.h"
+#include "sd-ndisc-redirect.h"
 #include "sd-ndisc-router.h"
 
 #include "_sd-common.h"
@@ -38,6 +40,8 @@ typedef struct sd_ndisc sd_ndisc;
 __extension__ typedef enum sd_ndisc_event_t {
         SD_NDISC_EVENT_TIMEOUT,
         SD_NDISC_EVENT_ROUTER,
+        SD_NDISC_EVENT_NEIGHBOR,
+        SD_NDISC_EVENT_REDIRECT,
         _SD_NDISC_EVENT_MAX,
         _SD_NDISC_EVENT_INVALID = -EINVAL,
         _SD_ENUM_FORCE_S64(NDISC_EVENT)
index 8ea0838ee654bd00820b107e55ec8053b57780d8..79cbb8975161eb9a09ed61a89bf696fe844097e5 100644 (file)
@@ -26,7 +26,8 @@
 
 #include "_sd-common.h"
 #include "sd-event.h"
-#include "sd-ndisc.h"
+#include "sd-ndisc-protocol.h"
+#include "sd-ndisc-router-solicit.h"
 
 _SD_BEGIN_DECLARATIONS;
 
index b350f83065e2347d78154c56e81037fa32a8beaa..435c38020e57d7191b86aeaa7c5f601bbe2338d3 100644 (file)
@@ -532,6 +532,7 @@ int transfer_read_definition(Transfer *t, const char *path) {
                                   "Target path is not a normalized, absolute path: %s", t->target.path);
 
         if (strv_isempty(t->target.patterns)) {
+                log_syntax(NULL, LOG_INFO, path, 1, 0, "Target specification lacks MatchPattern= expression. Assuming same value as in source specification.");
                 strv_free(t->target.patterns);
                 t->target.patterns = strv_copy(t->source.patterns);
                 if (!t->target.patterns)
index b55383ee39afff0a8457917acd1a90c31dcaef7f..9565b68e0502deb2af5dc616c8cdc7633d219777 100644 (file)
@@ -676,7 +676,7 @@ static int context_vacuum(
         if (space == 0)
                 log_info("Making room%s", special_glyph(SPECIAL_GLYPH_ELLIPSIS));
         else
-                log_info("Making room for %" PRIu64 " updates%s", space,special_glyph(SPECIAL_GLYPH_ELLIPSIS));
+                log_info("Making room for %" PRIu64 " updates%s", space, special_glyph(SPECIAL_GLYPH_ELLIPSIS));
 
         for (size_t i = 0; i < c->n_transfers; i++) {
                 r = transfer_vacuum(c->transfers[i], space, extra_protected_version);
index fcb291d02c2e918c1474d8c8f6f1f61b5a0ff137..0f9c067d50607848cd8e64905e9f2ae8306bbe6a 100644 (file)
@@ -15,7 +15,6 @@ executables += [
                 'c_args' : '-DSTANDALONE',
                 'link_with' : [
                         libbasic,
-                        libbasic_gcrypt,
                         libshared_static,
                         libsystemd_static,
                 ],
index 571d966ca0049b7c49cfd493444b0ec672790902..6419edda4905dc4547b028cdda2956b0e0a637b7 100644 (file)
@@ -265,7 +265,10 @@ executables += [
         },
         test_template + {
                 'sources' : files('test-dlopen-so.c'),
-                'dependencies' : libp11kit_cflags
+                'dependencies' : [
+                        libp11kit_cflags,
+                        libkmod_cflags,
+                ],
         },
         test_template + {
                 # only static linking apart from libdl, to make sure that the
@@ -335,7 +338,7 @@ executables += [
         },
         test_template + {
                 'sources' : files('test-netlink-manual.c'),
-                'dependencies' : libkmod,
+                'dependencies' : libkmod_cflags,
                 'conditions' : ['HAVE_KMOD'],
                 'type' : 'manual',
         },
index 331faf1e4b304fbb2cd9420d7ef4b9aaa67c8006..0cc9afcf340ea810a97305fec88b22f85176c2e2 100644 (file)
@@ -10,6 +10,7 @@
 #include "fd-util.h"
 #include "format-util.h"
 #include "fs-util.h"
+#include "path-util.h"
 #include "string-util.h"
 #include "tests.h"
 #include "tmpfile-util.h"
@@ -22,6 +23,12 @@ TEST_RET(add_acls_for_user) {
         uid_t uid;
         int r;
 
+        FOREACH_STRING(s, "capsh", "getfacl", "ls") {
+                r = find_executable(s, NULL);
+                if (r < 0)
+                        return log_tests_skipped_errno(r, "Could not find %s binary: %m", s);
+        }
+
         fd = mkostemp_safe(fn);
         assert_se(fd >= 0);
 
@@ -69,11 +76,18 @@ TEST_RET(add_acls_for_user) {
         return 0;
 }
 
-TEST(fd_acl_make_read_only) {
+TEST_RET(fd_acl_make_read_only) {
         _cleanup_(unlink_tempfilep) char fn[] = "/tmp/test-empty.XXXXXX";
         _cleanup_close_ int fd = -EBADF;
         const char *cmd;
         struct stat st;
+        int r;
+
+        FOREACH_STRING(s, "capsh", "getfacl", "ls", "stat") {
+                r = find_executable(s, NULL);
+                if (r < 0)
+                        return log_tests_skipped_errno(r, "Could not find %s binary: %m", s);
+        }
 
         fd = mkostemp_safe(fn);
         assert_se(fd >= 0);
@@ -81,7 +95,7 @@ TEST(fd_acl_make_read_only) {
         /* make it more exciting */
         (void) fd_add_uid_acl_permission(fd, 1, ACL_READ|ACL_WRITE|ACL_EXECUTE);
 
-        assert_se(fstat(fd, &st) >= 0);
+        ASSERT_OK_ERRNO(fstat(fd, &st));
         assert_se(FLAGS_SET(st.st_mode, 0200));
 
         cmd = strjoina("getfacl -p ", fn);
@@ -93,7 +107,7 @@ TEST(fd_acl_make_read_only) {
         log_info("read-only");
         assert_se(fd_acl_make_read_only(fd));
 
-        assert_se(fstat(fd, &st) >= 0);
+        ASSERT_OK_ERRNO(fstat(fd, &st));
         assert_se((st.st_mode & 0222) == 0000);
 
         cmd = strjoina("getfacl -p ", fn);
@@ -105,7 +119,7 @@ TEST(fd_acl_make_read_only) {
         log_info("writable");
         assert_se(fd_acl_make_writable(fd));
 
-        assert_se(fstat(fd, &st) >= 0);
+        ASSERT_OK_ERRNO(fstat(fd, &st));
         assert_se((st.st_mode & 0222) == 0200);
 
         cmd = strjoina("getfacl -p ", fn);
@@ -117,7 +131,7 @@ TEST(fd_acl_make_read_only) {
         log_info("read-only");
         assert_se(fd_acl_make_read_only(fd));
 
-        assert_se(fstat(fd, &st) >= 0);
+        ASSERT_OK_ERRNO(fstat(fd, &st));
         assert_se((st.st_mode & 0222) == 0000);
 
         cmd = strjoina("getfacl -p ", fn);
@@ -125,6 +139,8 @@ TEST(fd_acl_make_read_only) {
 
         cmd = strjoina("stat ", fn);
         assert_se(system(cmd) == 0);
+
+        return 0;
 }
 
 DEFINE_TEST_MAIN(LOG_INFO);
index 8c86b72dc025c17d7e1df1719480eb23389e5c59..1623c483a88d0af4cefc9b04283e0b2f32e1c4c8 100644 (file)
@@ -16,14 +16,14 @@ static const struct af_name* lookup_af(register const char *str, register GPERF_
 TEST(af_list) {
         for (unsigned i = 0; i < ELEMENTSOF(af_names); i++) {
                 if (af_names[i]) {
-                        assert_se(streq(af_to_name(i), af_names[i]));
+                        ASSERT_STREQ(af_to_name(i), af_names[i]);
                         assert_se(af_from_name(af_names[i]) == (int) i);
                 }
         }
 
-        assert_se(af_to_name(af_max()) == NULL);
-        assert_se(af_to_name(0) == NULL);
-        assert_se(af_to_name(-1) == NULL);
+        ASSERT_NULL(af_to_name(af_max()));
+        ASSERT_NULL(af_to_name(0));
+        ASSERT_NULL(af_to_name(-1));
         assert_se(af_from_name("huddlduddl") == -EINVAL);
         assert_se(af_from_name("") == -EINVAL);
 }
index 8731e1c3f73b71a85666b1ddc60059eba747e44e..b245e862c823ee37c63860e70a96e55737b9a132 100644 (file)
@@ -13,18 +13,18 @@ int main(int argc, char *argv[]) {
 
         test_setup_logging(LOG_INFO);
 
-        assert_se(architecture_from_string("") < 0);
-        assert_se(architecture_from_string(NULL) < 0);
-        assert_se(architecture_from_string("hoge") < 0);
-        assert_se(architecture_to_string(-1) == NULL);
-        assert_se(architecture_from_string(architecture_to_string(0)) == 0);
-        assert_se(architecture_from_string(architecture_to_string(1)) == 1);
+        ASSERT_LT(architecture_from_string(""), 0);
+        ASSERT_LT(architecture_from_string(NULL), 0);
+        ASSERT_LT(architecture_from_string("hoge"), 0);
+        ASSERT_NULL(architecture_to_string(-1));
+        ASSERT_EQ(architecture_from_string(architecture_to_string(0)), 0);
+        ASSERT_EQ(architecture_from_string(architecture_to_string(1)), 1);
 
         v = detect_virtualization();
         if (ERRNO_IS_NEG_PRIVILEGE(v))
                 return log_tests_skipped("Cannot detect virtualization");
 
-        assert_se(v >= 0);
+        ASSERT_OK(v);
 
         log_info("virtualization=%s id=%s",
                  VIRTUALIZATION_IS_CONTAINER(v) ? "container" :
@@ -32,20 +32,20 @@ int main(int argc, char *argv[]) {
                  virtualization_to_string(v));
 
         a = uname_architecture();
-        assert_se(a >= 0);
+        ASSERT_OK(a);
 
         p = architecture_to_string(a);
         assert_se(p);
         log_info("uname architecture=%s", p);
-        assert_se(architecture_from_string(p) == a);
+        ASSERT_EQ(architecture_from_string(p), a);
 
         a = native_architecture();
-        assert_se(a >= 0);
+        ASSERT_OK(a);
 
         p = architecture_to_string(a);
         assert_se(p);
         log_info("native architecture=%s", p);
-        assert_se(architecture_from_string(p) == a);
+        ASSERT_EQ(architecture_from_string(p), a);
 
         log_info("primary library architecture=" LIB_ARCH_TUPLE);
 
index d8dd4649afad715f5cc2d8f2ef0084ad7a16d656..15b79977504311ea30f47f394613d2e3f7a12cf3 100644 (file)
@@ -1,5 +1,7 @@
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 
+/* Make sure the net/if.h header is included before any linux/ one */
+#include <net/if.h>
 #include <linux/if_arp.h>
 
 #include "arphrd-util.h"
@@ -14,11 +16,11 @@ TEST(arphrd) {
                 if (name) {
                         log_info("%i: %s", i, name);
 
-                        assert_se(arphrd_from_name(name) == i);
+                        ASSERT_EQ(arphrd_from_name(name), i);
                 }
         }
 
-        assert_se(arphrd_to_name(ARPHRD_VOID + 1) == NULL);
+        ASSERT_NULL(arphrd_to_name(ARPHRD_VOID + 1));
         assert_se(arphrd_from_name("huddlduddl") == -EINVAL);
         assert_se(arphrd_from_name("") == -EINVAL);
 }
index 8fee07330d28f902b8da0bfba2c213c938834d2a..e58e86870b39cf6a156180b96e0650917d1202e1 100644 (file)
@@ -15,9 +15,9 @@ TEST(ask_password) {
 
         r = ask_password_tty(-EBADF, &req, /* until= */ 0, /* flags= */ ASK_PASSWORD_CONSOLE_COLOR, /* flag_file= */ NULL, &ret);
         if (r == -ECANCELED)
-                assert_se(ret == NULL);
+                ASSERT_NULL(ret);
         else {
-                assert_se(r >= 0);
+                ASSERT_OK(r);
                 assert_se(strv_length(ret) == 1);
                 log_info("Got \"%s\"", *ret);
         }
index e9f10fdae3b39140f456218c08fa672b08919b60..4076213d83463dd66e3681786b92c2dfad756f0b 100644 (file)
@@ -14,7 +14,7 @@
 #include "tmpfile-util.h"
 
 TEST(asynchronous_sync) {
-        assert_se(asynchronous_sync(NULL) >= 0);
+        ASSERT_OK(asynchronous_sync(NULL));
 }
 
 TEST(asynchronous_close) {
@@ -22,29 +22,29 @@ TEST(asynchronous_close) {
         int fd, r;
 
         fd = mkostemp_safe(name);
-        assert_se(fd >= 0);
+        ASSERT_OK(fd);
         asynchronous_close(fd);
 
         sleep(1);
 
-        assert_se(fcntl(fd, F_GETFD) == -1);
+        ASSERT_EQ(fcntl(fd, F_GETFD), -1);
         assert_se(errno == EBADF);
 
         r = safe_fork("(subreaper)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG_SIGKILL|FORK_LOG|FORK_WAIT, NULL);
-        assert(r >= 0);
+        ASSERT_OK(r);
 
         if (r == 0) {
                 /* child */
 
-                assert(make_reaper_process(true) >= 0);
+                ASSERT_OK(make_reaper_process(true));
 
                 fd = open("/dev/null", O_RDONLY|O_CLOEXEC);
-                assert_se(fd >= 0);
+                ASSERT_OK(fd);
                 asynchronous_close(fd);
 
                 sleep(1);
 
-                assert_se(fcntl(fd, F_GETFD) == -1);
+                ASSERT_EQ(fcntl(fd, F_GETFD), -1);
                 assert_se(errno == EBADF);
 
                 _exit(EXIT_SUCCESS);
@@ -55,34 +55,34 @@ TEST(asynchronous_rm_rf) {
         _cleanup_free_ char *t = NULL, *k = NULL;
         int r;
 
-        assert_se(mkdtemp_malloc(NULL, &t) >= 0);
+        ASSERT_OK(mkdtemp_malloc(NULL, &t));
         assert_se(k = path_join(t, "somefile"));
-        assert_se(touch(k) >= 0);
-        assert_se(asynchronous_rm_rf(t, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0);
+        ASSERT_OK(touch(k));
+        ASSERT_OK(asynchronous_rm_rf(t, REMOVE_ROOT|REMOVE_PHYSICAL));
 
         /* Do this once more, form a subreaper. Which is nice, because we can watch the async child even
          * though detached */
 
         r = safe_fork("(subreaper)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG_SIGTERM|FORK_LOG|FORK_WAIT, NULL);
-        assert_se(r >= 0);
+        ASSERT_OK(r);
 
         if (r == 0) {
                 _cleanup_free_ char *tt = NULL, *kk = NULL;
 
                 /* child */
 
-                assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGCHLD) >= 0);
-                assert_se(make_reaper_process(true) >= 0);
+                ASSERT_OK(sigprocmask_many(SIG_BLOCK, NULL, SIGCHLD));
+                ASSERT_OK(make_reaper_process(true));
 
-                assert_se(mkdtemp_malloc(NULL, &tt) >= 0);
+                ASSERT_OK(mkdtemp_malloc(NULL, &tt));
                 assert_se(kk = path_join(tt, "somefile"));
-                assert_se(touch(kk) >= 0);
-                assert_se(asynchronous_rm_rf(tt, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0);
+                ASSERT_OK(touch(kk));
+                ASSERT_OK(asynchronous_rm_rf(tt, REMOVE_ROOT|REMOVE_PHYSICAL));
 
                 for (;;) {
                         siginfo_t si = {};
 
-                        assert_se(waitid(P_ALL, 0, &si, WEXITED) >= 0);
+                        ASSERT_OK(waitid(P_ALL, 0, &si, WEXITED));
 
                         if (access(tt, F_OK) < 0) {
                                 assert_se(errno == ENOENT);
index 7e8bfc0ad65bb085dc9b3b0e7ff5282c7b07ac2f..1e0da59c489a902543c63d98f672571f100d8bc3 100644 (file)
@@ -30,7 +30,7 @@ static void set_alarm(usec_t usecs) {
         struct itimerval v = { };
 
         timeval_store(&v.it_value, usecs);
-        assert_se(setitimer(ITIMER_REAL, &v, NULL) >= 0);
+        ASSERT_OK(setitimer(ITIMER_REAL, &v, NULL));
 }
 
 #define TEST_BARRIER(_FUNCTION, _CHILD_CODE, _WAIT_CHILD, _PARENT_CODE, _WAIT_PARENT)  \
@@ -38,14 +38,14 @@ static void set_alarm(usec_t usecs) {
                 Barrier b = BARRIER_NULL;                               \
                 pid_t pid1, pid2;                                       \
                                                                         \
-                assert_se(barrier_create(&b) >= 0);                     \
-                assert_se(b.me > 0);                                    \
-                assert_se(b.them > 0);                                  \
-                assert_se(b.pipe[0] > 0);                               \
-                assert_se(b.pipe[1] > 0);                               \
+                ASSERT_OK(barrier_create(&b));                          \
+                ASSERT_GT(b.me, 0);                                     \
+                ASSERT_GT(b.them, 0);                                   \
+                ASSERT_GT(b.pipe[0], 0);                                \
+                ASSERT_GT(b.pipe[1], 0);                                \
                                                                         \
                 pid1 = fork();                                          \
-                assert_se(pid1 >= 0);                                   \
+                ASSERT_OK(pid1);                                        \
                 if (pid1 == 0) {                                        \
                         barrier_set_role(&b, BARRIER_CHILD);            \
                         { _CHILD_CODE; }                                \
@@ -53,7 +53,7 @@ static void set_alarm(usec_t usecs) {
                 }                                                       \
                                                                         \
                 pid2 = fork();                                          \
-                assert_se(pid2 >= 0);                                   \
+                ASSERT_OK(pid2);                                        \
                 if (pid2 == 0) {                                        \
                         barrier_set_role(&b, BARRIER_PARENT);           \
                         { _PARENT_CODE; }                               \
@@ -71,16 +71,16 @@ static void set_alarm(usec_t usecs) {
                 ({                                                      \
                         int pidr, status;                               \
                         pidr = waitpid(_pid, &status, 0);               \
-                        assert_se(pidr == _pid);                        \
+                        ASSERT_EQ(pidr, _pid);                          \
                         assert_se(WIFEXITED(status));                   \
-                        assert_se(WEXITSTATUS(status) == 42);           \
+                        ASSERT_EQ(WEXITSTATUS(status), 42);             \
                 })
 
 #define TEST_BARRIER_WAIT_ALARM(_pid) \
                 ({                                                      \
                         int pidr, status;                               \
                         pidr = waitpid(_pid, &status, 0);               \
-                        assert_se(pidr == _pid);                        \
+                        ASSERT_EQ(pidr, _pid);                          \
                         assert_se(WIFSIGNALED(status));                 \
                         assert_se(WTERMSIG(status) == SIGALRM);         \
                 })
index 8acf83312911fd041ec5b8cb509e128eb3019d8d..b91b2935bb8605cd9f00146a994a93c7466713ae 100644 (file)
@@ -12,50 +12,50 @@ int main(int argc, const char *argv[]) {
         b = bitmap_new();
         assert_se(b);
 
-        assert_se(bitmap_ensure_allocated(&b) == 0);
+        ASSERT_EQ(bitmap_ensure_allocated(&b), 0);
         b = bitmap_free(b);
-        assert_se(bitmap_ensure_allocated(&b) == 0);
+        ASSERT_EQ(bitmap_ensure_allocated(&b), 0);
 
-        assert_se(bitmap_isset(b, 0) == false);
-        assert_se(bitmap_isset(b, 1) == false);
-        assert_se(bitmap_isset(b, 256) == false);
-        assert_se(bitmap_isclear(b) == true);
+        ASSERT_FALSE(bitmap_isset(b, 0));
+        ASSERT_FALSE(bitmap_isset(b, 1));
+        ASSERT_FALSE(bitmap_isset(b, 256));
+        ASSERT_TRUE(bitmap_isclear(b));
 
-        assert_se(bitmap_set(b, 0) == 0);
-        assert_se(bitmap_isset(b, 0) == true);
-        assert_se(bitmap_isclear(b) == false);
+        ASSERT_EQ(bitmap_set(b, 0), 0);
+        ASSERT_TRUE(bitmap_isset(b, 0));
+        ASSERT_FALSE(bitmap_isclear(b));
         bitmap_unset(b, 0);
-        assert_se(bitmap_isset(b, 0) == false);
-        assert_se(bitmap_isclear(b) == true);
+        ASSERT_FALSE(bitmap_isset(b, 0));
+        ASSERT_TRUE(bitmap_isclear(b));
 
-        assert_se(bitmap_set(b, 1) == 0);
-        assert_se(bitmap_isset(b, 1) == true);
-        assert_se(bitmap_isclear(b) == false);
+        ASSERT_EQ(bitmap_set(b, 1), 0);
+        ASSERT_TRUE(bitmap_isset(b, 1));
+        ASSERT_FALSE(bitmap_isclear(b));
         bitmap_unset(b, 1);
-        assert_se(bitmap_isset(b, 1) == false);
-        assert_se(bitmap_isclear(b) == true);
+        ASSERT_FALSE(bitmap_isset(b, 1));
+        ASSERT_TRUE(bitmap_isclear(b));
 
-        assert_se(bitmap_set(b, 256) == 0);
-        assert_se(bitmap_isset(b, 256) == true);
-        assert_se(bitmap_isclear(b) == false);
+        ASSERT_EQ(bitmap_set(b, 256), 0);
+        ASSERT_TRUE(bitmap_isset(b, 256));
+        ASSERT_FALSE(bitmap_isclear(b));
         bitmap_unset(b, 256);
-        assert_se(bitmap_isset(b, 256) == false);
-        assert_se(bitmap_isclear(b) == true);
+        ASSERT_FALSE(bitmap_isset(b, 256));
+        ASSERT_TRUE(bitmap_isclear(b));
 
-        assert_se(bitmap_set(b, 32) == 0);
+        ASSERT_EQ(bitmap_set(b, 32), 0);
         bitmap_unset(b, 0);
-        assert_se(bitmap_isset(b, 32) == true);
+        ASSERT_TRUE(bitmap_isset(b, 32));
         bitmap_unset(b, 32);
 
         BITMAP_FOREACH(n, NULL)
                 assert_not_reached();
 
-        assert_se(bitmap_set(b, 0) == 0);
-        assert_se(bitmap_set(b, 1) == 0);
-        assert_se(bitmap_set(b, 256) == 0);
+        ASSERT_EQ(bitmap_set(b, 0), 0);
+        ASSERT_EQ(bitmap_set(b, 1), 0);
+        ASSERT_EQ(bitmap_set(b, 256), 0);
 
         BITMAP_FOREACH(n, b) {
-                assert_se(n == i);
+                ASSERT_EQ(n, i);
                 if (i == 0)
                         i = 1;
                 else if (i == 1)
@@ -64,12 +64,12 @@ int main(int argc, const char *argv[]) {
                         i = UINT_MAX;
         }
 
-        assert_se(i == UINT_MAX);
+        ASSERT_EQ(i, UINT_MAX);
 
         i = 0;
 
         BITMAP_FOREACH(n, b) {
-                assert_se(n == i);
+                ASSERT_EQ(n, i);
                 if (i == 0)
                         i = 1;
                 else if (i == 1)
@@ -78,38 +78,38 @@ int main(int argc, const char *argv[]) {
                         i = UINT_MAX;
         }
 
-        assert_se(i == UINT_MAX);
+        ASSERT_EQ(i, UINT_MAX);
 
         b2 = bitmap_copy(b);
         assert_se(b2);
-        assert_se(bitmap_equal(b, b2) == true);
-        assert_se(bitmap_equal(b, b) == true);
-        assert_se(bitmap_equal(b, NULL) == false);
-        assert_se(bitmap_equal(NULL, b) == false);
-        assert_se(bitmap_equal(NULL, NULL) == true);
+        ASSERT_TRUE(bitmap_equal(b, b2));
+        ASSERT_TRUE(bitmap_equal(b, b));
+        ASSERT_FALSE(bitmap_equal(b, NULL));
+        ASSERT_FALSE(bitmap_equal(NULL, b));
+        ASSERT_TRUE(bitmap_equal(NULL, NULL));
 
         bitmap_clear(b);
-        assert_se(bitmap_isclear(b) == true);
-        assert_se(bitmap_equal(b, b2) == false);
+        ASSERT_TRUE(bitmap_isclear(b));
+        ASSERT_FALSE(bitmap_equal(b, b2));
         b2 = bitmap_free(b2);
 
         assert_se(bitmap_set(b, UINT_MAX) == -ERANGE);
 
         b = bitmap_free(b);
-        assert_se(bitmap_ensure_allocated(&b) == 0);
-        assert_se(bitmap_ensure_allocated(&b2) == 0);
+        ASSERT_EQ(bitmap_ensure_allocated(&b), 0);
+        ASSERT_EQ(bitmap_ensure_allocated(&b2), 0);
 
         assert_se(bitmap_equal(b, b2));
-        assert_se(bitmap_set(b, 0) == 0);
+        ASSERT_EQ(bitmap_set(b, 0), 0);
         bitmap_unset(b, 0);
         assert_se(bitmap_equal(b, b2));
 
-        assert_se(bitmap_set(b, 1) == 0);
+        ASSERT_EQ(bitmap_set(b, 1), 0);
         bitmap_clear(b);
         assert_se(bitmap_equal(b, b2));
 
-        assert_se(bitmap_set(b, 0) == 0);
-        assert_se(bitmap_set(b2, 0) == 0);
+        ASSERT_EQ(bitmap_set(b, 0), 0);
+        ASSERT_EQ(bitmap_set(b2, 0), 0);
         assert_se(bitmap_equal(b, b2));
 
         return 0;
index c3e487696689ddb25840f9c791f8411e4b4ba844..677172964f1de70e523d6e7af136bcf9bcf644e5 100644 (file)
@@ -76,11 +76,11 @@ int main(int argc, char* argv[]) {
         test_setup_logging(LOG_DEBUG);
 
         p = test_acpi_fpdt();
-        assert_se(p >= 0);
+        ASSERT_OK(p);
         q = test_efi_loader();
-        assert_se(q >= 0);
+        ASSERT_OK(q);
         r = test_boot_timestamps();
-        assert_se(r >= 0);
+        ASSERT_OK(r);
 
         if (p == 0 && q == 0 && r == 0)
                 return log_tests_skipped("access to firmware variables not possible");
index 86d7702bf8a615fbd06ee1230cf2ea026ef5e0f7..88faa1efb2bf4bcf65221e4c3b76d489abe2714d 100644 (file)
@@ -63,7 +63,7 @@ TEST_RET(bootspec_sort) {
         _cleanup_(rm_rf_physical_and_freep) char *d = NULL;
         _cleanup_(boot_config_free) BootConfig config = BOOT_CONFIG_NULL;
 
-        assert_se(mkdtemp_malloc("/tmp/bootspec-testXXXXXX", &d) >= 0);
+        ASSERT_OK(mkdtemp_malloc("/tmp/bootspec-testXXXXXX", &d));
 
         for (size_t i = 0; i < ELEMENTSOF(entries); i++) {
                 _cleanup_free_ char *j = NULL;
@@ -71,24 +71,24 @@ TEST_RET(bootspec_sort) {
                 j = path_join(d, "/loader/entries/", entries[i].fname);
                 assert_se(j);
 
-                assert_se(write_string_file(j, entries[i].contents, WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_MKDIR_0755) >= 0);
+                ASSERT_OK(write_string_file(j, entries[i].contents, WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_MKDIR_0755));
         }
 
-        assert_se(boot_config_load(&config, d, NULL) >= 0);
+        ASSERT_OK(boot_config_load(&config, d, NULL));
 
         assert_se(config.n_entries == 6);
 
         /* First, because has sort key, and its the lowest one */
-        assert_se(streq(config.entries[0].id, "d.conf"));
+        ASSERT_STREQ(config.entries[0].id, "d.conf");
 
         /* These two have a sort key, and newest must be first */
-        assert_se(streq(config.entries[1].id, "cx.conf"));
-        assert_se(streq(config.entries[2].id, "c.conf"));
+        ASSERT_STREQ(config.entries[1].id, "cx.conf");
+        ASSERT_STREQ(config.entries[2].id, "c.conf");
 
         /* The following ones have no sort key, hence order by version compared ids, lowest first */
-        assert_se(streq(config.entries[3].id, "b.conf"));
-        assert_se(streq(config.entries[4].id, "a-10.conf"));
-        assert_se(streq(config.entries[5].id, "a-5.conf"));
+        ASSERT_STREQ(config.entries[3].id, "b.conf");
+        ASSERT_STREQ(config.entries[4].id, "a-10.conf");
+        ASSERT_STREQ(config.entries[5].id, "a-5.conf");
 
         return 0;
 }
@@ -101,7 +101,7 @@ static void test_extract_tries_one(const char *fname, int ret, const char *strip
         if (ret < 0)
                 return;
 
-        assert_se(streq_ptr(p, stripped));
+        ASSERT_STREQ(p, stripped);
         assert_se(l == tries_left);
         assert_se(d == tries_done);
 }
@@ -198,7 +198,7 @@ TEST_RET(bootspec_boot_config_find_entry) {
 
         /* Test finding a non-existent entry */
         entry = boot_config_find_entry(&config, "nonexistent.conf");
-        assert_se(entry == NULL);
+        ASSERT_NULL(entry);
 
         /* Test case-insensitivity */
         entry = boot_config_find_entry(&config, "A-10.CONF");
index 4bd606ecb3660fab2138f5fc1f4e0df1f2a8f7a7..f1db021c94d138a522246f1a0ce99fed492f1248 100644 (file)
@@ -22,13 +22,13 @@ static void test_policy_closed(const char *cgroup_path, BPFProgram **installed_p
         log_info("/* %s */", __func__);
 
         r = bpf_devices_cgroup_init(&prog, CGROUP_DEVICE_POLICY_CLOSED, true);
-        assert_se(r >= 0);
+        ASSERT_OK(r);
 
         r = bpf_devices_allow_list_static(prog, cgroup_path);
-        assert_se(r >= 0);
+        ASSERT_OK(r);
 
         r = bpf_devices_apply_policy(&prog, CGROUP_DEVICE_POLICY_CLOSED, true, cgroup_path, installed_prog);
-        assert_se(r >= 0);
+        ASSERT_OK(r);
 
         FOREACH_STRING(s, "/dev/null",
                           "/dev/zero",
@@ -59,19 +59,19 @@ static void test_policy_strict(const char *cgroup_path, BPFProgram **installed_p
         log_info("/* %s */", __func__);
 
         r = bpf_devices_cgroup_init(&prog, CGROUP_DEVICE_POLICY_STRICT, true);
-        assert_se(r >= 0);
+        ASSERT_OK(r);
 
         r = bpf_devices_allow_list_device(prog, cgroup_path, "/dev/null", CGROUP_DEVICE_READ|CGROUP_DEVICE_WRITE);
-        assert_se(r >= 0);
+        ASSERT_OK(r);
 
         r = bpf_devices_allow_list_device(prog, cgroup_path, "/dev/random", CGROUP_DEVICE_READ);
-        assert_se(r >= 0);
+        ASSERT_OK(r);
 
         r = bpf_devices_allow_list_device(prog, cgroup_path, "/dev/zero", CGROUP_DEVICE_WRITE);
-        assert_se(r >= 0);
+        ASSERT_OK(r);
 
         r = bpf_devices_apply_policy(&prog, CGROUP_DEVICE_POLICY_STRICT, true, cgroup_path, installed_prog);
-        assert_se(r >= 0);
+        ASSERT_OK(r);
 
         {
                 _cleanup_close_ int fd = -EBADF, fd2 = -EBADF;
@@ -136,13 +136,13 @@ static void test_policy_allow_list_major(const char *pattern, const char *cgroup
         log_info("/* %s(%s) */", __func__, pattern);
 
         r = bpf_devices_cgroup_init(&prog, CGROUP_DEVICE_POLICY_STRICT, true);
-        assert_se(r >= 0);
+        ASSERT_OK(r);
 
         r = bpf_devices_allow_list_major(prog, cgroup_path, pattern, 'c', CGROUP_DEVICE_READ|CGROUP_DEVICE_WRITE);
-        assert_se(r >= 0);
+        ASSERT_OK(r);
 
         r = bpf_devices_apply_policy(&prog, CGROUP_DEVICE_POLICY_STRICT, true, cgroup_path, installed_prog);
-        assert_se(r >= 0);
+        ASSERT_OK(r);
 
         /* /dev/null, /dev/full have major==1, /dev/tty has major==5 */
         {
@@ -195,13 +195,13 @@ static void test_policy_allow_list_major_star(char type, const char *cgroup_path
         log_info("/* %s(type=%c) */", __func__, type);
 
         r = bpf_devices_cgroup_init(&prog, CGROUP_DEVICE_POLICY_STRICT, true);
-        assert_se(r >= 0);
+        ASSERT_OK(r);
 
         r = bpf_devices_allow_list_major(prog, cgroup_path, "*", type, CGROUP_DEVICE_READ|CGROUP_DEVICE_WRITE);
-        assert_se(r >= 0);
+        ASSERT_OK(r);
 
         r = bpf_devices_apply_policy(&prog, CGROUP_DEVICE_POLICY_STRICT, true, cgroup_path, installed_prog);
-        assert_se(r >= 0);
+        ASSERT_OK(r);
 
         {
                 _cleanup_close_ int fd = -EBADF;
@@ -226,7 +226,7 @@ static void test_policy_empty(bool add_mismatched, const char *cgroup_path, BPFP
         log_info("/* %s(add_mismatched=%s) */", __func__, yes_no(add_mismatched));
 
         r = bpf_devices_cgroup_init(&prog, CGROUP_DEVICE_POLICY_STRICT, add_mismatched);
-        assert_se(r >= 0);
+        ASSERT_OK(r);
 
         if (add_mismatched) {
                 r = bpf_devices_allow_list_major(prog, cgroup_path, "foobarxxx", 'c', CGROUP_DEVICE_READ|CGROUP_DEVICE_WRITE);
@@ -234,7 +234,7 @@ static void test_policy_empty(bool add_mismatched, const char *cgroup_path, BPFP
         }
 
         r = bpf_devices_apply_policy(&prog, CGROUP_DEVICE_POLICY_STRICT, false, cgroup_path, installed_prog);
-        assert_se(r >= 0);
+        ASSERT_OK(r);
 
         {
                 _cleanup_close_ int fd = -EBADF;
@@ -258,7 +258,7 @@ int main(int argc, char *argv[]) {
 
         test_setup_logging(LOG_DEBUG);
 
-        assert_se(getrlimit(RLIMIT_MEMLOCK, &rl) >= 0);
+        ASSERT_OK(getrlimit(RLIMIT_MEMLOCK, &rl));
         rl.rlim_cur = rl.rlim_max = MAX(rl.rlim_max, CAN_MEMLOCK_SIZE);
         (void) setrlimit(RLIMIT_MEMLOCK, &rl);
 
@@ -278,10 +278,10 @@ int main(int argc, char *argv[]) {
         r = bpf_devices_supported();
         if (r == 0)
                 return log_tests_skipped("BPF device filter not supported");
-        assert_se(r == 1);
+        ASSERT_EQ(r, 1);
 
         r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, cgroup, NULL, &controller_path);
-        assert_se(r >= 0);
+        ASSERT_OK(r);
 
         _cleanup_(bpf_program_freep) BPFProgram *prog = NULL;
 
@@ -297,11 +297,11 @@ int main(int argc, char *argv[]) {
         test_policy_empty(false, cgroup, &prog);
         test_policy_empty(true, cgroup, &prog);
 
-        assert_se(path_extract_directory(cgroup, &parent) >= 0);
+        ASSERT_OK(path_extract_directory(cgroup, &parent));
 
-        assert_se(cg_mask_supported(&supported) >= 0);
+        ASSERT_OK(cg_mask_supported(&supported));
         r = cg_attach_everywhere(supported, parent, 0, NULL, NULL);
-        assert_se(r >= 0);
+        ASSERT_OK(r);
 
         return 0;
 }
index 30193effa6d821763770a7e637872eb673537fcb..e18130b42352a30e919e83862cce2ed64b82f34d 100644 (file)
@@ -39,7 +39,7 @@ int main(int argc, char *argv[]) {
         if (detect_container() > 0)
                 return log_tests_skipped("test-bpf-firewall fails inside LXC and Docker containers: https://github.com/systemd/systemd/issues/9666");
 
-        assert_se(getrlimit(RLIMIT_MEMLOCK, &rl) >= 0);
+        ASSERT_OK(getrlimit(RLIMIT_MEMLOCK, &rl));
         rl.rlim_cur = rl.rlim_max = MAX(rl.rlim_max, CAN_MEMLOCK_SIZE);
         (void) setrlimit(RLIMIT_MEMLOCK, &rl);
 
@@ -51,20 +51,20 @@ int main(int argc, char *argv[]) {
                 return log_tests_skipped("cgroupfs not available");
 
         _cleanup_free_ char *unit_dir = NULL;
-        assert_se(get_testdata_dir("units", &unit_dir) >= 0);
-        assert_se(set_unit_path(unit_dir) >= 0);
+        ASSERT_OK(get_testdata_dir("units", &unit_dir));
+        ASSERT_OK(set_unit_path(unit_dir));
         assert_se(runtime_dir = setup_fake_runtime_dir());
 
         r = bpf_program_new(BPF_PROG_TYPE_CGROUP_SKB, "sd_trivial", &p);
-        assert_se(r == 0);
+        ASSERT_EQ(r, 0);
 
         r = bpf_program_add_instructions(p, exit_insn, ELEMENTSOF(exit_insn));
-        assert_se(r == 0);
+        ASSERT_EQ(r, 0);
 
         r = bpf_firewall_supported();
         if (r == BPF_FIREWALL_UNSUPPORTED)
                 return log_tests_skipped("BPF firewalling not supported");
-        assert_se(r > 0);
+        ASSERT_GT(r, 0);
 
         if (r == BPF_FIREWALL_SUPPORTED_WITH_MULTI) {
                 log_notice("BPF firewalling with BPF_F_ALLOW_MULTI supported. Yay!");
@@ -73,7 +73,7 @@ int main(int argc, char *argv[]) {
                 log_notice("BPF firewalling (though without BPF_F_ALLOW_MULTI) supported. Good.");
 
         r = bpf_program_load_kernel(p, log_buf, ELEMENTSOF(log_buf));
-        assert_se(r >= 0);
+        ASSERT_OK(r);
 
         if (test_custom_filter) {
                 zero(attr);
@@ -94,29 +94,29 @@ int main(int argc, char *argv[]) {
 
         /* The simple tests succeeded. Now let's try full unit-based use-case. */
 
-        assert_se(manager_new(RUNTIME_SCOPE_USER, MANAGER_TEST_RUN_BASIC, &m) >= 0);
-        assert_se(manager_startup(m, NULL, NULL, NULL) >= 0);
+        ASSERT_OK(manager_new(RUNTIME_SCOPE_USER, MANAGER_TEST_RUN_BASIC, &m));
+        ASSERT_OK(manager_startup(m, NULL, NULL, NULL));
 
         assert_se(u = unit_new(m, sizeof(Service)));
-        assert_se(unit_add_name(u, "foo.service") == 0);
+        ASSERT_EQ(unit_add_name(u, "foo.service"), 0);
         assert_se(cc = unit_get_cgroup_context(u));
         u->perpetual = true;
 
         cc->ip_accounting = true;
 
-        assert_se(config_parse_in_addr_prefixes(u->id, "filename", 1, "Service", 1, "IPAddressAllow", 0, "10.0.1.0/24", &cc->ip_address_allow, NULL) == 0);
-        assert_se(config_parse_in_addr_prefixes(u->id, "filename", 1, "Service", 1, "IPAddressAllow", 0, "127.0.0.2", &cc->ip_address_allow, NULL) == 0);
-        assert_se(config_parse_in_addr_prefixes(u->id, "filename", 1, "Service", 1, "IPAddressDeny", 0, "127.0.0.3", &cc->ip_address_deny, NULL) == 0);
-        assert_se(config_parse_in_addr_prefixes(u->id, "filename", 1, "Service", 1, "IPAddressDeny", 0, "10.0.3.2/24", &cc->ip_address_deny, NULL) == 0);
-        assert_se(config_parse_in_addr_prefixes(u->id, "filename", 1, "Service", 1, "IPAddressDeny", 0, "127.0.0.1/25", &cc->ip_address_deny, NULL) == 0);
-        assert_se(config_parse_in_addr_prefixes(u->id, "filename", 1, "Service", 1, "IPAddressDeny", 0, "127.0.0.4", &cc->ip_address_deny, NULL) == 0);
+        ASSERT_EQ(config_parse_in_addr_prefixes(u->id, "filename", 1, "Service", 1, "IPAddressAllow", 0, "10.0.1.0/24", &cc->ip_address_allow, NULL), 0);
+        ASSERT_EQ(config_parse_in_addr_prefixes(u->id, "filename", 1, "Service", 1, "IPAddressAllow", 0, "127.0.0.2", &cc->ip_address_allow, NULL), 0);
+        ASSERT_EQ(config_parse_in_addr_prefixes(u->id, "filename", 1, "Service", 1, "IPAddressDeny", 0, "127.0.0.3", &cc->ip_address_deny, NULL), 0);
+        ASSERT_EQ(config_parse_in_addr_prefixes(u->id, "filename", 1, "Service", 1, "IPAddressDeny", 0, "10.0.3.2/24", &cc->ip_address_deny, NULL), 0);
+        ASSERT_EQ(config_parse_in_addr_prefixes(u->id, "filename", 1, "Service", 1, "IPAddressDeny", 0, "127.0.0.1/25", &cc->ip_address_deny, NULL), 0);
+        ASSERT_EQ(config_parse_in_addr_prefixes(u->id, "filename", 1, "Service", 1, "IPAddressDeny", 0, "127.0.0.4", &cc->ip_address_deny, NULL), 0);
 
         assert_se(set_size(cc->ip_address_allow) == 2);
         assert_se(set_size(cc->ip_address_deny) == 4);
 
         /* The deny list is defined redundantly, let's ensure it will be properly reduced */
-        assert_se(in_addr_prefixes_reduce(cc->ip_address_allow) >= 0);
-        assert_se(in_addr_prefixes_reduce(cc->ip_address_deny) >= 0);
+        ASSERT_OK(in_addr_prefixes_reduce(cc->ip_address_allow));
+        ASSERT_OK(in_addr_prefixes_reduce(cc->ip_address_deny));
 
         assert_se(set_size(cc->ip_address_allow) == 2);
         assert_se(set_size(cc->ip_address_deny) == 2);
@@ -166,7 +166,7 @@ int main(int argc, char *argv[]) {
         log_notice("%s", log_buf);
         log_notice("-------");
 
-        assert_se(r >= 0);
+        ASSERT_OK(r);
 
         r = bpf_program_load_kernel(crt->ip_bpf_egress, log_buf, ELEMENTSOF(log_buf));
 
@@ -175,9 +175,9 @@ int main(int argc, char *argv[]) {
         log_notice("%s", log_buf);
         log_notice("-------");
 
-        assert_se(r >= 0);
+        ASSERT_OK(r);
 
-        assert_se(unit_start(u, NULL) >= 0);
+        ASSERT_OK(unit_start(u, NULL));
 
         while (!IN_SET(SERVICE(u)->state, SERVICE_DEAD, SERVICE_FAILED))
                 assert_se(sd_event_run(m->event, UINT64_MAX) >= 0);
@@ -202,7 +202,7 @@ int main(int argc, char *argv[]) {
                 SERVICE(u)->type = SERVICE_ONESHOT;
                 u->load_state = UNIT_LOADED;
 
-                assert_se(unit_start(u, NULL) >= 0);
+                ASSERT_OK(unit_start(u, NULL));
 
                 while (!IN_SET(SERVICE(u)->state, SERVICE_DEAD, SERVICE_FAILED))
                         assert_se(sd_event_run(m->event, UINT64_MAX) >= 0);
index 35c7e0d692483c8c9ff525c3ace257fa69da9e13..cb6073d2a7211164a8d93f687592bc89a8d474a1 100644 (file)
@@ -282,7 +282,7 @@ int main(int argc, char *argv[]) {
         if (getuid() != 0)
                 return log_tests_skipped("not running as root");
 
-        assert_se(getrlimit(RLIMIT_MEMLOCK, &rl) >= 0);
+        ASSERT_OK(getrlimit(RLIMIT_MEMLOCK, &rl));
         rl.rlim_cur = rl.rlim_max = MAX(rl.rlim_max, CAN_MEMLOCK_SIZE);
         (void) setrlimit_closest(RLIMIT_MEMLOCK, &rl);
 
@@ -297,34 +297,34 @@ int main(int argc, char *argv[]) {
         if (r == -ENOMEDIUM)
                 return log_tests_skipped("cgroupfs not available");
 
-        assert_se(get_testdata_dir("units", &unit_dir) >= 0);
-        assert_se(set_unit_path(unit_dir) >= 0);
+        ASSERT_OK(get_testdata_dir("units", &unit_dir));
+        ASSERT_OK(set_unit_path(unit_dir));
         assert_se(runtime_dir = setup_fake_runtime_dir());
 
-        assert_se(manager_new(RUNTIME_SCOPE_USER, MANAGER_TEST_RUN_BASIC, &m) >= 0);
-        assert_se(manager_startup(m, NULL, NULL, NULL) >= 0);
+        ASSERT_OK(manager_new(RUNTIME_SCOPE_USER, MANAGER_TEST_RUN_BASIC, &m));
+        ASSERT_OK(manager_startup(m, NULL, NULL, NULL));
 
-        assert_se(test_bpf_cgroup_programs(m,
-                                "single_prog.service", single_prog, ELEMENTSOF(single_prog)) >= 0);
-        assert_se(test_bpf_cgroup_programs(m,
+        ASSERT_OK(test_bpf_cgroup_programs(m,
+                                "single_prog.service", single_prog, ELEMENTSOF(single_prog)));
+        ASSERT_OK(test_bpf_cgroup_programs(m,
                                 "multi_prog_same_hook.service",
-                                multi_prog_same_hook, ELEMENTSOF(multi_prog_same_hook)) >= 0);
-        assert_se(test_bpf_cgroup_programs(m,
+                                multi_prog_same_hook, ELEMENTSOF(multi_prog_same_hook)));
+        ASSERT_OK(test_bpf_cgroup_programs(m,
                                 "same_prog_multi_hook.service",
-                                same_prog_multi_hook, ELEMENTSOF(same_prog_multi_hook)) >= 0);
-        assert_se(test_bpf_cgroup_programs(m,
+                                same_prog_multi_hook, ELEMENTSOF(same_prog_multi_hook)));
+        ASSERT_OK(test_bpf_cgroup_programs(m,
                                 "same_prog_multi_option_0.service",
-                                same_prog_multi_option_0, ELEMENTSOF(same_prog_multi_option_0)) >= 0);
-        assert_se(test_bpf_cgroup_programs(m,
+                                same_prog_multi_option_0, ELEMENTSOF(same_prog_multi_option_0)));
+        ASSERT_OK(test_bpf_cgroup_programs(m,
                                 "same_prog_multi_option_1.service",
-                                same_prog_multi_option_1, ELEMENTSOF(same_prog_multi_option_1)) >= 0);
-        assert_se(test_bpf_cgroup_programs(m,
+                                same_prog_multi_option_1, ELEMENTSOF(same_prog_multi_option_1)));
+        ASSERT_OK(test_bpf_cgroup_programs(m,
                                 "same_prog_same_hook.service",
                                 same_prog_same_hook,
-                                ELEMENTSOF(same_prog_same_hook)) >= 0);
-        assert_se(test_bpf_cgroup_programs(m,
+                                ELEMENTSOF(same_prog_same_hook)));
+        ASSERT_OK(test_bpf_cgroup_programs(m,
                                 "path_split_test.service",
                                 path_split_test,
-                                ELEMENTSOF(path_split_test)) >= 0);
+                                ELEMENTSOF(path_split_test)));
         return 0;
 }
index e1d56163ef7a64fee96f9c967c376543ce736cff..c9962501db2cb335a65b397e2d53c5e0852693e5 100644 (file)
@@ -68,7 +68,7 @@ int main(int argc, char *argv[]) {
 
         test_setup_logging(LOG_DEBUG);
 
-        assert_se(getrlimit(RLIMIT_MEMLOCK, &rl) >= 0);
+        ASSERT_OK(getrlimit(RLIMIT_MEMLOCK, &rl));
         rl.rlim_cur = rl.rlim_max = MAX(rl.rlim_max, CAN_MEMLOCK_SIZE);
         (void) setrlimit_closest(RLIMIT_MEMLOCK, &rl);
 
@@ -82,21 +82,21 @@ int main(int argc, char *argv[]) {
         if (r == -ENOMEDIUM)
                 return log_tests_skipped("cgroupfs not available");
 
-        assert_se(get_testdata_dir("units", &unit_dir) >= 0);
-        assert_se(set_unit_path(unit_dir) >= 0);
+        ASSERT_OK(get_testdata_dir("units", &unit_dir));
+        ASSERT_OK(set_unit_path(unit_dir));
         assert_se(runtime_dir = setup_fake_runtime_dir());
 
-        assert_se(manager_new(RUNTIME_SCOPE_SYSTEM, MANAGER_TEST_RUN_BASIC, &m) >= 0);
-        assert_se(manager_startup(m, NULL, NULL, NULL) >= 0);
+        ASSERT_OK(manager_new(RUNTIME_SCOPE_SYSTEM, MANAGER_TEST_RUN_BASIC, &m));
+        ASSERT_OK(manager_startup(m, NULL, NULL, NULL));
 
         /* We need to enable access to the filesystem where the binary is so we
          * add @common-block */
-        assert_se(test_restrict_filesystems(m, "restrict_filesystems_test.service", "/sys/kernel/tracing/printk_formats", STRV_MAKE("@common-block")) < 0);
-        assert_se(test_restrict_filesystems(m, "restrict_filesystems_test.service", "/sys/kernel/tracing/printk_formats", STRV_MAKE("tracefs", "@common-block")) >= 0);
-        assert_se(test_restrict_filesystems(m, "restrict_filesystems_test.service", "/sys/kernel/tracing/printk_formats", STRV_MAKE("tracefs", "@common-block", "~tracefs")) < 0);
-        assert_se(test_restrict_filesystems(m, "restrict_filesystems_test.service", "/sys/kernel/debug/sleep_time", STRV_MAKE("@common-block")) < 0);
-        assert_se(test_restrict_filesystems(m, "restrict_filesystems_test.service", "/sys/kernel/debug/sleep_time", STRV_MAKE("debugfs", "@common-block")) >= 0);
-        assert_se(test_restrict_filesystems(m, "restrict_filesystems_test.service", "/sys/kernel/debug/sleep_time", STRV_MAKE("~debugfs")) < 0);
+        ASSERT_LT(test_restrict_filesystems(m, "restrict_filesystems_test.service", "/sys/kernel/tracing/printk_formats", STRV_MAKE("@common-block")), 0);
+        ASSERT_OK(test_restrict_filesystems(m, "restrict_filesystems_test.service", "/sys/kernel/tracing/printk_formats", STRV_MAKE("tracefs", "@common-block")));
+        ASSERT_LT(test_restrict_filesystems(m, "restrict_filesystems_test.service", "/sys/kernel/tracing/printk_formats", STRV_MAKE("tracefs", "@common-block", "~tracefs")), 0);
+        ASSERT_LT(test_restrict_filesystems(m, "restrict_filesystems_test.service", "/sys/kernel/debug/sleep_time", STRV_MAKE("@common-block")), 0);
+        ASSERT_OK(test_restrict_filesystems(m, "restrict_filesystems_test.service", "/sys/kernel/debug/sleep_time", STRV_MAKE("debugfs", "@common-block")));
+        ASSERT_LT(test_restrict_filesystems(m, "restrict_filesystems_test.service", "/sys/kernel/debug/sleep_time", STRV_MAKE("~debugfs")), 0);
 
         return 0;
 }
index 18a0f8f8bfdf93d8400e18bb26918fed282fc289..7cda114de6b1417b2f275af1419d33ce29258e4c 100644 (file)
@@ -16,24 +16,24 @@ static void _test_one(int line, const char *input, const char *output) {
         r = calendar_spec_from_string(input, &c);
         if (r < 0)
                 log_error_errno(r, "Failed to parse \"%s\": %m", input);
-        assert_se(r >= 0);
+        ASSERT_OK(r);
 
-        assert_se(calendar_spec_to_string(c, &p) >= 0);
+        ASSERT_OK(calendar_spec_to_string(c, &p));
         log_info("line %d: \"%s\" → \"%s\"%s%s", line, input, p,
                  !streq(p, output) ? " expected:" : "",
                  !streq(p, output) ? output : "");
 
-        assert_se(streq(p, output));
+        ASSERT_STREQ(p, output);
 
         u = now(CLOCK_REALTIME);
         r = calendar_spec_next_usec(c, u, &u);
         log_info("Next: %s", r < 0 ? STRERROR(r) : FORMAT_TIMESTAMP(u));
         c = calendar_spec_free(c);
 
-        assert_se(calendar_spec_from_string(p, &c) >= 0);
-        assert_se(calendar_spec_to_string(c, &q) >= 0);
+        ASSERT_OK(calendar_spec_from_string(p, &c));
+        ASSERT_OK(calendar_spec_to_string(c, &q));
 
-        assert_se(streq(q, p));
+        ASSERT_STREQ(q, p);
 }
 #define test_one(input, output) _test_one(__LINE__, input, output)
 
@@ -53,7 +53,7 @@ static void _test_next(int line, const char *input, const char *new_tz, usec_t a
         assert_se(set_unset_env("TZ", new_tz, true) == 0);
         tzset();
 
-        assert_se(calendar_spec_from_string(input, &c) >= 0);
+        ASSERT_OK(calendar_spec_from_string(input, &c));
 
         log_info("line %d: \"%s\" new_tz=%s", line, input, strnull(new_tz));
 
@@ -82,11 +82,11 @@ TEST(timestamp) {
 
         assert_se(format_timestamp_style(buf, sizeof buf, x, TIMESTAMP_US));
         log_info("%s", buf);
-        assert_se(calendar_spec_from_string(buf, &c) >= 0);
-        assert_se(calendar_spec_to_string(c, &t) >= 0);
+        ASSERT_OK(calendar_spec_from_string(buf, &c));
+        ASSERT_OK(calendar_spec_to_string(c, &t));
         log_info("%s", t);
 
-        assert_se(parse_timestamp(t, &y) >= 0);
+        ASSERT_OK(parse_timestamp(t, &y));
         assert_se(y == x);
 }
 
@@ -95,9 +95,9 @@ TEST(hourly_bug_4031) {
         usec_t n, u, w;
         int r;
 
-        assert_se(calendar_spec_from_string("hourly", &c) >= 0);
+        ASSERT_OK(calendar_spec_from_string("hourly", &c));
         n = now(CLOCK_REALTIME);
-        assert_se((r = calendar_spec_next_usec(c, n, &u)) >= 0);
+        ASSERT_OK((r = calendar_spec_next_usec(c, n, &u)));
 
         log_info("Now: %s (%"PRIu64")", FORMAT_TIMESTAMP_STYLE(n, TIMESTAMP_US), n);
         log_info("Next hourly: %s (%"PRIu64")", r < 0 ? STRERROR(r) : FORMAT_TIMESTAMP_STYLE(u, TIMESTAMP_US), u);
@@ -256,7 +256,7 @@ TEST(calendar_spec_from_string) {
 
 static int intro(void) {
         /* Tests have hard-coded results that do not expect a specific timezone to be set by the caller */
-        assert_se(unsetenv("TZ") >= 0);
+        ASSERT_OK(unsetenv("TZ"));
 
         return EXIT_SUCCESS;
 }
index a9cbf695b9012299880d2fb69fe21e2ee89d8ea8..49be4a2d2722bd2fa4121ba1a907821803f838a3 100644 (file)
@@ -21,7 +21,7 @@ TEST(cap_list) {
 
         assert_se(!CAPABILITY_TO_STRING(-1));
         if (capability_list_length() <= 62)
-                assert_se(streq(CAPABILITY_TO_STRING(62), "0x3e"));
+                ASSERT_STREQ(CAPABILITY_TO_STRING(62), "0x3e");
         assert_se(!CAPABILITY_TO_STRING(64));
 
         for (int i = 0; i < capability_list_length(); i++) {
@@ -31,7 +31,7 @@ TEST(cap_list) {
                 assert_se(capability_from_name(n) == i);
                 printf("%s = %i\n", n, i);
 
-                assert_se(streq(CAPABILITY_TO_STRING(i), n));
+                ASSERT_STREQ(CAPABILITY_TO_STRING(i), n);
         }
 
         assert_se(capability_from_name("asdfbsd") == -EINVAL);
@@ -70,7 +70,7 @@ static void test_capability_set_one(uint64_t c, const char *t) {
         uint64_t c1, c_masked = c & all_capabilities();
 
         assert_se(capability_set_to_string(c, &t1) == 0);
-        assert_se(streq(t1, t));
+        ASSERT_STREQ(t1, t);
 
         assert_se(capability_set_from_string(t1, &c1) > 0);
         assert_se(c1 == c_masked);
@@ -160,8 +160,8 @@ TEST(capability_set_to_string_negative) {
                 uint64_t m =
                         random_u64() % (UINT64_C(1) << (cap_last_cap() + 1));
 
-                assert_se(capability_set_to_string(m, &a) >= 0);
-                assert_se(capability_set_to_string_negative(m, &b) >= 0);
+                ASSERT_OK(capability_set_to_string(m, &a));
+                ASSERT_OK(capability_set_to_string_negative(m, &b));
 
                 printf("%s (%zu) → ", a, strlen(a));
 
@@ -170,7 +170,7 @@ TEST(capability_set_to_string_negative) {
                 else
                         printf("%s (%zu)\n", b, strlen(b));
 
-                assert_se(strlen(b) <= strlen(a));
+                ASSERT_LE(strlen(b), strlen(a));
         }
 }
 
index 9df5f5ba07084d1b8c3a6efc35c8d60279da8d43..0538eab6009e27e957d621308cc9a8eb4b66e460 100644 (file)
@@ -41,12 +41,12 @@ static void test_last_cap_file(void) {
         r = read_one_line_file("/proc/sys/kernel/cap_last_cap", &content);
         if (r == -ENOENT || ERRNO_IS_NEG_PRIVILEGE(r)) /* kernel pre 3.2 or no access */
                 return;
-        assert_se(r >= 0);
+        ASSERT_OK(r);
 
         r = safe_atolu(content, &val);
-        assert_se(r >= 0);
+        ASSERT_OK(r);
         assert_se(val != 0);
-        assert_se(val == cap_last_cap());
+        ASSERT_EQ(val, cap_last_cap());
 }
 
 /* verify cap_last_cap() against syscall probing */
@@ -64,7 +64,7 @@ static void test_last_cap_probe(void) {
         }
 
         assert_se(p != 0);
-        assert_se(p == cap_last_cap());
+        ASSERT_EQ(p, cap_last_cap());
 }
 
 static void fork_test(void (*test_func)(void)) {
@@ -130,7 +130,7 @@ static void test_drop_privileges_keep_net_raw(void) {
         show_capabilities();
 
         sock = socket(AF_INET, SOCK_RAW, IPPROTO_UDP);
-        assert_se(sock >= 0);
+        ASSERT_OK(sock);
         safe_close(sock);
 }
 
@@ -138,7 +138,7 @@ static void test_drop_privileges_dontkeep_net_raw(void) {
         int sock;
 
         sock = socket(AF_INET, SOCK_RAW, IPPROTO_UDP);
-        assert_se(sock >= 0);
+        ASSERT_OK(sock);
         safe_close(sock);
 
         assert_se(drop_privileges(test_uid, test_gid, test_flags) >= 0);
@@ -147,7 +147,7 @@ static void test_drop_privileges_dontkeep_net_raw(void) {
         show_capabilities();
 
         sock = socket(AF_INET, SOCK_RAW, IPPROTO_UDP);
-        assert_se(sock < 0);
+        ASSERT_LT(sock, 0);
 }
 
 static void test_drop_privileges_fail(void) {
@@ -155,8 +155,8 @@ static void test_drop_privileges_fail(void) {
         assert_se(getuid() == test_uid);
         assert_se(getgid() == test_gid);
 
-        assert_se(drop_privileges(test_uid, test_gid, test_flags) < 0);
-        assert_se(drop_privileges(0, 0, test_flags) < 0);
+        ASSERT_LT(drop_privileges(test_uid, test_gid, test_flags), 0);
+        ASSERT_LT(drop_privileges(0, 0, test_flags), 0);
 }
 
 static void test_drop_privileges(void) {
@@ -172,14 +172,14 @@ static void test_drop_privileges(void) {
 }
 
 static void test_have_effective_cap(void) {
-        assert_se(have_effective_cap(CAP_KILL) > 0);
-        assert_se(have_effective_cap(CAP_CHOWN) > 0);
+        ASSERT_GT(have_effective_cap(CAP_KILL), 0);
+        ASSERT_GT(have_effective_cap(CAP_CHOWN), 0);
 
-        assert_se(drop_privileges(test_uid, test_gid, test_flags | (1ULL << CAP_KILL)) >= 0);
+        ASSERT_OK(drop_privileges(test_uid, test_gid, test_flags | (1ULL << CAP_KILL)));
         assert_se(getuid() == test_uid);
         assert_se(getgid() == test_gid);
 
-        assert_se(have_effective_cap(CAP_KILL) > 0);
+        ASSERT_GT(have_effective_cap(CAP_KILL), 0);
         assert_se(have_effective_cap(CAP_CHOWN) == 0);
 }
 
@@ -237,9 +237,9 @@ static void test_ensure_cap_64_bit(void) {
         r = read_one_line_file("/proc/sys/kernel/cap_last_cap", &content);
         if (r == -ENOENT || ERRNO_IS_NEG_PRIVILEGE(r)) /* kernel pre 3.2 or no access */
                 return;
-        assert_se(r >= 0);
+        ASSERT_OK(r);
 
-        assert_se(safe_atolu(content, &p) >= 0);
+        ASSERT_OK(safe_atolu(content, &p));
 
         /* If caps don't fit into 64-bit anymore, we have a problem, fail the test. */
         assert_se(p <= 63);
@@ -252,10 +252,10 @@ static void test_capability_get_ambient(void) {
         uint64_t c;
         int r;
 
-        assert_se(capability_get_ambient(&c) >= 0);
+        ASSERT_OK(capability_get_ambient(&c));
 
         r = safe_fork("(getambient)", FORK_RESET_SIGNALS|FORK_DEATHSIG_SIGTERM|FORK_WAIT|FORK_LOG, NULL);
-        assert_se(r >= 0);
+        ASSERT_OK(r);
 
         if (r == 0) {
                 int x, y;
index bfc8fac8772b44cc8db1c312780f8ba7154e0361..857102f8a0a111277d44d82a5dbbca86623f6592 100644 (file)
@@ -20,9 +20,9 @@
 static void log_cgroup_mask(CGroupMask got, CGroupMask expected) {
         _cleanup_free_ char *e_store = NULL, *g_store = NULL;
 
-        assert_se(cg_mask_to_string(expected, &e_store) >= 0);
+        ASSERT_OK(cg_mask_to_string(expected, &e_store));
         log_info("Expected mask: %s", e_store);
-        assert_se(cg_mask_to_string(got, &g_store) >= 0);
+        ASSERT_OK(cg_mask_to_string(got, &g_store));
         log_info("Got mask: %s", g_store);
 }
 
@@ -39,8 +39,8 @@ TEST_RET(cgroup_mask, .sd_booted = true) {
 
         /* Prepare the manager. */
         _cleanup_free_ char *unit_dir = NULL;
-        assert_se(get_testdata_dir("units", &unit_dir) >= 0);
-        assert_se(set_unit_path(unit_dir) >= 0);
+        ASSERT_OK(get_testdata_dir("units", &unit_dir));
+        ASSERT_OK(set_unit_path(unit_dir));
         assert_se(runtime_dir = setup_fake_runtime_dir());
         r = manager_new(RUNTIME_SCOPE_USER, MANAGER_TEST_RUN_BASIC, &m);
         if (IN_SET(r, -EPERM, -EACCES)) {
@@ -63,13 +63,13 @@ TEST_RET(cgroup_mask, .sd_booted = true) {
         assert_se(manager_startup(m, NULL, NULL, NULL) >= 0);
 
         /* Load units and verify hierarchy. */
-        assert_se(manager_load_startable_unit_or_warn(m, "parent.slice", NULL, &parent) >= 0);
-        assert_se(manager_load_startable_unit_or_warn(m, "son.service", NULL, &son) >= 0);
-        assert_se(manager_load_startable_unit_or_warn(m, "daughter.service", NULL, &daughter) >= 0);
-        assert_se(manager_load_startable_unit_or_warn(m, "grandchild.service", NULL, &grandchild) >= 0);
-        assert_se(manager_load_startable_unit_or_warn(m, "parent-deep.slice", NULL, &parent_deep) >= 0);
-        assert_se(manager_load_startable_unit_or_warn(m, "nomem.slice", NULL, &nomem_parent) >= 0);
-        assert_se(manager_load_startable_unit_or_warn(m, "nomemleaf.service", NULL, &nomem_leaf) >= 0);
+        ASSERT_OK(manager_load_startable_unit_or_warn(m, "parent.slice", NULL, &parent));
+        ASSERT_OK(manager_load_startable_unit_or_warn(m, "son.service", NULL, &son));
+        ASSERT_OK(manager_load_startable_unit_or_warn(m, "daughter.service", NULL, &daughter));
+        ASSERT_OK(manager_load_startable_unit_or_warn(m, "grandchild.service", NULL, &grandchild));
+        ASSERT_OK(manager_load_startable_unit_or_warn(m, "parent-deep.slice", NULL, &parent_deep));
+        ASSERT_OK(manager_load_startable_unit_or_warn(m, "nomem.slice", NULL, &nomem_parent));
+        ASSERT_OK(manager_load_startable_unit_or_warn(m, "nomemleaf.service", NULL, &nomem_leaf));
         assert_se(UNIT_GET_SLICE(son) == parent);
         assert_se(UNIT_GET_SLICE(daughter) == parent);
         assert_se(UNIT_GET_SLICE(parent_deep) == parent);
@@ -135,7 +135,7 @@ static void test_cg_mask_to_string_one(CGroupMask mask, const char *t) {
         _cleanup_free_ char *b = NULL;
 
         assert_se(cg_mask_to_string(mask, &b) >= 0);
-        assert_se(streq_ptr(b, t));
+        ASSERT_STREQ(b, t);
 }
 
 TEST(cg_mask_to_string) {
@@ -157,7 +157,7 @@ TEST(cg_mask_to_string) {
 }
 
 static void cgroup_device_permissions_test_normalize(const char *a, const char *b) {
-        assert_se(streq_ptr(cgroup_device_permissions_to_string(cgroup_device_permissions_from_string(a)), b));
+        ASSERT_STREQ(cgroup_device_permissions_to_string(cgroup_device_permissions_from_string(a)), b);
 }
 
 TEST(cgroup_device_permissions) {
index ada32cbeb58959e99b3f0e6f2d583e59efdae277..8b5d02d5b79089638f0c51eb615d08fe3bfbd0ae 100644 (file)
@@ -14,7 +14,7 @@ static void test_is_wanted_print_one(bool header) {
         _cleanup_free_ char *cmdline = NULL;
 
         log_info("-- %s --", __func__);
-        assert_se(proc_cmdline(&cmdline) >= 0);
+        ASSERT_OK(proc_cmdline(&cmdline));
         log_info("cmdline: %s", cmdline);
         if (header)
                 (void) system("findmnt -n /sys/fs/cgroup");
@@ -31,33 +31,33 @@ TEST(is_wanted_print) {
 }
 
 TEST(is_wanted) {
-        assert_se(setenv("SYSTEMD_PROC_CMDLINE",
-                         "systemd.unified_cgroup_hierarchy", 1) >= 0);
+        ASSERT_OK(setenv("SYSTEMD_PROC_CMDLINE",
+                         "systemd.unified_cgroup_hierarchy", 1));
         test_is_wanted_print_one(false);
 
-        assert_se(setenv("SYSTEMD_PROC_CMDLINE",
-                         "systemd.unified_cgroup_hierarchy=0", 1) >= 0);
+        ASSERT_OK(setenv("SYSTEMD_PROC_CMDLINE",
+                         "systemd.unified_cgroup_hierarchy=0", 1));
         test_is_wanted_print_one(false);
 
-        assert_se(setenv("SYSTEMD_PROC_CMDLINE",
+        ASSERT_OK(setenv("SYSTEMD_PROC_CMDLINE",
                          "systemd.unified_cgroup_hierarchy=0 "
-                         "systemd.legacy_systemd_cgroup_controller", 1) >= 0);
+                         "systemd.legacy_systemd_cgroup_controller", 1));
         test_is_wanted_print_one(false);
 
-        assert_se(setenv("SYSTEMD_PROC_CMDLINE",
+        ASSERT_OK(setenv("SYSTEMD_PROC_CMDLINE",
                          "systemd.unified_cgroup_hierarchy=0 "
-                         "systemd.legacy_systemd_cgroup_controller=0", 1) >= 0);
+                         "systemd.legacy_systemd_cgroup_controller=0", 1));
         test_is_wanted_print_one(false);
 
         /* cgroup_no_v1=all implies unified cgroup hierarchy, unless otherwise
          * explicitly specified. */
-        assert_se(setenv("SYSTEMD_PROC_CMDLINE",
-                         "cgroup_no_v1=all", 1) >= 0);
+        ASSERT_OK(setenv("SYSTEMD_PROC_CMDLINE",
+                         "cgroup_no_v1=all", 1));
         test_is_wanted_print_one(false);
 
-        assert_se(setenv("SYSTEMD_PROC_CMDLINE",
+        ASSERT_OK(setenv("SYSTEMD_PROC_CMDLINE",
                          "cgroup_no_v1=all "
-                         "systemd.unified_cgroup_hierarchy=0", 1) >= 0);
+                         "systemd.unified_cgroup_hierarchy=0", 1));
         test_is_wanted_print_one(false);
 }
 
index 62618ce56246fa43e501c3fa8364698120adefbc..97101d40a5e9ac1bca8dfe452226002eba7662d4 100644 (file)
@@ -23,8 +23,8 @@ TEST_RET(default_memory_low, .sd_booted = true) {
                 return log_tests_skipped("cgroupfs not available");
 
         _cleanup_free_ char *unit_dir = NULL;
-        assert_se(get_testdata_dir("units", &unit_dir) >= 0);
-        assert_se(set_unit_path(unit_dir) >= 0);
+        ASSERT_OK(get_testdata_dir("units", &unit_dir));
+        ASSERT_OK(set_unit_path(unit_dir));
         assert_se(runtime_dir = setup_fake_runtime_dir());
         r = manager_new(RUNTIME_SCOPE_USER, MANAGER_TEST_RUN_BASIC, &m);
         if (IN_SET(r, -EPERM, -EACCES)) {
@@ -32,8 +32,8 @@ TEST_RET(default_memory_low, .sd_booted = true) {
                 return log_tests_skipped("cannot create manager");
         }
 
-        assert_se(r >= 0);
-        assert_se(manager_startup(m, NULL, NULL, NULL) >= 0);
+        ASSERT_OK(r);
+        ASSERT_OK(manager_startup(m, NULL, NULL, NULL));
 
         /* dml.slice has DefaultMemoryLow=50. Beyond that, individual subhierarchies look like this:
          *
@@ -88,27 +88,27 @@ TEST_RET(default_memory_low, .sd_booted = true) {
          *    │ dml-discard-empty.service │  │ dml-discard-set-ml.service │
          *    └───────────────────────────┘  └────────────────────────────┘
          */
-        assert_se(manager_load_startable_unit_or_warn(m, "dml.slice", NULL, &dml) >= 0);
+        ASSERT_OK(manager_load_startable_unit_or_warn(m, "dml.slice", NULL, &dml));
 
-        assert_se(manager_load_startable_unit_or_warn(m, "dml-passthrough.slice", NULL, &dml_passthrough) >= 0);
+        ASSERT_OK(manager_load_startable_unit_or_warn(m, "dml-passthrough.slice", NULL, &dml_passthrough));
         assert_se(UNIT_GET_SLICE(dml_passthrough) == dml);
-        assert_se(manager_load_startable_unit_or_warn(m, "dml-passthrough-empty.service", NULL, &dml_passthrough_empty) >= 0);
+        ASSERT_OK(manager_load_startable_unit_or_warn(m, "dml-passthrough-empty.service", NULL, &dml_passthrough_empty));
         assert_se(UNIT_GET_SLICE(dml_passthrough_empty) == dml_passthrough);
-        assert_se(manager_load_startable_unit_or_warn(m, "dml-passthrough-set-dml.service", NULL, &dml_passthrough_set_dml) >= 0);
+        ASSERT_OK(manager_load_startable_unit_or_warn(m, "dml-passthrough-set-dml.service", NULL, &dml_passthrough_set_dml));
         assert_se(UNIT_GET_SLICE(dml_passthrough_set_dml) == dml_passthrough);
-        assert_se(manager_load_startable_unit_or_warn(m, "dml-passthrough-set-ml.service", NULL, &dml_passthrough_set_ml) >= 0);
+        ASSERT_OK(manager_load_startable_unit_or_warn(m, "dml-passthrough-set-ml.service", NULL, &dml_passthrough_set_ml));
         assert_se(UNIT_GET_SLICE(dml_passthrough_set_ml) == dml_passthrough);
 
-        assert_se(manager_load_startable_unit_or_warn(m, "dml-override.slice", NULL, &dml_override) >= 0);
+        ASSERT_OK(manager_load_startable_unit_or_warn(m, "dml-override.slice", NULL, &dml_override));
         assert_se(UNIT_GET_SLICE(dml_override) == dml);
-        assert_se(manager_load_startable_unit_or_warn(m, "dml-override-empty.service", NULL, &dml_override_empty) >= 0);
+        ASSERT_OK(manager_load_startable_unit_or_warn(m, "dml-override-empty.service", NULL, &dml_override_empty));
         assert_se(UNIT_GET_SLICE(dml_override_empty) == dml_override);
 
-        assert_se(manager_load_startable_unit_or_warn(m, "dml-discard.slice", NULL, &dml_discard) >= 0);
+        ASSERT_OK(manager_load_startable_unit_or_warn(m, "dml-discard.slice", NULL, &dml_discard));
         assert_se(UNIT_GET_SLICE(dml_discard) == dml);
-        assert_se(manager_load_startable_unit_or_warn(m, "dml-discard-empty.service", NULL, &dml_discard_empty) >= 0);
+        ASSERT_OK(manager_load_startable_unit_or_warn(m, "dml-discard-empty.service", NULL, &dml_discard_empty));
         assert_se(UNIT_GET_SLICE(dml_discard_empty) == dml_discard);
-        assert_se(manager_load_startable_unit_or_warn(m, "dml-discard-set-ml.service", NULL, &dml_discard_set_ml) >= 0);
+        ASSERT_OK(manager_load_startable_unit_or_warn(m, "dml-discard-set-ml.service", NULL, &dml_discard_set_ml));
         assert_se(UNIT_GET_SLICE(dml_discard_set_ml) == dml_discard);
 
         assert_se(root = UNIT_GET_SLICE(dml));
index 4e7805b40a5db2f275a8b72792390d501437bf68..1d8f99cc6e58402510722f1a9fb316d902bceeb3 100644 (file)
@@ -23,7 +23,7 @@ static void check_p_d_u(const char *path, int code, const char *result) {
         r = cg_path_decode_unit(path, &unit);
         printf("%s: %s → %s %d expected %s %d\n", __func__, path, unit, r, strnull(result), code);
         assert_se(r == code);
-        assert_se(streq_ptr(unit, result));
+        ASSERT_STREQ(unit, result);
 }
 
 TEST(path_decode_unit) {
@@ -45,7 +45,7 @@ static void check_p_g_u(const char *path, int code, const char *result) {
         r = cg_path_get_unit(path, &unit);
         printf("%s: %s → %s %d expected %s %d\n", __func__, path, unit, r, strnull(result), code);
         assert_se(r == code);
-        assert_se(streq_ptr(unit, result));
+        ASSERT_STREQ(unit, result);
 }
 
 TEST(path_get_unit) {
@@ -69,7 +69,7 @@ static void check_p_g_u_p(const char *path, int code, const char *result) {
         r = cg_path_get_unit_path(path, &unit_path);
         printf("%s: %s → %s %d expected %s %d\n", __func__, path, unit_path, r, strnull(result), code);
         assert_se(r == code);
-        assert_se(streq_ptr(unit_path, result));
+        ASSERT_STREQ(unit_path, result);
 }
 
 TEST(path_get_unit_path) {
@@ -96,7 +96,7 @@ static void check_p_g_u_u(const char *path, int code, const char *result) {
         r = cg_path_get_user_unit(path, &unit);
         printf("%s: %s → %s %d expected %s %d\n", __func__, path, unit, r, strnull(result), code);
         assert_se(r == code);
-        assert_se(streq_ptr(unit, result));
+        ASSERT_STREQ(unit, result);
 }
 
 TEST(path_get_user_unit) {
@@ -119,7 +119,7 @@ static void check_p_g_s(const char *path, int code, const char *result) {
         _cleanup_free_ char *s = NULL;
 
         assert_se(cg_path_get_session(path, &s) == code);
-        assert_se(streq_ptr(s, result));
+        ASSERT_STREQ(s, result);
 }
 
 TEST(path_get_session) {
@@ -146,7 +146,7 @@ static void check_p_g_slice(const char *path, int code, const char *result) {
         _cleanup_free_ char *s = NULL;
 
         assert_se(cg_path_get_slice(path, &s) == code);
-        assert_se(streq_ptr(s, result));
+        ASSERT_STREQ(s, result);
 }
 
 TEST(path_get_slice) {
@@ -163,7 +163,7 @@ static void check_p_g_u_slice(const char *path, int code, const char *result) {
         _cleanup_free_ char *s = NULL;
 
         assert_se(cg_path_get_user_slice(path, &s) == code);
-        assert_se(streq_ptr(s, result));
+        ASSERT_STREQ(s, result);
 }
 
 TEST(path_get_user_slice) {
@@ -194,7 +194,7 @@ TEST(proc) {
         _cleanup_closedir_ DIR *d = NULL;
         int r;
 
-        assert_se(proc_dir_open(&d) >= 0);
+        ASSERT_OK(proc_dir_open(&d));
 
         for (;;) {
                 _cleanup_free_ char *path = NULL, *path_shifted = NULL, *session = NULL, *unit = NULL, *user_unit = NULL, *machine = NULL, *slice = NULL;
@@ -238,10 +238,10 @@ static void test_escape_one(const char *s, const char *expected) {
         assert_se(s);
         assert_se(expected);
 
-        assert_se(cg_escape(s, &b) >= 0);
-        assert_se(streq(b, expected));
+        ASSERT_OK(cg_escape(s, &b));
+        ASSERT_STREQ(b, expected);
 
-        assert_se(streq(cg_unescape(b), s));
+        ASSERT_STREQ(cg_unescape(b), s);
 
         assert_se(filename_is_valid(b));
         assert_se(!cg_needs_escape(s) || b[0] == '_');
@@ -284,7 +284,7 @@ static void test_slice_to_path_one(const char *unit, const char *path, int error
         log_info("actual: %s / %d", strnull(ret), r);
         log_info("expect: %s / %d", strnull(path), error);
         assert_se(r == error);
-        assert_se(streq_ptr(ret, path));
+        ASSERT_STREQ(ret, path);
 }
 
 TEST(slice_to_path) {
@@ -315,8 +315,8 @@ TEST(slice_to_path) {
 static void test_shift_path_one(const char *raw, const char *root, const char *shifted) {
         const char *s = NULL;
 
-        assert_se(cg_shift_path(raw, root, &s) >= 0);
-        assert_se(streq(s, shifted));
+        ASSERT_OK(cg_shift_path(raw, root, &s));
+        ASSERT_STREQ(s, shifted);
 }
 
 TEST(shift_path) {
@@ -329,7 +329,7 @@ TEST(shift_path) {
 TEST(mask_supported, .sd_booted = true) {
         CGroupMask m;
 
-        assert_se(cg_mask_supported(&m) >= 0);
+        ASSERT_OK(cg_mask_supported(&m));
 
         for (CGroupController c = 0; c < _CGROUP_CONTROLLER_MAX; c++)
                 printf("'%s' is supported: %s\n",
@@ -402,7 +402,7 @@ TEST(cg_get_keyed_attribute) {
         }
 
         assert_se(r == -ENOENT);
-        assert_se(val == NULL);
+        ASSERT_NULL(val);
 
         if (access("/sys/fs/cgroup/init.scope/cpu.stat", R_OK) < 0) {
                 log_info_errno(errno, "Skipping most of %s, /init.scope/cpu.stat not accessible: %m", __func__);
@@ -411,7 +411,7 @@ TEST(cg_get_keyed_attribute) {
 
         assert_se(cg_get_keyed_attribute("cpu", "/init.scope", "cpu.stat", STRV_MAKE("no_such_attr"), &val) == -ENXIO);
         assert_se(cg_get_keyed_attribute_graceful("cpu", "/init.scope", "cpu.stat", STRV_MAKE("no_such_attr"), &val) == 0);
-        assert_se(val == NULL);
+        ASSERT_NULL(val);
 
         assert_se(cg_get_keyed_attribute("cpu", "/init.scope", "cpu.stat", STRV_MAKE("usage_usec"), &val) == 0);
         val = mfree(val);
index 9b174d708b562b572793202cf54f7a39743045f4..8bd4af94e4e6f0e73dfd1ae40374ef68a6f9f6ab 100644 (file)
@@ -5,6 +5,7 @@
 #include "cgroup-setup.h"
 #include "cgroup-util.h"
 #include "errno-util.h"
+#include "fd-util.h"
 #include "path-util.h"
 #include "process-util.h"
 #include "string-util.h"
@@ -14,8 +15,8 @@ TEST(cg_split_spec) {
         char *c, *p;
 
         assert_se(cg_split_spec("foobar:/", &c, &p) == 0);
-        assert_se(streq(c, "foobar"));
-        assert_se(streq(p, "/"));
+        ASSERT_STREQ(c, "foobar");
+        ASSERT_STREQ(p, "/");
         c = mfree(c);
         p = mfree(p);
 
@@ -30,13 +31,13 @@ TEST(cg_split_spec) {
         assert_se(cg_split_spec("fo/obar:/", &c, &p) < 0);
 
         assert_se(cg_split_spec("/", &c, &p) >= 0);
-        assert_se(c == NULL);
-        assert_se(streq(p, "/"));
+        ASSERT_NULL(c);
+        ASSERT_STREQ(p, "/");
         p = mfree(p);
 
         assert_se(cg_split_spec("foo", &c, &p) >= 0);
-        assert_se(streq(c, "foo"));
-        assert_se(p == NULL);
+        ASSERT_STREQ(c, "foo");
+        ASSERT_NULL(p);
         c = mfree(c);
 }
 
@@ -78,7 +79,7 @@ TEST(cg_create) {
         assert_se(cg_create_and_attach(SYSTEMD_CGROUP_CONTROLLER, test_b, 0) == 0);
 
         assert_se(cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, getpid_cached(), &path) == 0);
-        assert_se(streq(path, test_b));
+        ASSERT_STREQ(path, test_b);
         free(path);
 
         assert_se(cg_attach(SYSTEMD_CGROUP_CONTROLLER, test_a, 0) == 0);
@@ -129,4 +130,47 @@ TEST(cg_create) {
         assert_se(cg_rmdir(SYSTEMD_CGROUP_CONTROLLER, test_a) == 0);
 }
 
+TEST(id) {
+        _cleanup_free_ char *p = NULL, *p2 = NULL;
+        _cleanup_close_ int fd = -EBADF, fd2 = -EBADF;
+        uint64_t id, id2;
+        int r;
+
+        r = cg_all_unified();
+        if (r == 0) {
+                log_tests_skipped("skipping cgroupid test, not running in unified mode");
+                return;
+        }
+        if (IN_SET(r, -ENOMEDIUM, -ENOENT)) {
+                log_tests_skipped("cgroupfs is not mounted");
+                return;
+        }
+        assert_se(r > 0);
+
+        fd = cg_path_open(SYSTEMD_CGROUP_CONTROLLER, "/");
+        assert_se(fd >= 0);
+
+        assert_se(fd_get_path(fd, &p) >= 0);
+        assert_se(path_equal(p, "/sys/fs/cgroup"));
+
+        assert_se(cg_fd_get_cgroupid(fd, &id) >= 0);
+
+        fd2 = cg_cgroupid_open(fd, id);
+
+        if (ERRNO_IS_NEG_PRIVILEGE(fd2))
+                log_notice("Skipping open-by-cgroup-id test because lacking privs.");
+        else {
+                assert_se(fd2 >= 0);
+
+                assert_se(fd_get_path(fd2, &p2) >= 0);
+                assert_se(path_equal(p2, "/sys/fs/cgroup"));
+
+                assert_se(cg_fd_get_cgroupid(fd2, &id2) >= 0);
+
+                assert_se(id == id2);
+
+                assert_se(inode_same_at(fd, NULL, fd2, NULL, AT_EMPTY_PATH) > 0);
+        }
+}
+
 DEFINE_TEST_MAIN(LOG_DEBUG);
index dbbc99bf81b44671df3ce51dca91c37fa901a982..5d3ee7aceecc43146dbe55368189c24bc54d5c1c 100644 (file)
@@ -23,11 +23,11 @@ static void test_chase_extract_filename_one(const char *path, const char *root,
         log_debug("/* %s(path=%s, root=%s) */", __func__, path, strnull(root));
 
         assert_se(chase(path, root, CHASE_EXTRACT_FILENAME, &ret1, NULL) > 0);
-        assert_se(streq(ret1, expected));
+        ASSERT_STREQ(ret1, expected);
 
         assert_se(chase(path, root, 0, &ret2, NULL) > 0);
-        assert_se(chase_extract_filename(ret2, root, &fname) >= 0);
-        assert_se(streq(fname, expected));
+        ASSERT_OK(chase_extract_filename(ret2, root, &fname));
+        ASSERT_STREQ(fname, expected);
 }
 
 TEST(chase) {
@@ -42,7 +42,7 @@ TEST(chase) {
         assert_se(mkdtemp(temp));
 
         top = strjoina(temp, "/top");
-        assert_se(mkdir(top, 0700) >= 0);
+        ASSERT_OK(mkdir(top, 0700));
 
         p = strjoina(top, "/dot");
         if (symlink(".", p) < 0) {
@@ -52,19 +52,19 @@ TEST(chase) {
         };
 
         p = strjoina(top, "/dotdot");
-        assert_se(symlink("..", p) >= 0);
+        ASSERT_OK(symlink("..", p));
 
         p = strjoina(top, "/dotdota");
-        assert_se(symlink("../a", p) >= 0);
+        ASSERT_OK(symlink("../a", p));
 
         p = strjoina(temp, "/a");
-        assert_se(symlink("b", p) >= 0);
+        ASSERT_OK(symlink("b", p));
 
         p = strjoina(temp, "/b");
-        assert_se(symlink("/usr", p) >= 0);
+        ASSERT_OK(symlink("/usr", p));
 
         p = strjoina(temp, "/start");
-        assert_se(symlink("top/dot/dotdota", p) >= 0);
+        ASSERT_OK(symlink("top/dot/dotdota", p));
 
         /* Paths that use symlinks underneath the "root" */
 
@@ -104,7 +104,7 @@ TEST(chase) {
         assert_se(path_equal(result, qslash));
         result = mfree(result);
 
-        assert_se(mkdir(q, 0700) >= 0);
+        ASSERT_OK(mkdir(q, 0700));
 
         r = chase(p, temp, 0, &result, NULL);
         assert_se(r > 0);
@@ -145,21 +145,21 @@ TEST(chase) {
         /* Paths that would "escape" outside of the "root" */
 
         p = strjoina(temp, "/6dots");
-        assert_se(symlink("../../..", p) >= 0);
+        ASSERT_OK(symlink("../../..", p));
 
         r = chase(p, temp, 0, &result, NULL);
         assert_se(r > 0 && path_equal(result, temp));
         result = mfree(result);
 
         p = strjoina(temp, "/6dotsusr");
-        assert_se(symlink("../../../usr", p) >= 0);
+        ASSERT_OK(symlink("../../../usr", p));
 
         r = chase(p, temp, 0, &result, NULL);
         assert_se(r > 0 && path_equal(result, q));
         result = mfree(result);
 
         p = strjoina(temp, "/top/8dotsusr");
-        assert_se(symlink("../../../../usr", p) >= 0);
+        ASSERT_OK(symlink("../../../../usr", p));
 
         r = chase(p, temp, 0, &result, NULL);
         assert_se(r > 0 && path_equal(result, q));
@@ -168,12 +168,12 @@ TEST(chase) {
         /* Paths that contain repeated slashes */
 
         p = strjoina(temp, "/slashslash");
-        assert_se(symlink("///usr///", p) >= 0);
+        ASSERT_OK(symlink("///usr///", p));
 
         r = chase(p, NULL, 0, &result, NULL);
         assert_se(r > 0);
         assert_se(path_equal(result, "/usr"));
-        assert_se(streq(result, "/usr")); /* we guarantee that we drop redundant slashes */
+        ASSERT_STREQ(result, "/usr"); /* we guarantee that we drop redundant slashes */
         result = mfree(result);
 
         r = chase(p, temp, 0, &result, NULL);
@@ -185,14 +185,14 @@ TEST(chase) {
 
         if (geteuid() == 0) {
                 p = strjoina(temp, "/user");
-                assert_se(mkdir(p, 0755) >= 0);
-                assert_se(chown(p, UID_NOBODY, GID_NOBODY) >= 0);
+                ASSERT_OK(mkdir(p, 0755));
+                ASSERT_OK(chown(p, UID_NOBODY, GID_NOBODY));
 
                 q = strjoina(temp, "/user/root");
-                assert_se(mkdir(q, 0755) >= 0);
+                ASSERT_OK(mkdir(q, 0755));
 
                 p = strjoina(q, "/link");
-                assert_se(symlink("/", p) >= 0);
+                ASSERT_OK(symlink("/", p));
 
                 /* Fail when user-owned directories contain root-owned subdirectories. */
                 r = chase(p, temp, CHASE_SAFE, &result, NULL);
@@ -218,22 +218,22 @@ TEST(chase) {
 
         r = chase("/../.././//../../etc", NULL, 0, &result, NULL);
         assert_se(r > 0);
-        assert_se(streq(result, "/etc"));
+        ASSERT_STREQ(result, "/etc");
         result = mfree(result);
 
         r = chase("/../.././//../../test-chase.fsldajfl", NULL, CHASE_NONEXISTENT, &result, NULL);
         assert_se(r == 0);
-        assert_se(streq(result, "/test-chase.fsldajfl"));
+        ASSERT_STREQ(result, "/test-chase.fsldajfl");
         result = mfree(result);
 
         r = chase("/../.././//../../etc", "/", CHASE_PREFIX_ROOT, &result, NULL);
         assert_se(r > 0);
-        assert_se(streq(result, "/etc"));
+        ASSERT_STREQ(result, "/etc");
         result = mfree(result);
 
         r = chase("/../.././//../../test-chase.fsldajfl", "/", CHASE_PREFIX_ROOT|CHASE_NONEXISTENT, &result, NULL);
         assert_se(r == 0);
-        assert_se(streq(result, "/test-chase.fsldajfl"));
+        ASSERT_STREQ(result, "/test-chase.fsldajfl");
         result = mfree(result);
 
         r = chase("/etc/machine-id/foo", NULL, 0, &result, NULL);
@@ -243,7 +243,7 @@ TEST(chase) {
         /* Path that loops back to self */
 
         p = strjoina(temp, "/recursive-symlink");
-        assert_se(symlink("recursive-symlink", p) >= 0);
+        ASSERT_OK(symlink("recursive-symlink", p));
         r = chase(p, NULL, 0, &result, NULL);
         assert_se(r == -ELOOP);
 
@@ -269,9 +269,9 @@ TEST(chase) {
 
         /* Relative paths */
 
-        assert_se(safe_getcwd(&pwd) >= 0);
+        ASSERT_OK(safe_getcwd(&pwd));
 
-        assert_se(chdir(temp) >= 0);
+        ASSERT_OK(chdir(temp));
 
         p = "this/is/a/relative/path";
         r = chase(p, NULL, CHASE_NONEXISTENT, &result, NULL);
@@ -309,46 +309,46 @@ TEST(chase) {
 
         if (geteuid() == 0) {
                 p = strjoina(temp, "/priv1");
-                assert_se(mkdir(p, 0755) >= 0);
+                ASSERT_OK(mkdir(p, 0755));
 
                 q = strjoina(p, "/priv2");
-                assert_se(mkdir(q, 0755) >= 0);
+                ASSERT_OK(mkdir(q, 0755));
 
-                assert_se(chase(q, NULL, CHASE_SAFE, NULL, NULL) >= 0);
+                ASSERT_OK(chase(q, NULL, CHASE_SAFE, NULL, NULL));
 
-                assert_se(chown(q, UID_NOBODY, GID_NOBODY) >= 0);
-                assert_se(chase(q, NULL, CHASE_SAFE, NULL, NULL) >= 0);
+                ASSERT_OK(chown(q, UID_NOBODY, GID_NOBODY));
+                ASSERT_OK(chase(q, NULL, CHASE_SAFE, NULL, NULL));
 
-                assert_se(chown(p, UID_NOBODY, GID_NOBODY) >= 0);
-                assert_se(chase(q, NULL, CHASE_SAFE, NULL, NULL) >= 0);
+                ASSERT_OK(chown(p, UID_NOBODY, GID_NOBODY));
+                ASSERT_OK(chase(q, NULL, CHASE_SAFE, NULL, NULL));
 
                 assert_se(chown(q, 0, 0) >= 0);
                 assert_se(chase(q, NULL, CHASE_SAFE, NULL, NULL) == -ENOLINK);
 
-                assert_se(rmdir(q) >= 0);
-                assert_se(symlink("/etc/passwd", q) >= 0);
+                ASSERT_OK(rmdir(q));
+                ASSERT_OK(symlink("/etc/passwd", q));
                 assert_se(chase(q, NULL, CHASE_SAFE, NULL, NULL) == -ENOLINK);
 
                 assert_se(chown(p, 0, 0) >= 0);
-                assert_se(chase(q, NULL, CHASE_SAFE, NULL, NULL) >= 0);
+                ASSERT_OK(chase(q, NULL, CHASE_SAFE, NULL, NULL));
         }
 
         p = strjoina(temp, "/machine-id-test");
-        assert_se(symlink("/usr/../etc/./machine-id", p) >= 0);
+        ASSERT_OK(symlink("/usr/../etc/./machine-id", p));
 
         r = chase(p, NULL, 0, NULL, &pfd);
         if (r != -ENOENT && sd_id128_get_machine(NULL) >= 0) {
                 _cleanup_close_ int fd = -EBADF;
                 sd_id128_t a, b;
 
-                assert_se(pfd >= 0);
+                ASSERT_OK(pfd);
 
                 fd = fd_reopen(pfd, O_RDONLY|O_CLOEXEC);
-                assert_se(fd >= 0);
+                ASSERT_OK(fd);
                 safe_close(pfd);
 
-                assert_se(id128_read_fd(fd, ID128_FORMAT_PLAIN, &a) >= 0);
-                assert_se(sd_id128_get_machine(&b) >= 0);
+                ASSERT_OK(id128_read_fd(fd, ID128_FORMAT_PLAIN, &a));
+                ASSERT_OK(sd_id128_get_machine(&b));
                 assert_se(sd_id128_equal(a, b));
         }
 
@@ -365,24 +365,24 @@ TEST(chase) {
         q = strjoina(temp, "/symlink");
         assert_se(symlink(p, q) >= 0);
         r = chase(q, NULL, CHASE_NOFOLLOW, &result, &pfd);
-        assert_se(r >= 0);
-        assert_se(pfd >= 0);
+        ASSERT_OK(r);
+        ASSERT_OK(pfd);
         assert_se(path_equal(result, q));
-        assert_se(fstat(pfd, &st) >= 0);
+        ASSERT_OK(fstat(pfd, &st));
         assert_se(S_ISLNK(st.st_mode));
         result = mfree(result);
         pfd = safe_close(pfd);
 
         /* s1 -> s2 -> nonexistent */
         q = strjoina(temp, "/s1");
-        assert_se(symlink("s2", q) >= 0);
+        ASSERT_OK(symlink("s2", q));
         p = strjoina(temp, "/s2");
-        assert_se(symlink("nonexistent", p) >= 0);
+        ASSERT_OK(symlink("nonexistent", p));
         r = chase(q, NULL, CHASE_NOFOLLOW, &result, &pfd);
-        assert_se(r >= 0);
-        assert_se(pfd >= 0);
+        ASSERT_OK(r);
+        ASSERT_OK(pfd);
         assert_se(path_equal(result, q));
-        assert_se(fstat(pfd, &st) >= 0);
+        ASSERT_OK(fstat(pfd, &st));
         assert_se(S_ISLNK(st.st_mode));
         result = mfree(result);
         pfd = safe_close(pfd);
@@ -393,41 +393,41 @@ TEST(chase) {
         r = chase(p, NULL, CHASE_STEP, &result, NULL);
         assert_se(r == 0);
         p = strjoina(temp, "/top/dot/dotdota");
-        assert_se(streq(p, result));
+        ASSERT_STREQ(p, result);
         result = mfree(result);
 
         r = chase(p, NULL, CHASE_STEP, &result, NULL);
         assert_se(r == 0);
         p = strjoina(temp, "/top/dotdota");
-        assert_se(streq(p, result));
+        ASSERT_STREQ(p, result);
         result = mfree(result);
 
         r = chase(p, NULL, CHASE_STEP, &result, NULL);
         assert_se(r == 0);
         p = strjoina(temp, "/top/../a");
-        assert_se(streq(p, result));
+        ASSERT_STREQ(p, result);
         result = mfree(result);
 
         r = chase(p, NULL, CHASE_STEP, &result, NULL);
         assert_se(r == 0);
         p = strjoina(temp, "/a");
-        assert_se(streq(p, result));
+        ASSERT_STREQ(p, result);
         result = mfree(result);
 
         r = chase(p, NULL, CHASE_STEP, &result, NULL);
         assert_se(r == 0);
         p = strjoina(temp, "/b");
-        assert_se(streq(p, result));
+        ASSERT_STREQ(p, result);
         result = mfree(result);
 
         r = chase(p, NULL, CHASE_STEP, &result, NULL);
         assert_se(r == 0);
-        assert_se(streq("/usr", result));
+        ASSERT_STREQ("/usr", result);
         result = mfree(result);
 
         r = chase("/usr", NULL, CHASE_STEP, &result, NULL);
         assert_se(r > 0);
-        assert_se(streq("/usr", result));
+        ASSERT_STREQ("/usr", result);
         result = mfree(result);
 
         /* Make sure that symlinks in the "root" path are not resolved, but those below are */
@@ -449,7 +449,7 @@ TEST(chase) {
         assert_se(chase("top/dot/dot", temp, CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS|CHASE_WARN, NULL, NULL) == -EREMCHG);
 
  cleanup:
-        assert_se(rm_rf(temp, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0);
+        ASSERT_OK(rm_rf(temp, REMOVE_ROOT|REMOVE_PHYSICAL));
 }
 
 TEST(chaseat) {
@@ -461,29 +461,29 @@ TEST(chaseat) {
         struct stat st;
         const char *p;
 
-        assert_se((tfd = mkdtemp_open(NULL, 0, &t)) >= 0);
+        ASSERT_OK((tfd = mkdtemp_open(NULL, 0, &t)));
 
         /* Test that AT_FDCWD with CHASE_AT_RESOLVE_IN_ROOT resolves against / and not the current working
          * directory. */
 
-        assert_se(symlinkat("/usr", tfd, "abc") >= 0);
+        ASSERT_OK(symlinkat("/usr", tfd, "abc"));
 
         p = strjoina(t, "/abc");
-        assert_se(chaseat(AT_FDCWD, p, CHASE_AT_RESOLVE_IN_ROOT, &result, NULL) >= 0);
-        assert_se(streq(result, "/usr"));
+        ASSERT_OK(chaseat(AT_FDCWD, p, CHASE_AT_RESOLVE_IN_ROOT, &result, NULL));
+        ASSERT_STREQ(result, "/usr");
         result = mfree(result);
 
         /* If the file descriptor points to the root directory, the result will be absolute. */
 
         fd = open("/", O_CLOEXEC | O_DIRECTORY | O_PATH);
-        assert_se(fd >= 0);
+        ASSERT_OK(fd);
 
-        assert_se(chaseat(fd, p, 0, &result, NULL) >= 0);
-        assert_se(streq(result, "/usr"));
+        ASSERT_OK(chaseat(fd, p, 0, &result, NULL));
+        ASSERT_STREQ(result, "/usr");
         result = mfree(result);
 
-        assert_se(chaseat(fd, p, CHASE_AT_RESOLVE_IN_ROOT, &result, NULL) >= 0);
-        assert_se(streq(result, "/usr"));
+        ASSERT_OK(chaseat(fd, p, CHASE_AT_RESOLVE_IN_ROOT, &result, NULL));
+        ASSERT_STREQ(result, "/usr");
         result = mfree(result);
 
         fd = safe_close(fd);
@@ -491,36 +491,36 @@ TEST(chaseat) {
         /* If the file descriptor does not point to the root directory, the result will be relative
          * unless the result is outside of the specified file descriptor. */
 
-        assert_se(chaseat(tfd, "abc", 0, &result, NULL) >= 0);
-        assert_se(streq(result, "/usr"));
+        ASSERT_OK(chaseat(tfd, "abc", 0, &result, NULL));
+        ASSERT_STREQ(result, "/usr");
         result = mfree(result);
 
-        assert_se(chaseat(tfd, "/abc", 0, &result, NULL) >= 0);
-        assert_se(streq(result, "/usr"));
+        ASSERT_OK(chaseat(tfd, "/abc", 0, &result, NULL));
+        ASSERT_STREQ(result, "/usr");
         result = mfree(result);
 
         assert_se(chaseat(tfd, "abc", CHASE_AT_RESOLVE_IN_ROOT, NULL, NULL) == -ENOENT);
         assert_se(chaseat(tfd, "/abc", CHASE_AT_RESOLVE_IN_ROOT, NULL, NULL) == -ENOENT);
 
-        assert_se(chaseat(tfd, "abc", CHASE_AT_RESOLVE_IN_ROOT | CHASE_NONEXISTENT, &result, NULL) >= 0);
-        assert_se(streq(result, "usr"));
+        ASSERT_OK(chaseat(tfd, "abc", CHASE_AT_RESOLVE_IN_ROOT | CHASE_NONEXISTENT, &result, NULL));
+        ASSERT_STREQ(result, "usr");
         result = mfree(result);
 
-        assert_se(chaseat(tfd, "/abc", CHASE_AT_RESOLVE_IN_ROOT | CHASE_NONEXISTENT, &result, NULL) >= 0);
-        assert_se(streq(result, "usr"));
+        ASSERT_OK(chaseat(tfd, "/abc", CHASE_AT_RESOLVE_IN_ROOT | CHASE_NONEXISTENT, &result, NULL));
+        ASSERT_STREQ(result, "usr");
         result = mfree(result);
 
         /* Test that absolute path or not are the same when resolving relative to a directory file
          * descriptor and that we always get a relative path back. */
 
-        assert_se(fd = openat(tfd, "def", O_CREAT|O_CLOEXEC, 0700) >= 0);
+        ASSERT_OK(fd = openat(tfd, "def", O_CREAT|O_CLOEXEC, 0700));
         fd = safe_close(fd);
-        assert_se(symlinkat("/def", tfd, "qed") >= 0);
-        assert_se(chaseat(tfd, "qed", CHASE_AT_RESOLVE_IN_ROOT, &result, NULL) >= 0);
-        assert_se(streq(result, "def"));
+        ASSERT_OK(symlinkat("/def", tfd, "qed"));
+        ASSERT_OK(chaseat(tfd, "qed", CHASE_AT_RESOLVE_IN_ROOT, &result, NULL));
+        ASSERT_STREQ(result, "def");
         result = mfree(result);
-        assert_se(chaseat(tfd, "/qed", CHASE_AT_RESOLVE_IN_ROOT, &result, NULL) >= 0);
-        assert_se(streq(result, "def"));
+        ASSERT_OK(chaseat(tfd, "/qed", CHASE_AT_RESOLVE_IN_ROOT, &result, NULL));
+        ASSERT_STREQ(result, "def");
         result = mfree(result);
 
         /* Valid directory file descriptor without CHASE_AT_RESOLVE_IN_ROOT should resolve symlinks against
@@ -529,157 +529,157 @@ TEST(chaseat) {
 
         /* Test CHASE_PARENT */
 
-        assert_se((fd = open_mkdir_at(tfd, "chase", O_CLOEXEC, 0755)) >= 0);
-        assert_se(symlinkat("/def", fd, "parent") >= 0);
+        ASSERT_OK((fd = open_mkdir_at(tfd, "chase", O_CLOEXEC, 0755)));
+        ASSERT_OK(symlinkat("/def", fd, "parent"));
         fd = safe_close(fd);
 
         /* Make sure that when we chase a symlink parent directory, that we chase the parent directory of the
          * symlink target and not the symlink itself. But if we add CHASE_NOFOLLOW, we get the parent
          * directory of the symlink itself. */
 
-        assert_se(chaseat(tfd, "chase/parent", CHASE_PARENT|CHASE_AT_RESOLVE_IN_ROOT, &result, &fd) >= 0);
-        assert_se(faccessat(fd, "def", F_OK, 0) >= 0);
-        assert_se(streq(result, "def"));
+        ASSERT_OK(chaseat(tfd, "chase/parent", CHASE_PARENT|CHASE_AT_RESOLVE_IN_ROOT, &result, &fd));
+        ASSERT_OK(faccessat(fd, "def", F_OK, 0));
+        ASSERT_STREQ(result, "def");
         fd = safe_close(fd);
         result = mfree(result);
 
-        assert_se(chaseat(tfd, "chase/parent", CHASE_AT_RESOLVE_IN_ROOT|CHASE_PARENT|CHASE_NOFOLLOW, &result, &fd) >= 0);
-        assert_se(faccessat(fd, "parent", F_OK, AT_SYMLINK_NOFOLLOW) >= 0);
-        assert_se(streq(result, "chase/parent"));
+        ASSERT_OK(chaseat(tfd, "chase/parent", CHASE_AT_RESOLVE_IN_ROOT|CHASE_PARENT|CHASE_NOFOLLOW, &result, &fd));
+        ASSERT_OK(faccessat(fd, "parent", F_OK, AT_SYMLINK_NOFOLLOW));
+        ASSERT_STREQ(result, "chase/parent");
         fd = safe_close(fd);
         result = mfree(result);
 
-        assert_se(chaseat(tfd, "chase", CHASE_PARENT|CHASE_AT_RESOLVE_IN_ROOT, &result, &fd) >= 0);
-        assert_se(faccessat(fd, "chase", F_OK, 0) >= 0);
-        assert_se(streq(result, "chase"));
+        ASSERT_OK(chaseat(tfd, "chase", CHASE_PARENT|CHASE_AT_RESOLVE_IN_ROOT, &result, &fd));
+        ASSERT_OK(faccessat(fd, "chase", F_OK, 0));
+        ASSERT_STREQ(result, "chase");
         fd = safe_close(fd);
         result = mfree(result);
 
-        assert_se(chaseat(tfd, "/", CHASE_PARENT|CHASE_AT_RESOLVE_IN_ROOT, &result, NULL) >= 0);
-        assert_se(streq(result, "."));
+        ASSERT_OK(chaseat(tfd, "/", CHASE_PARENT|CHASE_AT_RESOLVE_IN_ROOT, &result, NULL));
+        ASSERT_STREQ(result, ".");
         result = mfree(result);
 
-        assert_se(chaseat(tfd, ".", CHASE_PARENT|CHASE_AT_RESOLVE_IN_ROOT, &result, NULL) >= 0);
-        assert_se(streq(result, "."));
+        assert_se(chaseat(tfd, ".", CHASE_PARENT|CHASE_AT_RESOLVE_IN_ROOT, &result, NULL));
+        ASSERT_STREQ(result, ".");
         result = mfree(result);
 
         /* Test CHASE_MKDIR_0755 */
 
-        assert_se(chaseat(tfd, "m/k/d/i/r", CHASE_MKDIR_0755|CHASE_NONEXISTENT, &result, NULL) >= 0);
-        assert_se(faccessat(tfd, "m/k/d/i", F_OK, 0) >= 0);
+        ASSERT_OK(chaseat(tfd, "m/k/d/i/r", CHASE_MKDIR_0755|CHASE_NONEXISTENT, &result, NULL));
+        ASSERT_OK(faccessat(tfd, "m/k/d/i", F_OK, 0));
         assert_se(RET_NERRNO(faccessat(tfd, "m/k/d/i/r", F_OK, 0)) == -ENOENT);
-        assert_se(streq(result, "m/k/d/i/r"));
+        ASSERT_STREQ(result, "m/k/d/i/r");
         result = mfree(result);
 
-        assert_se(chaseat(tfd, "m/../q", CHASE_MKDIR_0755|CHASE_NONEXISTENT, &result, NULL) >= 0);
-        assert_se(faccessat(tfd, "m", F_OK, 0) >= 0);
+        ASSERT_OK(chaseat(tfd, "m/../q", CHASE_MKDIR_0755|CHASE_NONEXISTENT, &result, NULL));
+        ASSERT_OK(faccessat(tfd, "m", F_OK, 0));
         assert_se(RET_NERRNO(faccessat(tfd, "q", F_OK, 0)) == -ENOENT);
-        assert_se(streq(result, "q"));
+        ASSERT_STREQ(result, "q");
         result = mfree(result);
 
         assert_se(chaseat(tfd, "i/../p", CHASE_MKDIR_0755|CHASE_NONEXISTENT, NULL, NULL) == -ENOENT);
 
         /* Test CHASE_EXTRACT_FILENAME */
 
-        assert_se(chaseat(tfd, "chase/parent", CHASE_AT_RESOLVE_IN_ROOT|CHASE_PARENT|CHASE_NOFOLLOW|CHASE_EXTRACT_FILENAME, &result, &fd) >= 0);
-        assert_se(faccessat(fd, result, F_OK, AT_SYMLINK_NOFOLLOW) >= 0);
-        assert_se(streq(result, "parent"));
+        ASSERT_OK(chaseat(tfd, "chase/parent", CHASE_AT_RESOLVE_IN_ROOT|CHASE_PARENT|CHASE_NOFOLLOW|CHASE_EXTRACT_FILENAME, &result, &fd));
+        ASSERT_OK(faccessat(fd, result, F_OK, AT_SYMLINK_NOFOLLOW));
+        ASSERT_STREQ(result, "parent");
         fd = safe_close(fd);
         result = mfree(result);
 
-        assert_se(chaseat(tfd, "chase", CHASE_PARENT|CHASE_AT_RESOLVE_IN_ROOT|CHASE_EXTRACT_FILENAME, &result, &fd) >= 0);
-        assert_se(faccessat(fd, result, F_OK, 0) >= 0);
-        assert_se(streq(result, "chase"));
+        ASSERT_OK(chaseat(tfd, "chase", CHASE_PARENT|CHASE_AT_RESOLVE_IN_ROOT|CHASE_EXTRACT_FILENAME, &result, &fd));
+        ASSERT_OK(faccessat(fd, result, F_OK, 0));
+        ASSERT_STREQ(result, "chase");
         fd = safe_close(fd);
         result = mfree(result);
 
-        assert_se(chaseat(tfd, "/", CHASE_PARENT|CHASE_AT_RESOLVE_IN_ROOT|CHASE_EXTRACT_FILENAME, &result, NULL) >= 0);
-        assert_se(streq(result, "."));
+        ASSERT_OK(chaseat(tfd, "/", CHASE_PARENT|CHASE_AT_RESOLVE_IN_ROOT|CHASE_EXTRACT_FILENAME, &result, NULL));
+        ASSERT_STREQ(result, ".");
         result = mfree(result);
 
-        assert_se(chaseat(tfd, ".", CHASE_PARENT|CHASE_AT_RESOLVE_IN_ROOT|CHASE_EXTRACT_FILENAME, &result, NULL) >= 0);
-        assert_se(streq(result, "."));
+        ASSERT_OK(chaseat(tfd, ".", CHASE_PARENT|CHASE_AT_RESOLVE_IN_ROOT|CHASE_EXTRACT_FILENAME, &result, NULL));
+        ASSERT_STREQ(result, ".");
         result = mfree(result);
 
-        assert_se(chaseat(tfd, NULL, CHASE_PARENT|CHASE_AT_RESOLVE_IN_ROOT|CHASE_EXTRACT_FILENAME, &result, NULL) >= 0);
-        assert_se(streq(result, "."));
+        ASSERT_OK(chaseat(tfd, NULL, CHASE_PARENT|CHASE_AT_RESOLVE_IN_ROOT|CHASE_EXTRACT_FILENAME, &result, NULL));
+        ASSERT_STREQ(result, ".");
         result = mfree(result);
 
         /* Test chase_and_openat() */
 
         fd = chase_and_openat(tfd, "o/p/e/n/f/i/l/e", CHASE_MKDIR_0755, O_CREAT|O_EXCL|O_CLOEXEC, NULL);
-        assert_se(fd >= 0);
-        assert_se(fd_verify_regular(fd) >= 0);
+        ASSERT_OK(fd);
+        ASSERT_OK(fd_verify_regular(fd));
         fd = safe_close(fd);
 
         fd = chase_and_openat(tfd, "o/p/e/n/d/i/r", CHASE_MKDIR_0755, O_DIRECTORY|O_CREAT|O_EXCL|O_CLOEXEC, NULL);
-        assert_se(fd >= 0);
-        assert_se(fd_verify_directory(fd) >= 0);
+        ASSERT_OK(fd);
+        ASSERT_OK(fd_verify_directory(fd));
         fd = safe_close(fd);
 
         fd = chase_and_openat(tfd, NULL, CHASE_PARENT|CHASE_EXTRACT_FILENAME, O_PATH|O_DIRECTORY|O_CLOEXEC, &result);
-        assert_se(fd >= 0);
-        assert_se(streq(result, "."));
+        ASSERT_OK(fd);
+        ASSERT_STREQ(result, ".");
         fd = safe_close(fd);
         result = mfree(result);
 
         /* Test chase_and_openatdir() */
 
-        assert_se(chase_and_opendirat(tfd, "o/p/e/n/d/i", 0, &result, &dir) >= 0);
+        ASSERT_OK(chase_and_opendirat(tfd, "o/p/e/n/d/i", 0, &result, &dir));
         FOREACH_DIRENT(de, dir, assert_not_reached())
-                assert_se(streq(de->d_name, "r"));
-        assert_se(streq(result, "o/p/e/n/d/i"));
+                ASSERT_STREQ(de->d_name, "r");
+        ASSERT_STREQ(result, "o/p/e/n/d/i");
         result = mfree(result);
 
         /* Test chase_and_statat() */
 
-        assert_se(chase_and_statat(tfd, "o/p", 0, &result, &st) >= 0);
-        assert_se(stat_verify_directory(&st) >= 0);
-        assert_se(streq(result, "o/p"));
+        ASSERT_OK(chase_and_statat(tfd, "o/p", 0, &result, &st));
+        ASSERT_OK(stat_verify_directory(&st));
+        ASSERT_STREQ(result, "o/p");
         result = mfree(result);
 
         /* Test chase_and_accessat() */
 
-        assert_se(chase_and_accessat(tfd, "o/p/e", 0, F_OK, &result) >= 0);
-        assert_se(streq(result, "o/p/e"));
+        ASSERT_OK(chase_and_accessat(tfd, "o/p/e", 0, F_OK, &result));
+        ASSERT_STREQ(result, "o/p/e");
         result = mfree(result);
 
         /* Test chase_and_fopenat_unlocked() */
 
-        assert_se(chase_and_fopenat_unlocked(tfd, "o/p/e/n/f/i/l/e", 0, "re", &result, &f) >= 0);
+        ASSERT_OK(chase_and_fopenat_unlocked(tfd, "o/p/e/n/f/i/l/e", 0, "re", &result, &f));
         assert_se(fread(&(char[1]) {}, 1, 1, f) == 0);
         assert_se(feof(f));
         f = safe_fclose(f);
-        assert_se(streq(result, "o/p/e/n/f/i/l/e"));
+        ASSERT_STREQ(result, "o/p/e/n/f/i/l/e");
         result = mfree(result);
 
         /* Test chase_and_unlinkat() */
 
-        assert_se(chase_and_unlinkat(tfd, "o/p/e/n/f/i/l/e", 0, 0, &result) >= 0);
-        assert_se(streq(result, "o/p/e/n/f/i/l/e"));
+        ASSERT_OK(chase_and_unlinkat(tfd, "o/p/e/n/f/i/l/e", 0, 0, &result));
+        ASSERT_STREQ(result, "o/p/e/n/f/i/l/e");
         result = mfree(result);
 
         /* Test chase_and_open_parent_at() */
 
-        assert_se((fd = chase_and_open_parent_at(tfd, "chase/parent", CHASE_AT_RESOLVE_IN_ROOT|CHASE_NOFOLLOW, &result)) >= 0);
-        assert_se(faccessat(fd, result, F_OK, AT_SYMLINK_NOFOLLOW) >= 0);
-        assert_se(streq(result, "parent"));
+        ASSERT_OK((fd = chase_and_open_parent_at(tfd, "chase/parent", CHASE_AT_RESOLVE_IN_ROOT|CHASE_NOFOLLOW, &result)));
+        ASSERT_OK(faccessat(fd, result, F_OK, AT_SYMLINK_NOFOLLOW));
+        ASSERT_STREQ(result, "parent");
         fd = safe_close(fd);
         result = mfree(result);
 
-        assert_se((fd = chase_and_open_parent_at(tfd, "chase", CHASE_AT_RESOLVE_IN_ROOT, &result)) >= 0);
-        assert_se(faccessat(fd, result, F_OK, 0) >= 0);
-        assert_se(streq(result, "chase"));
+        ASSERT_OK((fd = chase_and_open_parent_at(tfd, "chase", CHASE_AT_RESOLVE_IN_ROOT, &result)));
+        ASSERT_OK(faccessat(fd, result, F_OK, 0));
+        ASSERT_STREQ(result, "chase");
         fd = safe_close(fd);
         result = mfree(result);
 
-        assert_se((fd = chase_and_open_parent_at(tfd, "/", CHASE_AT_RESOLVE_IN_ROOT, &result)) >= 0);
-        assert_se(streq(result, "."));
+        ASSERT_OK((fd = chase_and_open_parent_at(tfd, "/", CHASE_AT_RESOLVE_IN_ROOT, &result)));
+        ASSERT_STREQ(result, ".");
         fd = safe_close(fd);
         result = mfree(result);
 
-        assert_se((fd = chase_and_open_parent_at(tfd, ".", CHASE_AT_RESOLVE_IN_ROOT, &result)) >= 0);
-        assert_se(streq(result, "."));
+        ASSERT_OK((fd = chase_and_open_parent_at(tfd, ".", CHASE_AT_RESOLVE_IN_ROOT, &result)));
+        ASSERT_STREQ(result, ".");
         fd = safe_close(fd);
         result = mfree(result);
 }
@@ -687,47 +687,47 @@ TEST(chaseat) {
 TEST(chaseat_prefix_root) {
         _cleanup_free_ char *cwd = NULL, *ret = NULL, *expected = NULL;
 
-        assert_se(safe_getcwd(&cwd) >= 0);
+        ASSERT_OK(safe_getcwd(&cwd));
 
-        assert_se(chaseat_prefix_root("/hoge", NULL, &ret) >= 0);
-        assert_se(streq(ret, "/hoge"));
+        ASSERT_OK(chaseat_prefix_root("/hoge", NULL, &ret));
+        ASSERT_STREQ(ret, "/hoge");
 
         ret = mfree(ret);
 
-        assert_se(chaseat_prefix_root("/hoge", "a/b/c", &ret) >= 0);
-        assert_se(streq(ret, "/hoge"));
+        ASSERT_OK(chaseat_prefix_root("/hoge", "a/b/c", &ret));
+        ASSERT_STREQ(ret, "/hoge");
 
         ret = mfree(ret);
 
-        assert_se(chaseat_prefix_root("hoge", "/a/b//./c///", &ret) >= 0);
-        assert_se(streq(ret, "/a/b/c/hoge"));
+        ASSERT_OK(chaseat_prefix_root("hoge", "/a/b//./c///", &ret));
+        ASSERT_STREQ(ret, "/a/b/c/hoge");
 
         ret = mfree(ret);
 
-        assert_se(chaseat_prefix_root("hoge", "a/b//./c///", &ret) >= 0);
+        ASSERT_OK(chaseat_prefix_root("hoge", "a/b//./c///", &ret));
         assert_se(expected = path_join(cwd, "a/b/c/hoge"));
-        assert_se(streq(ret, expected));
+        ASSERT_STREQ(ret, expected);
 
         ret = mfree(ret);
         expected = mfree(expected);
 
-        assert_se(chaseat_prefix_root("./hoge/aaa/../././b", "/a/b//./c///", &ret) >= 0);
-        assert_se(streq(ret, "/a/b/c/hoge/aaa/../././b"));
+        ASSERT_OK(chaseat_prefix_root("./hoge/aaa/../././b", "/a/b//./c///", &ret));
+        ASSERT_STREQ(ret, "/a/b/c/hoge/aaa/../././b");
 
         ret = mfree(ret);
 
         assert_se(chaseat_prefix_root("./hoge/aaa/../././b", "a/b//./c///", &ret) >= 0);
         assert_se(expected = path_join(cwd, "a/b/c/hoge/aaa/../././b"));
-        assert_se(streq(ret, expected));
+        ASSERT_STREQ(ret, expected);
 }
 
 TEST(trailing_dot_dot) {
         _cleanup_free_ char *path = NULL, *fdpath = NULL;
         _cleanup_close_ int fd = -EBADF;
 
-        assert_se(chase("/usr/..", NULL, CHASE_PARENT, &path, &fd) >= 0);
+        ASSERT_OK(chase("/usr/..", NULL, CHASE_PARENT, &path, &fd));
         assert_se(path_equal(path, "/"));
-        assert_se(fd_get_path(fd, &fdpath) >= 0);
+        ASSERT_OK(fd_get_path(fd, &fdpath));
         assert_se(path_equal(fdpath, "/"));
 
         path = mfree(path);
@@ -735,16 +735,16 @@ TEST(trailing_dot_dot) {
         fd = safe_close(fd);
 
         _cleanup_(rm_rf_physical_and_freep) char *t = NULL;
-        assert_se(mkdtemp_malloc(NULL, &t) >= 0);
+        ASSERT_OK(mkdtemp_malloc(NULL, &t));
         _cleanup_free_ char *sub = ASSERT_PTR(path_join(t, "a/b/c/d"));
-        assert_se(mkdir_p(sub, 0700) >= 0);
+        ASSERT_OK(mkdir_p(sub, 0700));
         _cleanup_free_ char *suffixed = ASSERT_PTR(path_join(sub, ".."));
-        assert_se(chase(suffixed, NULL, CHASE_PARENT, &path, &fd) >= 0);
+        ASSERT_OK(chase(suffixed, NULL, CHASE_PARENT, &path, &fd));
         _cleanup_free_ char *expected1 = ASSERT_PTR(path_join(t, "a/b/c"));
         _cleanup_free_ char *expected2 = ASSERT_PTR(path_join(t, "a/b"));
 
         assert_se(path_equal(path, expected1));
-        assert_se(fd_get_path(fd, &fdpath) >= 0);
+        ASSERT_OK(fd_get_path(fd, &fdpath));
         assert_se(path_equal(fdpath, expected2));
 }
 
index d0d7419eaaf546627a6299d65babb55e38203cfa..280abb53774abf3de2484a2173255450deaa7a99 100644 (file)
@@ -16,7 +16,7 @@ static void test_config_parse_path_one(const char *rvalue, const char *expected)
         _cleanup_free_ char *path = NULL;
 
         assert_se(config_parse_path("unit", "filename", 1, "section", 1, "lvalue", 0, rvalue, &path, NULL) >= 0);
-        assert_se(streq_ptr(expected, path));
+        ASSERT_STREQ(expected, path);
 }
 
 static void test_config_parse_log_level_one(const char *rvalue, int expected) {
@@ -352,37 +352,37 @@ static void test_config_parse_one(unsigned i, const char *s) {
         switch (i) {
         case 0 ... 4:
                 assert_se(r == 1);
-                assert_se(streq(setting1, "1"));
+                ASSERT_STREQ(setting1, "1");
                 break;
 
         case 5 ... 10:
                 assert_se(r == 1);
-                assert_se(streq(setting1, "1 2 3"));
+                ASSERT_STREQ(setting1, "1 2 3");
                 break;
 
         case 11:
                 assert_se(r == 1);
-                assert_se(streq(setting1, "1\\\\ \\\\2"));
+                ASSERT_STREQ(setting1, "1\\\\ \\\\2");
                 break;
 
         case 12:
                 assert_se(r == 1);
-                assert_se(streq(setting1, x1000("ABCD")));
+                ASSERT_STREQ(setting1, x1000("ABCD"));
                 break;
 
         case 13 ... 14:
                 assert_se(r == 1);
-                assert_se(streq(setting1, x1000("ABCD") " foobar"));
+                ASSERT_STREQ(setting1, x1000("ABCD") " foobar");
                 break;
 
         case 15 ... 16:
                 assert_se(r == -ENOBUFS);
-                assert_se(setting1 == NULL);
+                ASSERT_NULL(setting1);
                 break;
 
         case 17:
                 assert_se(r == 1);
-                assert_se(streq(setting1, "2"));
+                ASSERT_STREQ(setting1, "2");
                 break;
         }
 }
@@ -443,12 +443,12 @@ TEST(config_parse_standard_file_with_dropins_full) {
                         /* ret_stats_by_path= */ NULL,
                         /* ret_dropin_files= */ &dropins);
         assert_se(r >= 0);
-        assert_se(streq_ptr(A, "aaa"));
-        assert_se(streq_ptr(B, "bbb"));
-        assert_se(streq_ptr(C, "c1"));
-        assert_se(streq_ptr(D, "ddd"));
-        assert_se(streq_ptr(E, "eee"));
-        assert_se(streq_ptr(F, NULL));
+        ASSERT_STREQ(A, "aaa");
+        ASSERT_STREQ(B, "bbb");
+        ASSERT_STREQ(C, "c1");
+        ASSERT_STREQ(D, "ddd");
+        ASSERT_STREQ(E, "eee");
+        ASSERT_STREQ(F, NULL);
 
         A = mfree(A);
         B = mfree(B);
@@ -482,12 +482,12 @@ TEST(config_parse_standard_file_with_dropins_full) {
                         /* ret_stats_by_path= */ NULL,
                         /* ret_dropin_files= */ NULL);
         assert_se(r >= 0);
-        assert_se(streq_ptr(A, "aaa"));
-        assert_se(streq_ptr(B, "bbb"));
-        assert_se(streq_ptr(C, "c1"));
-        assert_se(streq_ptr(D, "ddd"));
-        assert_se(streq_ptr(E, "eee"));
-        assert_se(streq_ptr(F, NULL));
+        ASSERT_STREQ(A, "aaa");
+        ASSERT_STREQ(B, "bbb");
+        ASSERT_STREQ(C, "c1");
+        ASSERT_STREQ(D, "ddd");
+        ASSERT_STREQ(E, "eee");
+        ASSERT_STREQ(F, NULL);
 }
 
 DEFINE_TEST_MAIN(LOG_INFO);
index 111c001de6b45405b40af6c130103ccfeca3aa63..7b16cf2258b025ede3eb9640a4a4c07c0131d3be 100644 (file)
@@ -45,7 +45,7 @@ TEST(copy_file) {
         assert_se(copy_file(fn, fn_copy, 0, 0644, COPY_REFLINK) == 0);
 
         assert_se(read_full_file(fn_copy, &buf, &sz) == 0);
-        assert_se(streq(buf, "foo bar bar bar foo\n"));
+        ASSERT_STREQ(buf, "foo bar bar bar foo\n");
         assert_se(sz == 20);
 }
 
@@ -125,7 +125,7 @@ TEST(copy_file_fd) {
         assert_se(lseek(out_fd, SEEK_SET, 0) == 0);
 
         assert_se(read(out_fd, buf, sizeof buf) == (ssize_t) strlen(text));
-        assert_se(streq(buf, text));
+        ASSERT_STREQ(buf, text);
 }
 
 TEST(copy_tree) {
@@ -202,7 +202,7 @@ TEST(copy_tree) {
 
                 assert_se(access(f, F_OK) == 0);
                 assert_se(read_full_file(f, &buf, &sz) == 0);
-                assert_se(streq(buf, "file\n"));
+                ASSERT_STREQ(buf, "file\n");
 
                 k = lgetxattr_malloc(f, "user.testxattr", &c);
                 assert_se(xattr_worked < 0 || ((k >= 0) == !!xattr_worked));
@@ -211,7 +211,7 @@ TEST(copy_tree) {
                         _cleanup_free_ char *d = NULL;
 
                         assert_se(base64mem(*p, strlen(*p), &d) >= 0);
-                        assert_se(streq(d, c));
+                        ASSERT_STREQ(d, c);
                 }
         }
 
@@ -266,13 +266,13 @@ TEST(copy_tree_at_symlink) {
 
         assert_se(copy_tree_at(tfd, "from", tfd, "to_1", UID_INVALID, GID_INVALID, 0, NULL, NULL) >= 0);
         assert_se(readlinkat_malloc(tfd, "to_1", &p) >= 0);
-        assert_se(streq(p, expect));
+        ASSERT_STREQ(p, expect);
         p = mfree(p);
 
         assert_se(q = path_join(t, "from"));
         assert_se(copy_tree_at(AT_FDCWD, q, tfd, "to_2", UID_INVALID, GID_INVALID, 0, NULL, NULL) >= 0);
         assert_se(readlinkat_malloc(tfd, "to_2", &p) >= 0);
-        assert_se(streq(p, expect));
+        ASSERT_STREQ(p, expect);
         p = mfree(p);
         q = mfree(q);
 
@@ -280,17 +280,17 @@ TEST(copy_tree_at_symlink) {
         assert_se(fd >= 0);
         assert_se(copy_tree_at(fd, NULL, tfd, "to_3", UID_INVALID, GID_INVALID, 0, NULL, NULL) >= 0);
         assert_se(readlinkat_malloc(tfd, "to_3", &p) >= 0);
-        assert_se(streq(p, expect));
+        ASSERT_STREQ(p, expect);
         p = mfree(p);
 
         assert_se(copy_tree_at(fd, "", tfd, "to_4", UID_INVALID, GID_INVALID, 0, NULL, NULL) >= 0);
         assert_se(readlinkat_malloc(tfd, "to_4", &p) >= 0);
-        assert_se(streq(p, expect));
+        ASSERT_STREQ(p, expect);
         p = mfree(p);
         fd = safe_close(fd);
 }
 
-TEST(copy_bytes) {
+TEST_RET(copy_bytes) {
         _cleanup_close_pair_ int pipefd[2] = EBADF_PAIR;
         _cleanup_close_ int infd = -EBADF;
         int r, r2;
@@ -299,7 +299,8 @@ TEST(copy_bytes) {
         infd = open("/usr/lib/os-release", O_RDONLY|O_CLOEXEC);
         if (infd < 0)
                 infd = open("/etc/os-release", O_RDONLY|O_CLOEXEC);
-        assert_se(infd >= 0);
+        if (infd < 0)
+                return log_tests_skipped_errno(errno, "Could not open /usr/lib/os-release or /etc/os-release: %m");
 
         assert_se(pipe2(pipefd, O_CLOEXEC) == 0);
 
@@ -324,6 +325,8 @@ TEST(copy_bytes) {
 
         r = copy_bytes(pipefd[1], infd, 1, 0);
         assert_se(r == -EBADF);
+
+        return 0;
 }
 
 static void test_copy_bytes_regular_file_one(const char *src, bool try_reflink, uint64_t max_bytes) {
@@ -418,7 +421,7 @@ TEST(copy_proc) {
 
         assert_se(read_one_line_file("/proc/version", &a) >= 0);
         assert_se(read_one_line_file(f, &b) >= 0);
-        assert_se(streq(a, b));
+        ASSERT_STREQ(a, b);
         assert_se(!isempty(a));
 }
 
@@ -441,7 +444,7 @@ TEST_RET(copy_holes) {
                 return log_tests_skipped("Filesystem doesn't support hole punching");
         assert_se(r >= 0);
 
-        assert_se(fstat(fd, &stat) >= 0);
+        ASSERT_OK_ERRNO(fstat(fd, &stat));
         blksz = stat.st_blksize;
         buf = alloca_safe(blksz);
         memset(buf, 1, blksz);
@@ -466,7 +469,7 @@ TEST_RET(copy_holes) {
         assert_se(lseek(fd_copy, 2 * blksz, SEEK_DATA) < 0 && errno == ENXIO);
 
         /* Test that the copied file has the correct size. */
-        assert_se(fstat(fd_copy, &stat) >= 0);
+        ASSERT_OK_ERRNO(fstat(fd_copy, &stat));
         assert_se(stat.st_size == 3 * blksz);
 
         close(fd);
@@ -487,7 +490,7 @@ TEST_RET(copy_holes_with_gaps) {
         assert_se((fd = openat(tfd, "src", O_CREAT | O_RDWR, 0600)) >= 0);
         assert_se((fd_copy = openat(tfd, "dst", O_CREAT | O_WRONLY, 0600)) >= 0);
 
-        assert_se(fstat(fd, &st) >= 0);
+        ASSERT_OK_ERRNO(fstat(fd, &st));
         blksz = st.st_blksize;
         buf = alloca_safe(blksz);
         memset(buf, 1, blksz);
@@ -516,7 +519,7 @@ TEST_RET(copy_holes_with_gaps) {
 
         /* Copy to the start of the second hole */
         assert_se(copy_bytes(fd, fd_copy, 3 * blksz, COPY_HOLES) >= 0);
-        assert_se(fstat(fd_copy, &st) >= 0);
+        ASSERT_OK_ERRNO(fstat(fd_copy, &st));
         assert_se(st.st_size == 3 * blksz);
 
         /* Copy to the middle of the second hole */
@@ -524,7 +527,7 @@ TEST_RET(copy_holes_with_gaps) {
         assert_se(lseek(fd_copy, 0, SEEK_SET) >= 0);
         assert_se(ftruncate(fd_copy, 0) >= 0);
         assert_se(copy_bytes(fd, fd_copy, 4 * blksz, COPY_HOLES) >= 0);
-        assert_se(fstat(fd_copy, &st) >= 0);
+        ASSERT_OK_ERRNO(fstat(fd_copy, &st));
         assert_se(st.st_size == 4 * blksz);
 
         /* Copy to the end of the second hole */
@@ -532,7 +535,7 @@ TEST_RET(copy_holes_with_gaps) {
         assert_se(lseek(fd_copy, 0, SEEK_SET) >= 0);
         assert_se(ftruncate(fd_copy, 0) >= 0);
         assert_se(copy_bytes(fd, fd_copy, 5 * blksz, COPY_HOLES) >= 0);
-        assert_se(fstat(fd_copy, &st) >= 0);
+        ASSERT_OK_ERRNO(fstat(fd_copy, &st));
         assert_se(st.st_size == 5 * blksz);
 
         /* Copy everything */
@@ -540,7 +543,7 @@ TEST_RET(copy_holes_with_gaps) {
         assert_se(lseek(fd_copy, 0, SEEK_SET) >= 0);
         assert_se(ftruncate(fd_copy, 0) >= 0);
         assert_se(copy_bytes(fd, fd_copy, UINT64_MAX, COPY_HOLES) >= 0);
-        assert_se(fstat(fd_copy, &st) >= 0);
+        ASSERT_OK_ERRNO(fstat(fd_copy, &st));
         assert_se(st.st_size == 6 * blksz);
 
         return 0;
index dc108cc2efef4ea55678c13295c7d01fca3292ad..83c8674c6f08bad3123f802609198a7daddde67d 100644 (file)
@@ -26,26 +26,26 @@ static void test_unit_escape_setting_one(
         assert_se(t = unit_escape_setting(s, 0, &a));
         assert_se(a_esc = cescape(t));
         log_debug("%s: [%s] → [%s]", __func__, s_esc, a_esc);
-        assert_se(a == NULL);
+        ASSERT_NULL(a);
         assert_se(t == s);
 
         assert_se(t = unit_escape_setting(s, UNIT_ESCAPE_EXEC_SYNTAX_ENV, &b));
         assert_se(b_esc = cescape(t));
         log_debug("%s: [%s] → [%s]", __func__, s_esc, b_esc);
         assert_se(b == NULL || streq(b, t));
-        assert_se(streq(t, expected_exec_env));
+        ASSERT_STREQ(t, expected_exec_env);
 
         assert_se(t = unit_escape_setting(s, UNIT_ESCAPE_EXEC_SYNTAX, &c));
         assert_se(c_esc = cescape(t));
         log_debug("%s: [%s] → [%s]", __func__, s_esc, c_esc);
         assert_se(c == NULL || streq(c, t));
-        assert_se(streq(t, expected_exec));
+        ASSERT_STREQ(t, expected_exec);
 
         assert_se(t = unit_escape_setting(s, UNIT_ESCAPE_C, &d));
         assert_se(d_esc = cescape(t));
         log_debug("%s: [%s] → [%s]", __func__, s_esc, d_esc);
         assert_se(d == NULL || streq(d, t));
-        assert_se(streq(t, expected_c));
+        ASSERT_STREQ(t, expected_c);
 }
 
 TEST(unit_escape_setting) {
@@ -81,22 +81,22 @@ static void test_unit_concat_strv_one(
         assert_se(a = unit_concat_strv(s, 0));
         assert_se(a_esc = cescape(a));
         log_debug("%s: [%s] → [%s]", __func__, s_esc, a_esc);
-        assert_se(streq(a, expected_none));
+        ASSERT_STREQ(a, expected_none);
 
         assert_se(b = unit_concat_strv(s, UNIT_ESCAPE_EXEC_SYNTAX_ENV));
         assert_se(b_esc = cescape(b));
         log_debug("%s: [%s] → [%s]", __func__, s_esc, b_esc);
-        assert_se(streq(b, expected_exec_env));
+        ASSERT_STREQ(b, expected_exec_env);
 
         assert_se(c = unit_concat_strv(s, UNIT_ESCAPE_EXEC_SYNTAX));
         assert_se(c_esc = cescape(c));
         log_debug("%s: [%s] → [%s]", __func__, s_esc, c_esc);
-        assert_se(streq(c, expected_exec));
+        ASSERT_STREQ(c, expected_exec);
 
         assert_se(d = unit_concat_strv(s, UNIT_ESCAPE_C));
         assert_se(d_esc = cescape(d));
         log_debug("%s: [%s] → [%s]", __func__, s_esc, d_esc);
-        assert_se(streq(d, expected_c));
+        ASSERT_STREQ(d, expected_c);
 }
 
 TEST(unit_concat_strv) {
index 0c2304e2c09610d517e6a7d9b9bcb787670db426..ccb52c96d4d64a2923eef0f6e21ac9bd4b0bbd89 100644 (file)
@@ -23,11 +23,11 @@ TEST(parse_cpu_set) {
         str = mfree(str);
         assert_se(str = cpu_set_to_range_string(&c));
         log_info("cpu_set_to_range_string: %s", str);
-        assert_se(streq(str, "0"));
+        ASSERT_STREQ(str, "0");
         str = mfree(str);
         assert_se(str = cpu_set_to_mask_string(&c));
         log_info("cpu_set_to_mask_string: %s", str);
-        assert_se(streq(str, "1"));
+        ASSERT_STREQ(str, "1");
         str = mfree(str);
         cpu_set_reset(&c);
 
@@ -45,11 +45,11 @@ TEST(parse_cpu_set) {
         str = mfree(str);
         assert_se(str = cpu_set_to_range_string(&c));
         log_info("cpu_set_to_range_string: %s", str);
-        assert_se(streq(str, "1-2 4"));
+        ASSERT_STREQ(str, "1-2 4");
         str = mfree(str);
         assert_se(str = cpu_set_to_mask_string(&c));
         log_info("cpu_set_to_mask_string: %s", str);
-        assert_se(streq(str, "16"));
+        ASSERT_STREQ(str, "16");
         str = mfree(str);
         cpu_set_reset(&c);
 
@@ -67,11 +67,11 @@ TEST(parse_cpu_set) {
         str = mfree(str);
         assert_se(str = cpu_set_to_range_string(&c));
         log_info("cpu_set_to_range_string: %s", str);
-        assert_se(streq(str, "0-3 8-11"));
+        ASSERT_STREQ(str, "0-3 8-11");
         str = mfree(str);
         assert_se(str = cpu_set_to_mask_string(&c));
         log_info("cpu_set_to_mask_string: %s", str);
-        assert_se(streq(str, "f0f"));
+        ASSERT_STREQ(str, "f0f");
         str = mfree(str);
         cpu_set_reset(&c);
 
@@ -86,11 +86,11 @@ TEST(parse_cpu_set) {
         str = mfree(str);
         assert_se(str = cpu_set_to_range_string(&c));
         log_info("cpu_set_to_range_string: %s", str);
-        assert_se(streq(str, "8-11"));
+        ASSERT_STREQ(str, "8-11");
         str = mfree(str);
         assert_se(str = cpu_set_to_mask_string(&c));
         log_info("cpu_set_to_mask_string: %s", str);
-        assert_se(streq(str, "f00"));
+        ASSERT_STREQ(str, "f00");
         str = mfree(str);
         cpu_set_reset(&c);
 
@@ -120,11 +120,11 @@ TEST(parse_cpu_set) {
         str = mfree(str);
         assert_se(str = cpu_set_to_range_string(&c));
         log_info("cpu_set_to_range_string: %s", str);
-        assert_se(streq(str, "0-7 63"));
+        ASSERT_STREQ(str, "0-7 63");
         str = mfree(str);
         assert_se(str = cpu_set_to_mask_string(&c));
         log_info("cpu_set_to_mask_string: %s", str);
-        assert_se(streq(str, "80000000,000000ff"));
+        ASSERT_STREQ(str, "80000000,000000ff");
         str = mfree(str);
         cpu_set_reset(&c);
 
@@ -149,7 +149,7 @@ TEST(parse_cpu_set) {
                 assert_se(CPU_ISSET_S(cpu, c.allocated, c.set));
         assert_se(str = cpu_set_to_mask_string(&c));
         log_info("cpu_set_to_mask_string: %s", str);
-        assert_se(streq(str, "f0f0,00000000"));
+        ASSERT_STREQ(str, "f0f0,00000000");
         str = mfree(str);
         cpu_set_reset(&c);
         assert_se(parse_cpu_set_full("64-71", &c, true, NULL, "fake", 1, "CPUAffinity") >= 0);
@@ -159,7 +159,7 @@ TEST(parse_cpu_set) {
                 assert_se(CPU_ISSET_S(cpu, c.allocated, c.set));
         assert_se(str = cpu_set_to_mask_string(&c));
         log_info("cpu_set_to_mask_string: %s", str);
-        assert_se(streq(str, "ff,00000000,00000000"));
+        ASSERT_STREQ(str, "ff,00000000,00000000");
         str = mfree(str);
         cpu_set_reset(&c);
 
@@ -176,11 +176,11 @@ TEST(parse_cpu_set) {
         str = mfree(str);
         assert_se(str = cpu_set_to_range_string(&c));
         log_info("cpu_set_to_range_string: %s", str);
-        assert_se(streq(str, "0-3 8-11"));
+        ASSERT_STREQ(str, "0-3 8-11");
         str = mfree(str);
         assert_se(str = cpu_set_to_mask_string(&c));
         log_info("cpu_set_to_mask_string: %s", str);
-        assert_se(streq(str, "f0f"));
+        ASSERT_STREQ(str, "f0f");
         str = mfree(str);
         cpu_set_reset(&c);
 
@@ -190,7 +190,7 @@ TEST(parse_cpu_set) {
         assert_se(CPU_COUNT_S(c.allocated, c.set) == 0);
         assert_se(str = cpu_set_to_mask_string(&c));
         log_info("cpu_set_to_mask_string: %s", str);
-        assert_se(streq(str, "0"));
+        ASSERT_STREQ(str, "0");
         str = mfree(str);
         cpu_set_reset(&c);
 
@@ -205,11 +205,11 @@ TEST(parse_cpu_set) {
         str = mfree(str);
         assert_se(str = cpu_set_to_range_string(&c));
         log_info("cpu_set_to_range_string: %s", str);
-        assert_se(streq(str, "0-11"));
+        ASSERT_STREQ(str, "0-11");
         str = mfree(str);
         assert_se(str = cpu_set_to_mask_string(&c));
         log_info("cpu_set_to_mask_string: %s", str);
-        assert_se(streq(str, "fff"));
+        ASSERT_STREQ(str, "fff");
         str = mfree(str);
         cpu_set_reset(&c);
 
@@ -226,11 +226,11 @@ TEST(parse_cpu_set) {
         str = mfree(str);
         assert_se(str = cpu_set_to_range_string(&c));
         log_info("cpu_set_to_range_string: %s", str);
-        assert_se(streq(str, "0 2 4-11"));
+        ASSERT_STREQ(str, "0 2 4-11");
         str = mfree(str);
         assert_se(str = cpu_set_to_mask_string(&c));
         log_info("cpu_set_to_mask_string: %s", str);
-        assert_se(streq(str, "ff5"));
+        ASSERT_STREQ(str, "ff5");
         str = mfree(str);
         cpu_set_reset(&c);
 
@@ -250,7 +250,7 @@ TEST(parse_cpu_set) {
         assert_se(c.allocated == 0);
         assert_se(str = cpu_set_to_mask_string(&c));
         log_info("cpu_set_to_mask_string: %s", str);
-        assert_se(streq(str, "0"));
+        ASSERT_STREQ(str, "0");
         str = mfree(str);
 
         /* Runaway quoted string */
@@ -266,7 +266,7 @@ TEST(parse_cpu_set) {
         str = mfree(str);
         assert_se(str = cpu_set_to_range_string(&c));
         log_info("cpu_set_to_range_string: %s", str);
-        assert_se(streq(str, "8000-8191"));
+        ASSERT_STREQ(str, "8000-8191");
         str = mfree(str);
         assert_se(str = cpu_set_to_mask_string(&c));
         log_info("cpu_set_to_mask_string: %s", str);
index b4beafc31d6279ff2ad2e4059cb4083fe451b844..84619c91de92e9ed60656e661456212d1f0356c1 100644 (file)
@@ -23,37 +23,37 @@ TEST(read_credential_strings) {
                 assert_se(saved = strdup(e));
 
         assert_se(read_credential_strings_many("foo", &x, "bar", &y) == 0);
-        assert_se(x == NULL);
-        assert_se(y == NULL);
+        ASSERT_NULL(x);
+        ASSERT_NULL(y);
 
         assert_se(mkdtemp_malloc(NULL, &tmp) >= 0);
 
         assert_se(setenv("CREDENTIALS_DIRECTORY", tmp, /* override= */ true) >= 0);
 
         assert_se(read_credential_strings_many("foo", &x, "bar", &y) == 0);
-        assert_se(x == NULL);
-        assert_se(y == NULL);
+        ASSERT_NULL(x);
+        ASSERT_NULL(y);
 
         assert_se(p = path_join(tmp, "bar"));
         assert_se(write_string_file(p, "piff", WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_AVOID_NEWLINE) >= 0);
 
         assert_se(read_credential_strings_many("foo", &x, "bar", &y) == 0);
-        assert_se(x == NULL);
-        assert_se(streq(y, "piff"));
+        ASSERT_NULL(x);
+        ASSERT_STREQ(y, "piff");
 
         assert_se(write_string_file(p, "paff", WRITE_STRING_FILE_TRUNCATE|WRITE_STRING_FILE_AVOID_NEWLINE) >= 0);
 
         assert_se(read_credential_strings_many("foo", &x, "bar", &y) == 0);
-        assert_se(x == NULL);
-        assert_se(streq(y, "paff"));
+        ASSERT_NULL(x);
+        ASSERT_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, "paff"));
+        ASSERT_STREQ(x, "knurz");
+        ASSERT_STREQ(y, "paff");
 
         p = mfree(p);
         assert_se(p = path_join(tmp, "bazz"));
@@ -64,8 +64,8 @@ TEST(read_credential_strings) {
         y = mfree(y);
 
         assert_se(read_credential_strings_many("bazz", &x, "bar", &y) == -EBADMSG);
-        assert_se(streq(x, "knurz"));
-        assert_se(streq(y, "paff"));
+        ASSERT_STREQ(x, "knurz");
+        ASSERT_STREQ(y, "paff");
 
         if (saved)
                 assert_se(setenv("CREDENTIALS_DIRECTORY", saved, /* override= */ 1) >= 0);
index 6202a5d6d4d4bbbcab052c6078f185cf3a97216e..9f9be4d37277c688b551860b3e7b23826b9ad9a7 100644 (file)
@@ -14,25 +14,25 @@ TEST(string_hashsum) {
                                  OPENSSL_OR_GCRYPT("SHA224", GCRY_MD_SHA224),
                                  &out1) == 0);
         /* echo -n 'asdf' | sha224sum - */
-        assert_se(streq(out1, "7872a74bcbf298a1e77d507cd95d4f8d96131cbbd4cdfc571e776c8a"));
+        ASSERT_STREQ(out1, "7872a74bcbf298a1e77d507cd95d4f8d96131cbbd4cdfc571e776c8a");
 
         assert_se(string_hashsum("asdf", 4,
                                  OPENSSL_OR_GCRYPT("SHA256", GCRY_MD_SHA256),
                                  &out2) == 0);
         /* echo -n 'asdf' | sha256sum - */
-        assert_se(streq(out2, "f0e4c2f76c58916ec258f246851bea091d14d4247a2fc3e18694461b1816e13b"));
+        ASSERT_STREQ(out2, "f0e4c2f76c58916ec258f246851bea091d14d4247a2fc3e18694461b1816e13b");
 
         assert_se(string_hashsum("", 0,
                                  OPENSSL_OR_GCRYPT("SHA224", GCRY_MD_SHA224),
                                  &out3) == 0);
         /* echo -n '' | sha224sum - */
-        assert_se(streq(out3, "d14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f"));
+        ASSERT_STREQ(out3, "d14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f");
 
         assert_se(string_hashsum("", 0,
                                  OPENSSL_OR_GCRYPT("SHA256", GCRY_MD_SHA256),
                                  &out4) == 0);
         /* echo -n '' | sha256sum - */
-        assert_se(streq(out4, "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"));
+        ASSERT_STREQ(out4, "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855");
 }
 
 DEFINE_TEST_MAIN(LOG_INFO);
index 6d1cfe62899b2e35718ecaf0ad29ee715336c5a9..4abd7a65190944e5d6e972c6557192ac8bfb5996 100644 (file)
@@ -22,7 +22,7 @@ static void test_acquire_data_fd_one(unsigned flags) {
 
         zero(rbuffer);
         assert_se(read(fd, rbuffer, sizeof(rbuffer)) == 3);
-        assert_se(streq(rbuffer, "foo"));
+        ASSERT_STREQ(rbuffer, "foo");
 
         fd = safe_close(fd);
 
@@ -31,7 +31,7 @@ static void test_acquire_data_fd_one(unsigned flags) {
 
         zero(rbuffer);
         assert_se(read(fd, rbuffer, sizeof(rbuffer)) == 0);
-        assert_se(streq(rbuffer, ""));
+        ASSERT_STREQ(rbuffer, "");
 
         fd = safe_close(fd);
 
index 162ac342f50bb787debbd64ffd8c05f95aed868a..c7d239e22fde8d8249201451edd1984bfe08389b 100644 (file)
@@ -1,5 +1,7 @@
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 
+#include <unistd.h>
+
 #include "alloc-util.h"
 #include "string-util.h"
 #include "tests.h"
@@ -83,9 +85,11 @@ int main(int argc, char *argv[]) {
         test_one("today");
         test_one("tomorrow");
         test_one_noutc("16:20 UTC");
-        test_one_noutc("16:20 Asia/Seoul");
-        test_one_noutc("tomorrow Asia/Seoul");
-        test_one_noutc("2012-12-30 18:42 Asia/Seoul");
+        if (access("/usr/share/zoneinfo/Asia/Seoul", F_OK) >= 0) {
+                test_one_noutc("16:20 Asia/Seoul");
+                test_one_noutc("tomorrow Asia/Seoul");
+                test_one_noutc("2012-12-30 18:42 Asia/Seoul");
+        }
         test_one_noutc("now");
         test_one_noutc("+2d");
         test_one_noutc("+2y 4d");
@@ -96,9 +100,11 @@ int main(int argc, char *argv[]) {
         test_should_fail("1969-12-31 UTC");
         test_should_fail("-1000y");
         test_should_fail("today UTC UTC");
-        test_should_fail("now Asia/Seoul");
-        test_should_fail("+2d Asia/Seoul");
-        test_should_fail("@1395716396 Asia/Seoul");
+        if (access("/usr/share/zoneinfo/Asia/Seoul", F_OK) >= 0) {
+                test_should_fail("now Asia/Seoul");
+                test_should_fail("+2d Asia/Seoul");
+                test_should_fail("@1395716396 Asia/Seoul");
+        }
 #if SIZEOF_TIME_T == 8
         test_should_pass("9999-12-30 23:59:59 UTC");
         test_should_fail("9999-12-31 00:00:00 UTC");
index 2068e351b9f815675a7aa38dc701b8aeb0c7b957..ebef794001b82e01062175a390b0c10e18971cc5 100644 (file)
@@ -109,7 +109,7 @@ TEST(device_path_make_canonical) {
 static void test_devnum_format_str_one(dev_t devnum, const char *s) {
         dev_t x;
 
-        assert_se(streq(FORMAT_DEVNUM(devnum), s));
+        ASSERT_STREQ(FORMAT_DEVNUM(devnum), s);
         assert_se(parse_devnum(s, &x) >= 0);
         assert_se(x == devnum);
 }
index be0496904f8a999c8fae133be5ae4c9505efd957..8192a53e6fe0779f1a9e82c4aadd3b91b564e1fc 100644 (file)
@@ -68,7 +68,7 @@ TEST (test_dirent_is_file) {
         }
 
         dir = opendir(t);
-        if (dir == NULL) {
+        if (!dir) {
                 log_error_errno(errno, "Failed to open directory '%s': %m", t);
                 exit(EXIT_FAILURE);
         }
@@ -144,7 +144,7 @@ TEST (test_dirent_is_file_with_suffix) {
         }
 
         dir = opendir(t);
-        if (dir == NULL) {
+        if (!dir) {
                 log_error_errno(errno, "Failed to open directory '%s': %m", t);
                 exit(EXIT_FAILURE);
         }
index 3232506d5a12f6e9e7ff8adee4fae62e8fdb13c9..d38dff182aff3aa264e66e1d1f305066628d339d 100644 (file)
@@ -7,11 +7,13 @@
 #include "compress.h"
 #include "cryptsetup-util.h"
 #include "elf-util.h"
+#include "gcrypt-util.h"
 #include "idn-util.h"
 #include "libarchive-util.h"
 #include "libfido2-util.h"
 #include "macro.h"
 #include "main-func.h"
+#include "module-util.h"
 #include "password-quality-util-passwdqc.h"
 #include "password-quality-util-pwquality.h"
 #include "pcre2-util.h"
@@ -88,6 +90,14 @@ static int run(int argc, char **argv) {
         assert_se(dlopen_lzma() >= 0);
 #endif
 
+#if HAVE_GCRYPT
+        assert_se(initialize_libgcrypt(/* secmem= */ false) >= 0);
+#endif
+
+#if HAVE_KMOD
+        assert_se(dlopen_libkmod() >= 0);
+#endif
+
         return 0;
 }
 
index 6c107e2f65a3740a0657009097ae431b66b6d8ac..d775601dae84afb7c93529d0d148f5ebe37dcbc4 100644 (file)
@@ -16,20 +16,20 @@ static void test_dns_label_unescape_one(const char *what, const char *expect, si
         r = dns_label_unescape(&w, buffer, buffer_sz, 0);
         assert_se(r == ret);
         if (r >= 0)
-                assert_se(streq(buffer, expect));
+                ASSERT_STREQ(buffer, expect);
 
         w = what;
         r = dns_label_unescape(&w, buffer, buffer_sz, DNS_LABEL_LDH);
         assert_se(r == ret_ldh);
         if (r >= 0)
-                assert_se(streq(buffer, expect));
+                ASSERT_STREQ(buffer, expect);
 
         w = what;
         r = dns_label_unescape(&w, buffer, buffer_sz, DNS_LABEL_NO_ESCAPES);
         const int ret_noe = strchr(what, '\\') ? -EINVAL : ret;
         assert_se(r == ret_noe);
         if (r >= 0)
-                assert_se(streq(buffer, expect));
+                ASSERT_STREQ(buffer, expect);
 }
 
 TEST(dns_label_unescape) {
@@ -131,12 +131,12 @@ static void test_dns_label_unescape_suffix_one(const char *what, const char *exp
         r = dns_label_unescape_suffix(what, &label, buffer, buffer_sz);
         assert_se(r == ret1);
         if (r >= 0)
-                assert_se(streq(buffer, expect1));
+                ASSERT_STREQ(buffer, expect1);
 
         r = dns_label_unescape_suffix(what, &label, buffer, buffer_sz);
         assert_se(r == ret2);
         if (r >= 0)
-                assert_se(streq(buffer, expect2));
+                ASSERT_STREQ(buffer, expect2);
 }
 
 TEST(dns_label_unescape_suffix) {
@@ -173,7 +173,7 @@ static void test_dns_label_escape_one(const char *what, size_t l, const char *ex
         if (r < 0)
                 return;
 
-        assert_se(streq_ptr(expect, t));
+        ASSERT_STREQ(expect, t);
 }
 
 TEST(dns_label_escape) {
@@ -193,7 +193,7 @@ static void test_dns_name_normalize_one(const char *what, const char *expect, in
         if (r < 0)
                 return;
 
-        assert_se(streq_ptr(expect, t));
+        ASSERT_STREQ(expect, t);
 }
 
 TEST(dns_name_normalize) {
@@ -332,7 +332,7 @@ static void test_dns_name_reverse_one(const char *address, const char *name) {
 
         assert_se(in_addr_from_string_auto(address, &familya, &a) >= 0);
         assert_se(dns_name_reverse(familya, &a, &p) >= 0);
-        assert_se(streq(p, name));
+        ASSERT_STREQ(p, name);
         assert_se(dns_name_address(p, &familyb, &b) > 0);
         assert_se(familya == familyb);
         assert_se(in_addr_equal(familya, &a, &b));
@@ -349,7 +349,7 @@ static void test_dns_name_concat_one(const char *a, const char *b, int r, const
         _cleanup_free_ char *p = NULL;
 
         assert_se(dns_name_concat(a, b, 0, &p) == r);
-        assert_se(streq_ptr(p, result));
+        ASSERT_STREQ(p, result);
 }
 
 TEST(dns_name_concat) {
@@ -486,14 +486,14 @@ static void test_dns_service_join_one(const char *a, const char *b, const char *
         log_info("%s, %s, %s, →%d, %s", strnull(a), strnull(b), strnull(c), r, strnull(d));
 
         assert_se(dns_service_join(a, b, c, &t) == r);
-        assert_se(streq_ptr(t, d));
+        ASSERT_STREQ(t, d);
 
         if (r < 0)
                 return;
 
         assert_se(dns_service_split(t, &x, &y, &z) >= 0);
-        assert_se(streq_ptr(a, x));
-        assert_se(streq_ptr(b, y));
+        ASSERT_STREQ(a, x);
+        ASSERT_STREQ(b, y);
         assert_se(dns_name_equal(c, z) > 0);
 }
 
@@ -518,9 +518,9 @@ static void test_dns_service_split_one(const char *joined, const char *a, const
         log_info("%s, %s, %s, %s, →%d", joined, strnull(a), strnull(b), strnull(c), r);
 
         assert_se(dns_service_split(joined, &x, &y, &z) == r);
-        assert_se(streq_ptr(x, a));
-        assert_se(streq_ptr(y, b));
-        assert_se(streq_ptr(z, c));
+        ASSERT_STREQ(x, a);
+        ASSERT_STREQ(y, b);
+        ASSERT_STREQ(z, c);
 
         if (r < 0)
                 return;
@@ -549,7 +549,7 @@ static void test_dns_name_change_suffix_one(const char *name, const char *old_su
         log_info("%s, %s, %s, →%s", name, old_suffix, new_suffix, strnull(result));
 
         assert_se(dns_name_change_suffix(name, old_suffix, new_suffix, &s) == r);
-        assert_se(streq_ptr(s, result));
+        ASSERT_STREQ(s, result);
 }
 
 TEST(dns_name_change_suffix) {
@@ -570,7 +570,7 @@ static void test_dns_name_suffix_one(const char *name, unsigned n_labels, const
         log_info("%s, %u, → %s, %d", name, n_labels, strnull(result), ret);
 
         assert_se(ret == dns_name_suffix(name, n_labels, &p));
-        assert_se(streq_ptr(p, result));
+        ASSERT_STREQ(p, result);
 }
 
 TEST(dns_name_suffix) {
@@ -660,7 +660,7 @@ static void test_dns_name_common_suffix_one(const char *a, const char *b, const
         log_info("%s, %s, →%s", a, b, result);
 
         assert_se(dns_name_common_suffix(a, b, &c) >= 0);
-        assert_se(streq(c, result));
+        ASSERT_STREQ(c, result);
 }
 
 TEST(dns_name_common_suffix) {
index c272c561d2909707596c17aa1051165e22e0a2a2..f1814768fd608e82a27958859eac3ebb60591e9d 100644 (file)
@@ -57,9 +57,9 @@ static void test_ellipsize_mem_one(const char *s, size_t old_length, size_t new_
                 assert_se(utf8_console_width(t3) <= max_width);
 
         if (new_length >= old_length) {
-                assert_se(streq(t1, n));
-                assert_se(streq(t2, n));
-                assert_se(streq(t3, n));
+                ASSERT_STREQ(t1, n);
+                ASSERT_STREQ(t2, n);
+                ASSERT_STREQ(t3, n);
         }
 }
 
@@ -143,17 +143,17 @@ TEST(ellipsize_ansi_cats) {
 
         e = ellipsize("01" ANSI_NORMAL "23", 4, 0);
         puts(e);
-        assert_se(streq(e, "01" ANSI_NORMAL "23"));
+        ASSERT_STREQ(e, "01" ANSI_NORMAL "23");
         f = ellipsize("ab" ANSI_NORMAL "cd", 4, 90);
         puts(f);
-        assert_se(streq(f, "ab" ANSI_NORMAL "cd"));
+        ASSERT_STREQ(f, "ab" ANSI_NORMAL "cd");
 
         g = ellipsize("🐱🐱" ANSI_NORMAL "🐱🐱" ANSI_NORMAL, 5, 0);
         puts(g);
-        assert_se(streq(g, "…" ANSI_NORMAL "🐱🐱" ANSI_NORMAL));
+        ASSERT_STREQ(g, "…" ANSI_NORMAL "🐱🐱" ANSI_NORMAL);
         h = ellipsize("🐱🐱" ANSI_NORMAL "🐱🐱" ANSI_NORMAL, 5, 90);
         puts(h);
-        assert_se(streq(h, "🐱…" ANSI_NORMAL "🐱" ANSI_NORMAL));
+        ASSERT_STREQ(h, "🐱…" ANSI_NORMAL "🐱" ANSI_NORMAL);
 }
 
 DEFINE_TEST_MAIN(LOG_INFO);
index e03d179d4e671b86b1546bef57ebd732485630c3..fa64fbefaba8500a8586cca20834de6e49444cb8 100644 (file)
@@ -214,7 +214,7 @@ int main(int argc, char *argv[]) {
         assert_se(manager_load_unit(m, "unit-with-multiple-dashes.service", NULL, NULL, &unit_with_multiple_dashes) >= 0);
 
         assert_se(strv_equal(unit_with_multiple_dashes->documentation, STRV_MAKE("man:test", "man:override2", "man:override3")));
-        assert_se(streq_ptr(unit_with_multiple_dashes->description, "override4"));
+        ASSERT_STREQ(unit_with_multiple_dashes->description, "override4");
 
         /* Now merge a synthetic unit into the existing one */
         assert_se(unit_new_for_name(m, sizeof(Service), "merged.service", &stub) >= 0);
index 3fc6d623fdd460fe38a6b0b8f8d4e69f2ef55177..f34b2aac72300eee2cf24deedae279fff1edf7e5 100644 (file)
@@ -65,13 +65,13 @@ TEST(load_env_file_1) {
 
         _cleanup_strv_free_ char **data = NULL;
         assert_se(load_env_file(NULL, name, &data) == 0);
-        assert_se(streq(data[0], "a=a"));
-        assert_se(streq(data[1], "b=bc"));
-        assert_se(streq(data[2], "d=de  f"));
-        assert_se(streq(data[3], "g=g "));
-        assert_se(streq(data[4], "h=ąęół śćńźżμ"));
-        assert_se(streq(data[5], "i=i"));
-        assert_se(data[6] == NULL);
+        ASSERT_STREQ(data[0], "a=a");
+        ASSERT_STREQ(data[1], "b=bc");
+        ASSERT_STREQ(data[2], "d=de  f");
+        ASSERT_STREQ(data[3], "g=g ");
+        ASSERT_STREQ(data[4], "h=ąęół śćńźżμ");
+        ASSERT_STREQ(data[5], "i=i");
+        ASSERT_NULL(data[6]);
 }
 
 TEST(load_env_file_2) {
@@ -80,8 +80,8 @@ TEST(load_env_file_2) {
 
         _cleanup_strv_free_ char **data = NULL;
         assert_se(load_env_file(NULL, name, &data) == 0);
-        assert_se(streq(data[0], "a=a"));
-        assert_se(data[1] == NULL);
+        ASSERT_STREQ(data[0], "a=a");
+        ASSERT_NULL(data[1]);
 }
 
 TEST(load_env_file_3) {
@@ -90,9 +90,9 @@ TEST(load_env_file_3) {
 
         _cleanup_strv_free_ char **data = NULL;
         assert_se(load_env_file(NULL, name, &data) == 0);
-        assert_se(streq(data[0], "normal1=line111"));
-        assert_se(streq(data[1], "normal2=line222"));
-        assert_se(data[2] == NULL);
+        ASSERT_STREQ(data[0], "normal1=line111");
+        ASSERT_STREQ(data[1], "normal2=line222");
+        ASSERT_NULL(data[2]);
 }
 
 TEST(load_env_file_4) {
@@ -101,10 +101,10 @@ TEST(load_env_file_4) {
 
         _cleanup_strv_free_ char **data = NULL;
         assert_se(load_env_file(NULL, name, &data) == 0);
-        assert_se(streq(data[0], "HWMON_MODULES=coretemp f71882fg"));
-        assert_se(streq(data[1], "MODULE_0=coretemp"));
-        assert_se(streq(data[2], "MODULE_1=f71882fg"));
-        assert_se(data[3] == NULL);
+        ASSERT_STREQ(data[0], "HWMON_MODULES=coretemp f71882fg");
+        ASSERT_STREQ(data[1], "MODULE_0=coretemp");
+        ASSERT_STREQ(data[2], "MODULE_1=f71882fg");
+        ASSERT_NULL(data[3]);
 }
 
 TEST(load_env_file_5) {
@@ -113,9 +113,9 @@ TEST(load_env_file_5) {
 
         _cleanup_strv_free_ char **data = NULL;
         assert_se(load_env_file(NULL, name, &data) == 0);
-        assert_se(streq(data[0], "a="));
-        assert_se(streq(data[1], "b="));
-        assert_se(data[2] == NULL);
+        ASSERT_STREQ(data[0], "a=");
+        ASSERT_STREQ(data[1], "b=");
+        ASSERT_NULL(data[2]);
 }
 
 TEST(load_env_file_6) {
@@ -124,11 +124,11 @@ TEST(load_env_file_6) {
 
         _cleanup_strv_free_ char **data = NULL;
         assert_se(load_env_file(NULL, name, &data) == 0);
-        assert_se(streq(data[0], "a= n t x y '"));
-        assert_se(streq(data[1], "b=$'"));
-        assert_se(streq(data[2], "c= \\n\\t\\$\\`\\\\\n"));
-        assert_se(streq(data[3], "d= \\n\\t$`\\\n"));
-        assert_se(data[4] == NULL);
+        ASSERT_STREQ(data[0], "a= n t x y '");
+        ASSERT_STREQ(data[1], "b=$'");
+        ASSERT_STREQ(data[2], "c= \\n\\t\\$\\`\\\\\n");
+        ASSERT_STREQ(data[3], "d= \\n\\t$`\\\n");
+        ASSERT_NULL(data[4]);
 }
 
 TEST(load_env_file_invalid_utf8) {
@@ -178,13 +178,13 @@ TEST(write_and_load_env_file) {
                 assert_se(f = popen(cmd, "re"));
                 assert_se(read_full_stream(f, &from_shell, &sz) >= 0);
                 assert_se(sz == strlen(v));
-                assert_se(streq(from_shell, v));
+                ASSERT_STREQ(from_shell, v);
 
                 assert_se(load_env_file(NULL, p, &l) >= 0);
                 assert_se(strv_equal(l, STRV_MAKE(j)));
 
                 assert_se(parse_env_file(NULL, p, "TEST", &w) >= 0);
-                assert_se(streq_ptr(w, v));
+                ASSERT_STREQ(w, v);
         }
 }
 
index c267c2e34189116acae7c18b1d179e2eaf377151..e2c009dc9c5a6ef23122d239985269cf8b8fed31 100644 (file)
@@ -26,26 +26,26 @@ TEST(strv_env_delete) {
         d = strv_env_delete(a, 2, b, c);
         assert_se(d);
 
-        assert_se(streq(d[0], "WALDO=WALDO"));
-        assert_se(streq(d[1], "WALDO="));
+        ASSERT_STREQ(d[0], "WALDO=WALDO");
+        ASSERT_STREQ(d[1], "WALDO=");
         assert_se(strv_length(d) == 2);
 }
 
 TEST(strv_env_get) {
         char **l = STRV_MAKE("ONE_OR_TWO=1", "THREE=3", "ONE_OR_TWO=2", "FOUR=4");
 
-        assert_se(streq(strv_env_get(l, "ONE_OR_TWO"), "2"));
-        assert_se(streq(strv_env_get(l, "THREE"), "3"));
-        assert_se(streq(strv_env_get(l, "FOUR"), "4"));
+        ASSERT_STREQ(strv_env_get(l, "ONE_OR_TWO"), "2");
+        ASSERT_STREQ(strv_env_get(l, "THREE"), "3");
+        ASSERT_STREQ(strv_env_get(l, "FOUR"), "4");
 }
 
 TEST(strv_env_pairs_get) {
         char **l = STRV_MAKE("ONE_OR_TWO", "1", "THREE", "3", "ONE_OR_TWO", "2", "FOUR", "4", "FIVE", "5", "SIX", "FIVE", "SEVEN", "7");
 
-        assert_se(streq(strv_env_pairs_get(l, "ONE_OR_TWO"), "2"));
-        assert_se(streq(strv_env_pairs_get(l, "THREE"), "3"));
-        assert_se(streq(strv_env_pairs_get(l, "FOUR"), "4"));
-        assert_se(streq(strv_env_pairs_get(l, "FIVE"), "5"));
+        ASSERT_STREQ(strv_env_pairs_get(l, "ONE_OR_TWO"), "2");
+        ASSERT_STREQ(strv_env_pairs_get(l, "THREE"), "3");
+        ASSERT_STREQ(strv_env_pairs_get(l, "FOUR"), "4");
+        ASSERT_STREQ(strv_env_pairs_get(l, "FIVE"), "5");
 }
 
 TEST(strv_env_unset) {
@@ -56,8 +56,8 @@ TEST(strv_env_unset) {
 
         assert_se(strv_env_unset(l, "SCHLUMPF") == l);
 
-        assert_se(streq(l[0], "PIEP"));
-        assert_se(streq(l[1], "NANANANA=YES"));
+        ASSERT_STREQ(l[0], "PIEP");
+        ASSERT_STREQ(l[1], "NANANANA=YES");
         assert_se(strv_length(l) == 2);
 }
 
@@ -67,22 +67,22 @@ TEST(strv_env_merge) {
 
         _cleanup_strv_free_ char **r = strv_env_merge(NULL, a, NULL, b, NULL, a, b, b, NULL);
         assert_se(r);
-        assert_se(streq(r[0], "FOO="));
-        assert_se(streq(r[1], "WALDO="));
-        assert_se(streq(r[2], "PIEP"));
-        assert_se(streq(r[3], "SCHLUMPF=SMURFF"));
-        assert_se(streq(r[4], "EQ==="));
-        assert_se(streq(r[5], "PIEP="));
-        assert_se(streq(r[6], "NANANANA=YES"));
+        ASSERT_STREQ(r[0], "FOO=");
+        ASSERT_STREQ(r[1], "WALDO=");
+        ASSERT_STREQ(r[2], "PIEP");
+        ASSERT_STREQ(r[3], "SCHLUMPF=SMURFF");
+        ASSERT_STREQ(r[4], "EQ===");
+        ASSERT_STREQ(r[5], "PIEP=");
+        ASSERT_STREQ(r[6], "NANANANA=YES");
         assert_se(strv_length(r) == 7);
 
         assert_se(strv_env_clean(r) == r);
-        assert_se(streq(r[0], "FOO="));
-        assert_se(streq(r[1], "WALDO="));
-        assert_se(streq(r[2], "SCHLUMPF=SMURFF"));
-        assert_se(streq(r[3], "EQ==="));
-        assert_se(streq(r[4], "PIEP="));
-        assert_se(streq(r[5], "NANANANA=YES"));
+        ASSERT_STREQ(r[0], "FOO=");
+        ASSERT_STREQ(r[1], "WALDO=");
+        ASSERT_STREQ(r[2], "SCHLUMPF=SMURFF");
+        ASSERT_STREQ(r[3], "EQ===");
+        ASSERT_STREQ(r[4], "PIEP=");
+        ASSERT_STREQ(r[5], "NANANANA=YES");
         assert_se(strv_length(r) == 6);
 }
 
@@ -96,8 +96,8 @@ TEST(strv_env_replace_strdup) {
 
         assert_se(strv_length(a) == 2);
         strv_sort(a);
-        assert_se(streq(a[0], "a=A"));
-        assert_se(streq(a[1], "b=b"));
+        ASSERT_STREQ(a[0], "a=A");
+        ASSERT_STREQ(a[1], "b=b");
 }
 
 TEST(strv_env_replace_strdup_passthrough) {
@@ -114,9 +114,9 @@ TEST(strv_env_replace_strdup_passthrough) {
         assert_se(strv_env_replace_strdup_passthrough(&a, "$a") == -EINVAL);
 
         assert_se(strv_length(a) == 3);
-        assert_se(streq(a[0], "a=a"));
-        assert_se(streq(a[1], "b="));
-        assert_se(streq(a[2], "c="));
+        ASSERT_STREQ(a[0], "a=a");
+        ASSERT_STREQ(a[1], "b=");
+        ASSERT_STREQ(a[2], "c=");
 }
 
 TEST(strv_env_assign) {
@@ -130,7 +130,7 @@ TEST(strv_env_assign) {
         assert_se(strv_env_assign(&a, "a=", "B") == -EINVAL);
 
         assert_se(strv_length(a) == 1);
-        assert_se(streq(a[0], "a=A"));
+        ASSERT_STREQ(a[0], "a=A");
 }
 
 TEST(strv_env_assignf) {
@@ -151,7 +151,7 @@ TEST(strv_env_assignf) {
         assert_se(strv_env_assignf(&a, "a=", "B") == -EINVAL);
 
         assert_se(strv_length(a) == 1);
-        assert_se(streq(a[0], "a=A"));
+        ASSERT_STREQ(a[0], "a=A");
 }
 
 TEST(strv_env_assign_many) {
@@ -190,15 +190,15 @@ TEST(env_strv_get_n) {
         };
         char **env = (char**) _env;
 
-        assert_se(streq(strv_env_get_n(env, "FOO__", 3, 0), "BAR BAR"));
-        assert_se(streq(strv_env_get_n(env, "FOO__", 3, REPLACE_ENV_USE_ENVIRONMENT), "BAR BAR"));
-        assert_se(streq(strv_env_get_n(env, "FOO", 3, 0), "BAR BAR"));
-        assert_se(streq(strv_env_get_n(env, "FOO", 3, REPLACE_ENV_USE_ENVIRONMENT), "BAR BAR"));
+        ASSERT_STREQ(strv_env_get_n(env, "FOO__", 3, 0), "BAR BAR");
+        ASSERT_STREQ(strv_env_get_n(env, "FOO__", 3, REPLACE_ENV_USE_ENVIRONMENT), "BAR BAR");
+        ASSERT_STREQ(strv_env_get_n(env, "FOO", 3, 0), "BAR BAR");
+        ASSERT_STREQ(strv_env_get_n(env, "FOO", 3, REPLACE_ENV_USE_ENVIRONMENT), "BAR BAR");
 
-        assert_se(streq(strv_env_get_n(env, "PATH__", 4, 0), "unset"));
-        assert_se(streq(strv_env_get_n(env, "PATH", 4, 0), "unset"));
-        assert_se(streq(strv_env_get_n(env, "PATH__", 4, REPLACE_ENV_USE_ENVIRONMENT), "unset"));
-        assert_se(streq(strv_env_get_n(env, "PATH", 4, REPLACE_ENV_USE_ENVIRONMENT), "unset"));
+        ASSERT_STREQ(strv_env_get_n(env, "PATH__", 4, 0), "unset");
+        ASSERT_STREQ(strv_env_get_n(env, "PATH", 4, 0), "unset");
+        ASSERT_STREQ(strv_env_get_n(env, "PATH__", 4, REPLACE_ENV_USE_ENVIRONMENT), "unset");
+        ASSERT_STREQ(strv_env_get_n(env, "PATH", 4, REPLACE_ENV_USE_ENVIRONMENT), "unset");
 
         env[3] = NULL; /* kill our $PATH */
 
@@ -222,19 +222,19 @@ static void test_replace_env1(bool braceless) {
         unsigned flags = REPLACE_ENV_ALLOW_BRACELESS*braceless;
 
         assert_se(replace_env("FOO=$FOO=${FOO}", (char**) env, flags, &t) >= 0);
-        assert_se(streq(t, braceless ? "FOO=BAR BAR=BAR BAR" : "FOO=$FOO=BAR BAR"));
+        ASSERT_STREQ(t, braceless ? "FOO=BAR BAR=BAR BAR" : "FOO=$FOO=BAR BAR");
 
         assert_se(replace_env("BAR=$BAR=${BAR}", (char**) env, flags, &s) >= 0);
-        assert_se(streq(s, braceless ? "BAR=waldo=waldo" : "BAR=$BAR=waldo"));
+        ASSERT_STREQ(s, braceless ? "BAR=waldo=waldo" : "BAR=$BAR=waldo");
 
         assert_se(replace_env("BARBAR=$BARBAR=${BARBAR}", (char**) env, flags, &q) >= 0);
-        assert_se(streq(q, braceless ? "BARBAR==" : "BARBAR=$BARBAR="));
+        ASSERT_STREQ(q, braceless ? "BARBAR==" : "BARBAR=$BARBAR=");
 
         assert_se(replace_env("BAR=$BAR$BAR${BAR}${BAR}", (char**) env, flags, &r) >= 0);
-        assert_se(streq(r, braceless ? "BAR=waldowaldowaldowaldo" : "BAR=$BAR$BARwaldowaldo"));
+        ASSERT_STREQ(r, braceless ? "BAR=waldowaldowaldowaldo" : "BAR=$BAR$BARwaldowaldo");
 
         assert_se(replace_env("${BAR}$BAR$BAR", (char**) env, flags, &p) >= 0);
-        assert_se(streq(p, braceless ? "waldowaldowaldo" : "waldo$BAR$BAR"));
+        ASSERT_STREQ(p, braceless ? "waldowaldowaldo" : "waldo$BAR$BAR");
 }
 
 static void test_replace_env2(bool extended) {
@@ -249,25 +249,25 @@ static void test_replace_env2(bool extended) {
         unsigned flags = REPLACE_ENV_ALLOW_EXTENDED*extended;
 
         assert_se(replace_env("FOO=${FOO:-${BAR}}", (char**) env, flags, &t) >= 0);
-        assert_se(streq(t, extended ? "FOO=foo" : "FOO=${FOO:-bar}"));
+        ASSERT_STREQ(t, extended ? "FOO=foo" : "FOO=${FOO:-bar}");
 
         assert_se(replace_env("BAR=${XXX:-${BAR}}", (char**) env, flags, &s) >= 0);
-        assert_se(streq(s, extended ? "BAR=bar" : "BAR=${XXX:-bar}"));
+        ASSERT_STREQ(s, extended ? "BAR=bar" : "BAR=${XXX:-bar}");
 
         assert_se(replace_env("XXX=${XXX:+${BAR}}", (char**) env, flags, &q) >= 0);
-        assert_se(streq(q, extended ? "XXX=" : "XXX=${XXX:+bar}"));
+        ASSERT_STREQ(q, extended ? "XXX=" : "XXX=${XXX:+bar}");
 
         assert_se(replace_env("FOO=${FOO:+${BAR}}", (char**) env, flags, &r) >= 0);
-        assert_se(streq(r, extended ? "FOO=bar" : "FOO=${FOO:+bar}"));
+        ASSERT_STREQ(r, extended ? "FOO=bar" : "FOO=${FOO:+bar}");
 
         assert_se(replace_env("FOO=${FOO:-${BAR}post}", (char**) env, flags, &p) >= 0);
-        assert_se(streq(p, extended ? "FOO=foo" : "FOO=${FOO:-barpost}"));
+        ASSERT_STREQ(p, extended ? "FOO=foo" : "FOO=${FOO:-barpost}");
 
         assert_se(replace_env("XXX=${XXX:+${BAR}post}", (char**) env, flags, &x) >= 0);
-        assert_se(streq(x, extended ? "XXX=" : "XXX=${XXX:+barpost}"));
+        ASSERT_STREQ(x, extended ? "XXX=" : "XXX=${XXX:+barpost}");
 
         assert_se(replace_env("FOO=${FOO}between${BAR:-baz}", (char**) env, flags, &y) >= 0);
-        assert_se(streq(y, extended ? "FOO=foobetweenbar" : "FOO=foobetween${BAR:-baz}"));
+        ASSERT_STREQ(y, extended ? "FOO=foobetweenbar" : "FOO=foobetween${BAR:-baz}");
 }
 
 TEST(replace_env) {
@@ -307,23 +307,23 @@ TEST(replace_env_argv) {
 
         assert_se(replace_env_argv((char**) line, (char**) env, &r, NULL, NULL) >= 0);
         assert_se(r);
-        assert_se(streq(r[0], "FOO$FOO"));
-        assert_se(streq(r[1], "FOO$FOOFOO"));
-        assert_se(streq(r[2], "FOOBAR BAR$FOO"));
-        assert_se(streq(r[3], "FOOBAR BAR"));
-        assert_se(streq(r[4], "BAR BAR"));
-        assert_se(streq(r[5], "BAR"));
-        assert_se(streq(r[6], "BAR"));
-        assert_se(streq(r[7], "BAR BARwaldo"));
-        assert_se(streq(r[8], "${FOO"));
-        assert_se(streq(r[9], "FOO$BAR BAR"));
-        assert_se(streq(r[10], "$FOOBAR BAR"));
-        assert_se(streq(r[11], "${FOO:-waldo}"));
-        assert_se(streq(r[12], "${QUUX:-BAR BAR}"));
-        assert_se(streq(r[13], "${FOO:+waldo}"));
-        assert_se(streq(r[14], "${QUUX:+waldo}"));
-        assert_se(streq(r[15], "${FOO:+|waldo|}}"));
-        assert_se(streq(r[16], "${FOO:+|waldo{|}"));
+        ASSERT_STREQ(r[0], "FOO$FOO");
+        ASSERT_STREQ(r[1], "FOO$FOOFOO");
+        ASSERT_STREQ(r[2], "FOOBAR BAR$FOO");
+        ASSERT_STREQ(r[3], "FOOBAR BAR");
+        ASSERT_STREQ(r[4], "BAR BAR");
+        ASSERT_STREQ(r[5], "BAR");
+        ASSERT_STREQ(r[6], "BAR");
+        ASSERT_STREQ(r[7], "BAR BARwaldo");
+        ASSERT_STREQ(r[8], "${FOO");
+        ASSERT_STREQ(r[9], "FOO$BAR BAR");
+        ASSERT_STREQ(r[10], "$FOOBAR BAR");
+        ASSERT_STREQ(r[11], "${FOO:-waldo}");
+        ASSERT_STREQ(r[12], "${QUUX:-BAR BAR}");
+        ASSERT_STREQ(r[13], "${FOO:+waldo}");
+        ASSERT_STREQ(r[14], "${QUUX:+waldo}");
+        ASSERT_STREQ(r[15], "${FOO:+|waldo|}}");
+        ASSERT_STREQ(r[16], "${FOO:+|waldo{|}");
         assert_se(strv_length(r) == 17);
 }
 
@@ -393,15 +393,15 @@ TEST(env_clean) {
         assert_se(strv_env_clean(e) == e);
         assert_se(strv_env_is_valid(e));
 
-        assert_se(streq(e[0], "FOOBAR=WALDO"));
-        assert_se(streq(e[1], "X="));
-        assert_se(streq(e[2], "F=F"));
-        assert_se(streq(e[3], "abcd=äöüß"));
-        assert_se(streq(e[4], "xyz=xyz\n"));
-        assert_se(streq(e[5], "another=final one"));
-        assert_se(streq(e[6], "CRLF=\r\n"));
-        assert_se(streq(e[7], "LESS_TERMCAP_mb=\x1b[01;31m"));
-        assert_se(e[8] == NULL);
+        ASSERT_STREQ(e[0], "FOOBAR=WALDO");
+        ASSERT_STREQ(e[1], "X=");
+        ASSERT_STREQ(e[2], "F=F");
+        ASSERT_STREQ(e[3], "abcd=äöüß");
+        ASSERT_STREQ(e[4], "xyz=xyz\n");
+        ASSERT_STREQ(e[5], "another=final one");
+        ASSERT_STREQ(e[6], "CRLF=\r\n");
+        ASSERT_STREQ(e[7], "LESS_TERMCAP_mb=\x1b[01;31m");
+        ASSERT_NULL(e[8]);
 }
 
 TEST(env_name_is_valid) {
@@ -451,13 +451,13 @@ TEST(env_assignment_is_valid) {
 
 TEST(putenv_dup) {
         assert_se(putenv_dup("A=a1", true) == 0);
-        assert_se(streq_ptr(getenv("A"), "a1"));
+        ASSERT_STREQ(getenv("A"), "a1");
         assert_se(putenv_dup("A=a1", true) == 0);
-        assert_se(streq_ptr(getenv("A"), "a1"));
+        ASSERT_STREQ(getenv("A"), "a1");
         assert_se(putenv_dup("A=a2", false) == 0);
-        assert_se(streq_ptr(getenv("A"), "a1"));
+        ASSERT_STREQ(getenv("A"), "a1");
         assert_se(putenv_dup("A=a2", true) == 0);
-        assert_se(streq_ptr(getenv("A"), "a2"));
+        ASSERT_STREQ(getenv("A"), "a2");
 }
 
 TEST(setenv_systemd_exec_pid) {
@@ -476,7 +476,7 @@ TEST(setenv_systemd_exec_pid) {
         assert_se(setenv("SYSTEMD_EXEC_PID", "*", 1) >= 0);
         assert_se(setenv_systemd_exec_pid(true) == 0);
         assert_se(e = getenv("SYSTEMD_EXEC_PID"));
-        assert_se(streq(e, "*"));
+        ASSERT_STREQ(e, "*");
 
         assert_se(setenv("SYSTEMD_EXEC_PID", "123abc", 1) >= 0);
         assert_se(setenv_systemd_exec_pid(true) == 1);
@@ -523,10 +523,10 @@ TEST(getenv_steal_erase) {
                         copy1 = strdup(eq + 1);
                         assert_se(copy1);
 
-                        assert_se(streq_ptr(getenv(n), copy1));
+                        ASSERT_STREQ(getenv(n), copy1);
                         assert_se(getenv(n) == eq + 1);
                         assert_se(getenv_steal_erase(n, &copy2) > 0);
-                        assert_se(streq_ptr(copy1, copy2));
+                        ASSERT_STREQ(copy1, copy2);
                         assert_se(isempty(eq + 1));
                         assert_se(!getenv(n));
                 }
@@ -571,12 +571,12 @@ TEST(getenv_path_list) {
         /* Finally some valid paths */
         assert_se(setenv("TEST_GETENV_PATH_LIST", "/foo:/bar/baz:/hello/world:/path with spaces:/final", 1) >= 0);
         assert_se(getenv_path_list("TEST_GETENV_PATH_LIST", &path_list) >= 0);
-        assert_se(streq(path_list[0], "/foo"));
-        assert_se(streq(path_list[1], "/bar/baz"));
-        assert_se(streq(path_list[2], "/hello/world"));
-        assert_se(streq(path_list[3], "/path with spaces"));
-        assert_se(streq(path_list[4], "/final"));
-        assert_se(path_list[5] == NULL);
+        ASSERT_STREQ(path_list[0], "/foo");
+        ASSERT_STREQ(path_list[1], "/bar/baz");
+        ASSERT_STREQ(path_list[2], "/hello/world");
+        ASSERT_STREQ(path_list[3], "/path with spaces");
+        ASSERT_STREQ(path_list[4], "/final");
+        ASSERT_NULL(path_list[5]);
 
         assert_se(unsetenv("TEST_GETENV_PATH_LIST") >= 0);
 }
index f91a1f770f0047142c6a7ef00a25eccb0fcdf052..77f418750cd4de15203245a72b9d7d909b60842e 100644 (file)
 TEST(errno_list) {
         for (size_t i = 0; i < ELEMENTSOF(errno_names); i++) {
                 if (errno_names[i]) {
-                        assert_se(streq(errno_to_name(i), errno_names[i]));
+                        ASSERT_STREQ(errno_to_name(i), errno_names[i]);
                         assert_se(errno_from_name(errno_names[i]) == (int) i);
                 }
         }
 
 #ifdef ECANCELLED
         /* ECANCELLED is an alias of ECANCELED. */
-        assert_se(streq(errno_to_name(ECANCELLED), "ECANCELED"));
+        ASSERT_STREQ(errno_to_name(ECANCELLED), "ECANCELED");
 #endif
-        assert_se(streq(errno_to_name(ECANCELED), "ECANCELED"));
+        ASSERT_STREQ(errno_to_name(ECANCELED), "ECANCELED");
 
 #ifdef EREFUSED
         /* EREFUSED is an alias of ECONNREFUSED. */
-        assert_se(streq(errno_to_name(EREFUSED), "ECONNREFUSED"));
+        ASSERT_STREQ(errno_to_name(EREFUSED), "ECONNREFUSED");
 #endif
-        assert_se(streq(errno_to_name(ECONNREFUSED), "ECONNREFUSED"));
+        ASSERT_STREQ(errno_to_name(ECONNREFUSED), "ECONNREFUSED");
 }
 
 DEFINE_TEST_MAIN(LOG_INFO);
index 376d532281af001cd7f8cfd314b88cd04a0a03c9..ab463bd1b394b791238df41ccbd22e8053c294eb 100644 (file)
@@ -31,8 +31,8 @@ TEST(STRERROR) {
         assert_se(strstr(b, "201"));
 
         /* Check with negative values */
-        assert_se(streq(a, STRERROR(-200)));
-        assert_se(streq(b, STRERROR(-201)));
+        ASSERT_STREQ(a, STRERROR(-200));
+        ASSERT_STREQ(b, STRERROR(-201));
 
         const char *c = STRERROR(INT_MAX);
         char buf[DECIMAL_STR_MAX(int)];
index 364e0f395643ab54c7937b0cf5013ad2007fbe96..89a25febb43ff8cc47d2a8af77096776b01d415b 100644 (file)
@@ -9,14 +9,14 @@ TEST(cescape) {
         _cleanup_free_ char *t = NULL;
 
         assert_se(t = cescape("abc\\\"\b\f\n\r\t\v\a\003\177\234\313"));
-        assert_se(streq(t, "abc\\\\\\\"\\b\\f\\n\\r\\t\\v\\a\\003\\177\\234\\313"));
+        ASSERT_STREQ(t, "abc\\\\\\\"\\b\\f\\n\\r\\t\\v\\a\\003\\177\\234\\313");
 }
 
 TEST(xescape) {
         _cleanup_free_ char *t = NULL;
 
         assert_se(t = xescape("abc\\\"\b\f\n\r\t\v\a\003\177\234\313", ""));
-        assert_se(streq(t, "abc\\x5c\"\\x08\\x0c\\x0a\\x0d\\x09\\x0b\\x07\\x03\\x7f\\x9c\\xcb"));
+        ASSERT_STREQ(t, "abc\\x5c\"\\x08\\x0c\\x0a\\x0d\\x09\\x0b\\x07\\x03\\x7f\\x9c\\xcb");
 }
 
 static void test_xescape_full_one(bool eight_bits) {
@@ -36,7 +36,7 @@ static void test_xescape_full_one(bool eight_bits) {
                 log_info("%02u: <%s>", i, t);
 
                 if (i >= full_fit)
-                        assert_se(streq(t, escaped));
+                        ASSERT_STREQ(t, escaped);
                 else if (i >= 3) {
                         /* We need up to four columns, so up to three columns may be wasted */
                         assert_se(strlen(t) == i || strlen(t) == i - 1 || strlen(t) == i - 2 || strlen(t) == i - 3);
@@ -69,50 +69,50 @@ TEST(cunescape) {
 
         assert_se(cunescape("abc\\\\\\\"\\b\\f\\a\\n\\r\\t\\v\\003\\177\\234\\313\\000\\x00", 0, &unescaped) < 0);
         assert_se(cunescape("abc\\\\\\\"\\b\\f\\a\\n\\r\\t\\v\\003\\177\\234\\313\\000\\x00", UNESCAPE_RELAX, &unescaped) >= 0);
-        assert_se(streq_ptr(unescaped, "abc\\\"\b\f\a\n\r\t\v\003\177\234\313\\000\\x00"));
+        ASSERT_STREQ(unescaped, "abc\\\"\b\f\a\n\r\t\v\003\177\234\313\\000\\x00");
         unescaped = mfree(unescaped);
 
         /* incomplete sequences */
         assert_se(cunescape("\\x0", 0, &unescaped) < 0);
         assert_se(cunescape("\\x0", UNESCAPE_RELAX, &unescaped) >= 0);
-        assert_se(streq_ptr(unescaped, "\\x0"));
+        ASSERT_STREQ(unescaped, "\\x0");
         unescaped = mfree(unescaped);
 
         assert_se(cunescape("\\x", 0, &unescaped) < 0);
         assert_se(cunescape("\\x", UNESCAPE_RELAX, &unescaped) >= 0);
-        assert_se(streq_ptr(unescaped, "\\x"));
+        ASSERT_STREQ(unescaped, "\\x");
         unescaped = mfree(unescaped);
 
         assert_se(cunescape("\\", 0, &unescaped) < 0);
         assert_se(cunescape("\\", UNESCAPE_RELAX, &unescaped) >= 0);
-        assert_se(streq_ptr(unescaped, "\\"));
+        ASSERT_STREQ(unescaped, "\\");
         unescaped = mfree(unescaped);
 
         assert_se(cunescape("\\11", 0, &unescaped) < 0);
         assert_se(cunescape("\\11", UNESCAPE_RELAX, &unescaped) >= 0);
-        assert_se(streq_ptr(unescaped, "\\11"));
+        ASSERT_STREQ(unescaped, "\\11");
         unescaped = mfree(unescaped);
 
         assert_se(cunescape("\\1", 0, &unescaped) < 0);
         assert_se(cunescape("\\1", UNESCAPE_RELAX, &unescaped) >= 0);
-        assert_se(streq_ptr(unescaped, "\\1"));
+        ASSERT_STREQ(unescaped, "\\1");
         unescaped = mfree(unescaped);
 
         assert_se(cunescape("\\u0000", 0, &unescaped) < 0);
         assert_se(cunescape("\\u00DF\\U000000df\\u03a0\\U00000041", UNESCAPE_RELAX, &unescaped) >= 0);
-        assert_se(streq_ptr(unescaped, "ßßΠA"));
+        ASSERT_STREQ(unescaped, "ßßΠA");
         unescaped = mfree(unescaped);
 
         assert_se(cunescape("\\073", 0, &unescaped) >= 0);
-        assert_se(streq_ptr(unescaped, ";"));
+        ASSERT_STREQ(unescaped, ";");
         unescaped = mfree(unescaped);
 
         assert_se(cunescape("A=A\\\\x0aB", 0, &unescaped) >= 0);
-        assert_se(streq_ptr(unescaped, "A=A\\x0aB"));
+        ASSERT_STREQ(unescaped, "A=A\\x0aB");
         unescaped = mfree(unescaped);
 
         assert_se(cunescape("A=A\\\\x0aB", UNESCAPE_RELAX, &unescaped) >= 0);
-        assert_se(streq_ptr(unescaped, "A=A\\x0aB"));
+        ASSERT_STREQ(unescaped, "A=A\\x0aB");
         unescaped = mfree(unescaped);
 
         assert_se(cunescape("\\x00\\x00\\x00", UNESCAPE_ACCEPT_NUL, &unescaped) == 3);
@@ -136,7 +136,7 @@ static void test_shell_escape_one(const char *s, const char *bad, const char *ex
 
         assert_se(r = shell_escape(s, bad));
         log_debug("%s → %s (expected %s)", s, r, expected);
-        assert_se(streq_ptr(r, expected));
+        ASSERT_STREQ(r, expected);
 }
 
 TEST(shell_escape) {
@@ -153,7 +153,7 @@ static void test_shell_maybe_quote_one(const char *s, ShellEscapeFlags flags, co
 
         assert_se(ret = shell_maybe_quote(s, flags));
         log_debug("[%s] → [%s] (%s)", s, ret, expected);
-        assert_se(streq(ret, expected));
+        ASSERT_STREQ(ret, expected);
 }
 
 TEST(shell_maybe_quote) {
@@ -207,7 +207,7 @@ static void test_quote_command_line_one(char **argv, const char *expected) {
 
         assert_se(s = quote_command_line(argv, SHELL_ESCAPE_EMPTY));
         log_info("%s", s);
-        assert_se(streq(s, expected));
+        ASSERT_STREQ(s, expected);
 }
 
 TEST(quote_command_line) {
@@ -228,7 +228,7 @@ static void test_octescape_one(const char *s, const char *expected) {
 
         assert_se(ret = octescape(s, strlen_ptr(s)));
         log_debug("octescape(\"%s\") → \"%s\" (expected: \"%s\")", strnull(s), ret, expected);
-        assert_se(streq(ret, expected));
+        ASSERT_STREQ(ret, expected);
 }
 
 TEST(octescape) {
@@ -244,7 +244,7 @@ static void test_decescape_one(const char *s, const char *bad, const char *expec
 
         assert_se(ret = decescape(s, bad, strlen_ptr(s)));
         log_debug("decescape(\"%s\") → \"%s\" (expected: \"%s\")", strnull(s), ret, expected);
-        assert_se(streq(ret, expected));
+        ASSERT_STREQ(ret, expected);
 }
 
 TEST(decescape) {
index d680f80a7008869b141f4b5e07602f3cd3d8d8d8..4826a6f13b0b9e88ec381829e5c22329784f05ea 100644 (file)
@@ -72,7 +72,7 @@ static void test_parse_hw_addr_full_one(const char *in, size_t expected_len, con
         if (r >= 0) {
                 if (!IN_SET(expected_len, 0, SIZE_MAX))
                         assert_se(h.length == expected_len);
-                assert_se(streq(HW_ADDR_TO_STR(&h), expected));
+                ASSERT_STREQ(HW_ADDR_TO_STR(&h), expected);
         }
 }
 
index 2304f6a8b6751c9e52b56e8bfeb96c46b5bc3a03..a57786037bc3fd715c6dcbba05aa3de3fa7d7965 100644 (file)
@@ -195,7 +195,7 @@ TEST(execution_order) {
         execute_directories(dirs, DEFAULT_TIMEOUT_USEC, ignore_stdout, ignore_stdout_args, NULL, NULL, EXEC_DIR_PARALLEL | EXEC_DIR_IGNORE_ERRORS);
 
         assert_se(read_full_file(output, &contents, NULL) >= 0);
-        assert_se(streq(contents, "30-override\n80-foo\n90-bar\nlast\n"));
+        ASSERT_STREQ(contents, "30-override\n80-foo\n90-bar\nlast\n");
 }
 
 static int gather_stdout_one(int fd, void *arg) {
@@ -279,7 +279,7 @@ TEST(stdout_gathering) {
 
         log_info("got: %s", output);
 
-        assert_se(streq(output, "a\nb\nc\nd\n"));
+        ASSERT_STREQ(output, "a\nb\nc\nd\n");
 }
 
 TEST(environment_gathering) {
@@ -346,10 +346,10 @@ TEST(environment_gathering) {
         STRV_FOREACH(p, env)
                 log_info("got env: \"%s\"", *p);
 
-        assert_se(streq(strv_env_get(env, "A"), "22:23:24"));
-        assert_se(streq(strv_env_get(env, "B"), "12"));
-        assert_se(streq(strv_env_get(env, "C"), "001"));
-        assert_se(streq(strv_env_get(env, "PATH"), "no-sh-built-in-path:/no/such/file"));
+        ASSERT_STREQ(strv_env_get(env, "A"), "22:23:24");
+        ASSERT_STREQ(strv_env_get(env, "B"), "12");
+        ASSERT_STREQ(strv_env_get(env, "C"), "001");
+        ASSERT_STREQ(strv_env_get(env, "PATH"), "no-sh-built-in-path:/no/such/file");
 
         /* now retest with "default" path passed in, as created by
          * manager_default_environment */
@@ -363,10 +363,10 @@ TEST(environment_gathering) {
         STRV_FOREACH(p, env)
                 log_info("got env: \"%s\"", *p);
 
-        assert_se(streq(strv_env_get(env, "A"), "22:23:24"));
-        assert_se(streq(strv_env_get(env, "B"), "12"));
-        assert_se(streq(strv_env_get(env, "C"), "001"));
-        assert_se(streq(strv_env_get(env, "PATH"), DEFAULT_PATH ":/no/such/file"));
+        ASSERT_STREQ(strv_env_get(env, "A"), "22:23:24");
+        ASSERT_STREQ(strv_env_get(env, "B"), "12");
+        ASSERT_STREQ(strv_env_get(env, "C"), "001");
+        ASSERT_STREQ(strv_env_get(env, "PATH"), DEFAULT_PATH ":/no/such/file");
 
         /* reset environ PATH */
         assert_se(set_unset_env("PATH", old, true) == 0);
index 76659fe0717107c223c0848de17b97ecb032576e..e9b922b42ef34d5b8bb7d665a4b7701aac0aab76 100644 (file)
@@ -7,6 +7,7 @@
 
 #include "sd-event.h"
 
+#include "build-path.h"
 #include "capability-util.h"
 #include "cpu-set-util.h"
 #include "copy.h"
@@ -455,6 +456,9 @@ static void test_exec_personality(Manager *m) {
 #if defined(__x86_64__)
         test(m, "exec-personality-x86-64.service", 0, CLD_EXITED);
 
+#elif defined(__s390x__)
+        test(m, "exec-personality-s390x.service", 0, CLD_EXITED);
+
 #elif defined(__s390__)
         test(m, "exec-personality-s390.service", 0, CLD_EXITED);
 
@@ -1263,7 +1267,11 @@ static void test_exec_specifier(Manager *m) {
 static void test_exec_standardinput(Manager *m) {
         test(m, "exec-standardinput-data.service", 0, CLD_EXITED);
         test(m, "exec-standardinput-file.service", 0, CLD_EXITED);
+
+        ExecOutput saved = m->defaults.std_output;
+        m->defaults.std_output = EXEC_OUTPUT_NULL;
         test(m, "exec-standardinput-file-cat.service", 0, CLD_EXITED);
+        m->defaults.std_output = saved;
 }
 
 static void test_exec_standardoutput(Manager *m) {
@@ -1386,7 +1394,7 @@ static void run_tests(RuntimeScope scope, char **patterns) {
                 return (void) log_tests_skipped_errno(r, "manager_new");
         assert_se(r >= 0);
 
-        m->defaults.std_output = EXEC_OUTPUT_NULL; /* don't rely on host journald */
+        m->defaults.std_output = EXEC_OUTPUT_INHERIT; /* don't rely on host journald */
         assert_se(manager_startup(m, NULL, NULL, NULL) >= 0);
 
         /* Uncomment below if you want to make debugging logs stored to journal. */
@@ -1428,7 +1436,8 @@ static int prepare_ns(const char *process_name) {
                       NULL);
         assert_se(r >= 0);
         if (r == 0) {
-                _cleanup_free_ char *unit_dir = NULL;
+                _cleanup_free_ char *unit_dir = NULL, *build_dir = NULL, *build_dir_mount = NULL;
+                int ret;
 
                 /* Make "/" read-only. */
                 assert_se(mount_nofollow_verbose(LOG_DEBUG, NULL, "/", NULL, MS_BIND|MS_REMOUNT|MS_RDONLY, NULL) >= 0);
@@ -1440,15 +1449,42 @@ static int prepare_ns(const char *process_name) {
 
                 assert_se(mkdir_p(PRIVATE_UNIT_DIR, 0755) >= 0);
                 assert_se(mount_nofollow_verbose(LOG_DEBUG, "tmpfs", PRIVATE_UNIT_DIR, "tmpfs", MS_NOSUID|MS_NODEV, NULL) >= 0);
+                /* Mark our test "playground" as MS_SLAVE, so we can MS_MOVE mounts underneath it. */
+                assert_se(mount_nofollow_verbose(LOG_DEBUG, NULL, PRIVATE_UNIT_DIR, NULL, MS_SLAVE, NULL) >= 0);
 
                 /* Copy unit files to make them accessible even when unprivileged. */
                 assert_se(get_testdata_dir("test-execute/", &unit_dir) >= 0);
                 assert_se(copy_directory_at(AT_FDCWD, unit_dir, AT_FDCWD, PRIVATE_UNIT_DIR, COPY_MERGE_EMPTY) >= 0);
 
                 /* Mount tmpfs on the following directories to make not StateDirectory= or friends disturb the host. */
+                ret = get_build_exec_dir(&build_dir);
+                assert_se(ret >= 0 || ret == -ENOEXEC);
+
+                if (build_dir) {
+                        /* Account for a build directory being in one of the soon-to-be-tmpfs directories. If we
+                         * overmount it with an empty tmpfs, manager_new() will pin the wrong systemd-executor binary,
+                         * which can then lead to unexpected (and painful to debug) test fails. */
+                        assert_se(access(build_dir, F_OK) >= 0);
+                        assert_se(build_dir_mount = path_join(PRIVATE_UNIT_DIR, "build_dir"));
+                        assert_se(mkdir_p(build_dir_mount, 0755) >= 0);
+                        assert_se(mount_nofollow_verbose(LOG_DEBUG, build_dir, build_dir_mount, NULL, MS_BIND, NULL) >= 0);
+                }
+
                 FOREACH_STRING(p, "/dev/shm", "/root", "/tmp", "/var/tmp", "/var/lib")
                         assert_se(mount_nofollow_verbose(LOG_DEBUG, "tmpfs", p, "tmpfs", MS_NOSUID|MS_NODEV, NULL) >= 0);
 
+                if (build_dir_mount) {
+                        ret = RET_NERRNO(access(build_dir, F_OK));
+                        assert_se(ret >= 0 || ret == -ENOENT);
+
+                        if (ret == -ENOENT) {
+                                /* The build directory got overmounted by tmpfs, so let's use the "backup" bind mount to
+                                 * bring it back. */
+                                assert_se(mkdir_p(build_dir, 0755) >= 0);
+                                assert_se(mount_nofollow_verbose(LOG_DEBUG, build_dir_mount, build_dir, NULL, MS_MOVE, NULL) >= 0);
+                        }
+                }
+
                 /* Prepare credstore like tmpfiles.d/credstore.conf for LoadCredential= tests. */
                 FOREACH_STRING(p, "/run/credstore", "/run/credstore.encrypted") {
                         assert_se(mkdir_p(p, 0) >= 0);
index 86d39767d49a7ee23413b942ca9b4f1601d173ef..aab81935192f818b42db70f3f9caab8a6312964e 100644 (file)
@@ -29,8 +29,8 @@ TEST(exit_status_from_string) {
 }
 
 TEST(exit_status_NUMA_POLICY) {
-        assert_se(streq(exit_status_to_string(EXIT_NUMA_POLICY, EXIT_STATUS_FULL), "NUMA_POLICY"));
-        assert_se(streq(exit_status_to_string(EXIT_NUMA_POLICY, EXIT_STATUS_SYSTEMD), "NUMA_POLICY"));
+        ASSERT_STREQ(exit_status_to_string(EXIT_NUMA_POLICY, EXIT_STATUS_FULL), "NUMA_POLICY");
+        ASSERT_STREQ(exit_status_to_string(EXIT_NUMA_POLICY, EXIT_STATUS_SYSTEMD), "NUMA_POLICY");
         assert_se(!exit_status_to_string(EXIT_NUMA_POLICY, EXIT_STATUS_BSD));
         assert_se(!exit_status_to_string(EXIT_NUMA_POLICY, EXIT_STATUS_LSB));
 }
index 0f10fe7e6f5aa37f71df699233009278927da964..1bc4088fb42e3d089429b524f043a9bce513ec92 100644 (file)
@@ -14,12 +14,12 @@ TEST(extract_first_word) {
 
         p = original = "foobar waldo";
         assert_se(extract_first_word(&p, &t, NULL, 0) > 0);
-        assert_se(streq(t, "foobar"));
+        ASSERT_STREQ(t, "foobar");
         free(t);
         assert_se(p == original + 7);
 
         assert_se(extract_first_word(&p, &t, NULL, 0) > 0);
-        assert_se(streq(t, "waldo"));
+        ASSERT_STREQ(t, "waldo");
         free(t);
         assert_se(isempty(p));
 
@@ -29,12 +29,12 @@ TEST(extract_first_word) {
 
         p = original = "\"foobar\" \'waldo\'";
         assert_se(extract_first_word(&p, &t, NULL, 0) > 0);
-        assert_se(streq(t, "\"foobar\""));
+        ASSERT_STREQ(t, "\"foobar\"");
         free(t);
         assert_se(p == original + 9);
 
         assert_se(extract_first_word(&p, &t, NULL, 0) > 0);
-        assert_se(streq(t, "\'waldo\'"));
+        ASSERT_STREQ(t, "\'waldo\'");
         free(t);
         assert_se(isempty(p));
 
@@ -44,12 +44,12 @@ TEST(extract_first_word) {
 
         p = original = "\"foobar\" \'waldo\'";
         assert_se(extract_first_word(&p, &t, NULL, EXTRACT_UNQUOTE) > 0);
-        assert_se(streq(t, "foobar"));
+        ASSERT_STREQ(t, "foobar");
         free(t);
         assert_se(p == original + 9);
 
         assert_se(extract_first_word(&p, &t, NULL, EXTRACT_UNQUOTE) > 0);
-        assert_se(streq(t, "waldo"));
+        ASSERT_STREQ(t, "waldo");
         free(t);
         assert_se(isempty(p));
 
@@ -59,7 +59,7 @@ TEST(extract_first_word) {
 
         p = original = "\"";
         assert_se(extract_first_word(&p, &t, NULL, 0) == 1);
-        assert_se(streq(t, "\""));
+        ASSERT_STREQ(t, "\"");
         free(t);
         assert_se(isempty(p));
 
@@ -69,7 +69,7 @@ TEST(extract_first_word) {
 
         p = original = "\'";
         assert_se(extract_first_word(&p, &t, NULL, 0) == 1);
-        assert_se(streq(t, "\'"));
+        ASSERT_STREQ(t, "\'");
         free(t);
         assert_se(isempty(p));
 
@@ -79,31 +79,31 @@ TEST(extract_first_word) {
 
         p = original = "\'fooo";
         assert_se(extract_first_word(&p, &t, NULL, 0) == 1);
-        assert_se(streq(t, "\'fooo"));
+        ASSERT_STREQ(t, "\'fooo");
         free(t);
         assert_se(isempty(p));
 
         p = original = "KEY=val \"KEY2=val with space\" \"KEY3=val with \\\"quotation\\\"\"";
         assert_se(extract_first_word(&p, &t, NULL, EXTRACT_UNQUOTE) == 1);
-        assert_se(streq(t, "KEY=val"));
+        ASSERT_STREQ(t, "KEY=val");
         free(t);
         assert_se(extract_first_word(&p, &t, NULL, EXTRACT_UNQUOTE) == 1);
-        assert_se(streq(t, "KEY2=val with space"));
+        ASSERT_STREQ(t, "KEY2=val with space");
         free(t);
         assert_se(extract_first_word(&p, &t, NULL, EXTRACT_UNQUOTE) == 1);
-        assert_se(streq(t, "KEY3=val with \"quotation\""));
+        ASSERT_STREQ(t, "KEY3=val with \"quotation\"");
         free(t);
         assert_se(isempty(p));
 
         p = original = "KEY=val \"KEY2=val space\" \"KEY3=val with \\\"quotation\\\"\"";
         assert_se(extract_first_word(&p, &t, NULL, EXTRACT_RETAIN_ESCAPE) == 1);
-        assert_se(streq(t, "KEY=val"));
+        ASSERT_STREQ(t, "KEY=val");
         free(t);
         assert_se(extract_first_word(&p, &t, NULL, EXTRACT_RETAIN_ESCAPE) == 1);
-        assert_se(streq(t, "\"KEY2=val"));
+        ASSERT_STREQ(t, "\"KEY2=val");
         free(t);
         assert_se(extract_first_word(&p, &t, NULL, EXTRACT_RETAIN_ESCAPE) == 1);
-        assert_se(streq(t, "space\""));
+        ASSERT_STREQ(t, "space\"");
         free(t);
         assert_se(startswith(p, "\"KEY3="));
 
@@ -113,78 +113,78 @@ TEST(extract_first_word) {
 
         p = original = "\'fooo";
         assert_se(extract_first_word(&p, &t, NULL, EXTRACT_UNQUOTE|EXTRACT_RELAX) > 0);
-        assert_se(streq(t, "fooo"));
+        ASSERT_STREQ(t, "fooo");
         free(t);
         assert_se(isempty(p));
 
         p = original = "\"fooo";
         assert_se(extract_first_word(&p, &t, NULL, EXTRACT_UNQUOTE|EXTRACT_RELAX) > 0);
-        assert_se(streq(t, "fooo"));
+        ASSERT_STREQ(t, "fooo");
         free(t);
         assert_se(isempty(p));
 
         p = original = "yay\'foo\'bar";
         assert_se(extract_first_word(&p, &t, NULL, 0) > 0);
-        assert_se(streq(t, "yay\'foo\'bar"));
+        ASSERT_STREQ(t, "yay\'foo\'bar");
         free(t);
         assert_se(isempty(p));
 
         p = original = "yay\'foo\'bar";
         assert_se(extract_first_word(&p, &t, NULL, EXTRACT_UNQUOTE) > 0);
-        assert_se(streq(t, "yayfoobar"));
+        ASSERT_STREQ(t, "yayfoobar");
         free(t);
         assert_se(isempty(p));
 
         p = original = "   foobar   ";
         assert_se(extract_first_word(&p, &t, NULL, 0) > 0);
-        assert_se(streq(t, "foobar"));
+        ASSERT_STREQ(t, "foobar");
         free(t);
         assert_se(isempty(p));
 
         p = original = " foo\\ba\\x6ar ";
         assert_se(extract_first_word(&p, &t, NULL, EXTRACT_CUNESCAPE) > 0);
-        assert_se(streq(t, "foo\ba\x6ar"));
+        ASSERT_STREQ(t, "foo\ba\x6ar");
         free(t);
         assert_se(isempty(p));
 
         p = original = " foo\\ba\\x6ar ";
         assert_se(extract_first_word(&p, &t, NULL, 0) > 0);
-        assert_se(streq(t, "foobax6ar"));
+        ASSERT_STREQ(t, "foobax6ar");
         free(t);
         assert_se(isempty(p));
 
         p = original = "    f\\u00f6o \"pi\\U0001F4A9le\"   ";
         assert_se(extract_first_word(&p, &t, NULL, EXTRACT_CUNESCAPE) > 0);
-        assert_se(streq(t, "föo"));
+        ASSERT_STREQ(t, "föo");
         free(t);
         assert_se(p == original + 13);
 
         assert_se(extract_first_word(&p, &t, NULL, EXTRACT_UNQUOTE|EXTRACT_CUNESCAPE) > 0);
-        assert_se(streq(t, "pi\360\237\222\251le"));
+        ASSERT_STREQ(t, "pi\360\237\222\251le");
         free(t);
         assert_se(isempty(p));
 
         p = original = "fooo\\";
         assert_se(extract_first_word(&p, &t, NULL, EXTRACT_RELAX) > 0);
-        assert_se(streq(t, "fooo"));
+        ASSERT_STREQ(t, "fooo");
         free(t);
         assert_se(isempty(p));
 
         p = original = "fooo\\";
         assert_se(extract_first_word(&p, &t, NULL, EXTRACT_UNESCAPE_RELAX) > 0);
-        assert_se(streq(t, "fooo\\"));
+        ASSERT_STREQ(t, "fooo\\");
         free(t);
         assert_se(isempty(p));
 
         p = original = "fooo\\";
         assert_se(extract_first_word(&p, &t, NULL, EXTRACT_UNESCAPE_RELAX|EXTRACT_RELAX) > 0);
-        assert_se(streq(t, "fooo\\"));
+        ASSERT_STREQ(t, "fooo\\");
         free(t);
         assert_se(isempty(p));
 
         p = original = "fooo\\";
         assert_se(extract_first_word(&p, &t, NULL, EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_RELAX) > 0);
-        assert_se(streq(t, "fooo\\"));
+        ASSERT_STREQ(t, "fooo\\");
         free(t);
         assert_se(isempty(p));
 
@@ -194,18 +194,18 @@ TEST(extract_first_word) {
 
         p = original = "\"foo\\";
         assert_se(extract_first_word(&p, &t, NULL, EXTRACT_UNQUOTE|EXTRACT_RELAX) > 0);
-        assert_se(streq(t, "foo"));
+        ASSERT_STREQ(t, "foo");
         free(t);
         assert_se(isempty(p));
 
         p = original = "foo::bar";
         assert_se(extract_first_word(&p, &t, ":", 0) == 1);
-        assert_se(streq(t, "foo"));
+        ASSERT_STREQ(t, "foo");
         free(t);
         assert_se(p == original + 5);
 
         assert_se(extract_first_word(&p, &t, ":", 0) == 1);
-        assert_se(streq(t, "bar"));
+        ASSERT_STREQ(t, "bar");
         free(t);
         assert_se(isempty(p));
 
@@ -215,12 +215,12 @@ TEST(extract_first_word) {
 
         p = original = "foo\\:bar::waldo";
         assert_se(extract_first_word(&p, &t, ":", 0) == 1);
-        assert_se(streq(t, "foo:bar"));
+        ASSERT_STREQ(t, "foo:bar");
         free(t);
         assert_se(p == original + 10);
 
         assert_se(extract_first_word(&p, &t, ":", 0) == 1);
-        assert_se(streq(t, "waldo"));
+        ASSERT_STREQ(t, "waldo");
         free(t);
         assert_se(isempty(p));
 
@@ -234,31 +234,31 @@ TEST(extract_first_word) {
 
         p = original = "\"foo\\";
         assert_se(extract_first_word(&p, &t, NULL, EXTRACT_UNQUOTE|EXTRACT_UNESCAPE_RELAX|EXTRACT_RELAX) > 0);
-        assert_se(streq(t, "foo\\"));
+        ASSERT_STREQ(t, "foo\\");
         free(t);
         assert_se(isempty(p));
 
         p = original = "\"foo\\";
         assert_se(extract_first_word(&p, &t, NULL, EXTRACT_UNQUOTE|EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_RELAX|EXTRACT_RELAX) > 0);
-        assert_se(streq(t, "foo\\"));
+        ASSERT_STREQ(t, "foo\\");
         free(t);
         assert_se(isempty(p));
 
         p = original = "fooo\\ bar quux";
         assert_se(extract_first_word(&p, &t, NULL, EXTRACT_RELAX) > 0);
-        assert_se(streq(t, "fooo bar"));
+        ASSERT_STREQ(t, "fooo bar");
         free(t);
         assert_se(p == original + 10);
 
         p = original = "fooo\\ bar quux";
         assert_se(extract_first_word(&p, &t, NULL, EXTRACT_UNESCAPE_RELAX) > 0);
-        assert_se(streq(t, "fooo bar"));
+        ASSERT_STREQ(t, "fooo bar");
         free(t);
         assert_se(p == original + 10);
 
         p = original = "fooo\\ bar quux";
         assert_se(extract_first_word(&p, &t, NULL, EXTRACT_UNESCAPE_RELAX|EXTRACT_RELAX) > 0);
-        assert_se(streq(t, "fooo bar"));
+        ASSERT_STREQ(t, "fooo bar");
         free(t);
         assert_se(p == original + 10);
 
@@ -268,7 +268,7 @@ TEST(extract_first_word) {
 
         p = original = "fooo\\ bar quux";
         assert_se(extract_first_word(&p, &t, NULL, EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_RELAX) > 0);
-        assert_se(streq(t, "fooo\\ bar"));
+        ASSERT_STREQ(t, "fooo\\ bar");
         free(t);
         assert_se(p == original + 10);
 
@@ -278,54 +278,54 @@ TEST(extract_first_word) {
 
         p = original = "\\w+@\\K[\\d.]+";
         assert_se(extract_first_word(&p, &t, NULL, EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_RELAX) > 0);
-        assert_se(streq(t, "\\w+@\\K[\\d.]+"));
+        ASSERT_STREQ(t, "\\w+@\\K[\\d.]+");
         free(t);
         assert_se(isempty(p));
 
         p = original = "\\w+\\b";
         assert_se(extract_first_word(&p, &t, NULL, EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_RELAX) > 0);
-        assert_se(streq(t, "\\w+\b"));
+        ASSERT_STREQ(t, "\\w+\b");
         free(t);
         assert_se(isempty(p));
 
         p = original = "-N ''";
         assert_se(extract_first_word(&p, &t, NULL, EXTRACT_UNQUOTE) > 0);
-        assert_se(streq(t, "-N"));
+        ASSERT_STREQ(t, "-N");
         free(t);
         assert_se(p == original + 3);
 
         assert_se(extract_first_word(&p, &t, NULL, EXTRACT_UNQUOTE) > 0);
-        assert_se(streq(t, ""));
+        ASSERT_STREQ(t, "");
         free(t);
         assert_se(isempty(p));
 
         p = original = ":foo\\:bar::waldo:";
         assert_se(extract_first_word(&p, &t, ":", EXTRACT_DONT_COALESCE_SEPARATORS) == 1);
         assert_se(t);
-        assert_se(streq(t, ""));
+        ASSERT_STREQ(t, "");
         free(t);
         assert_se(p == original + 1);
 
         assert_se(extract_first_word(&p, &t, ":", EXTRACT_DONT_COALESCE_SEPARATORS) == 1);
-        assert_se(streq(t, "foo:bar"));
+        ASSERT_STREQ(t, "foo:bar");
         free(t);
         assert_se(p == original + 10);
 
         assert_se(extract_first_word(&p, &t, ":", EXTRACT_DONT_COALESCE_SEPARATORS) == 1);
         assert_se(t);
-        assert_se(streq(t, ""));
+        ASSERT_STREQ(t, "");
         free(t);
         assert_se(p == original + 11);
 
         assert_se(extract_first_word(&p, &t, ":", EXTRACT_DONT_COALESCE_SEPARATORS) == 1);
-        assert_se(streq(t, "waldo"));
+        ASSERT_STREQ(t, "waldo");
         free(t);
         assert_se(p == original + 17);
 
         assert_se(extract_first_word(&p, &t, ":", EXTRACT_DONT_COALESCE_SEPARATORS) == 1);
-        assert_se(streq(t, ""));
+        ASSERT_STREQ(t, "");
         free(t);
-        assert_se(p == NULL);
+        ASSERT_NULL(p);
 
         assert_se(extract_first_word(&p, &t, ":", EXTRACT_DONT_COALESCE_SEPARATORS) == 0);
         assert_se(!t);
@@ -333,81 +333,81 @@ TEST(extract_first_word) {
 
         p = "foo\\xbar";
         assert_se(extract_first_word(&p, &t, NULL, 0) > 0);
-        assert_se(streq(t, "fooxbar"));
+        ASSERT_STREQ(t, "fooxbar");
         free(t);
-        assert_se(p == NULL);
+        ASSERT_NULL(p);
 
         p = "foo\\xbar";
         assert_se(extract_first_word(&p, &t, NULL, EXTRACT_RETAIN_ESCAPE) > 0);
-        assert_se(streq(t, "foo\\xbar"));
+        ASSERT_STREQ(t, "foo\\xbar");
         free(t);
-        assert_se(p == NULL);
+        ASSERT_NULL(p);
 
         p = "\\:";
         assert_se(extract_first_word(&p, &t, ":", EXTRACT_UNESCAPE_SEPARATORS) == 1);
-        assert_se(streq(t, ":"));
+        ASSERT_STREQ(t, ":");
         free(t);
-        assert_se(p == NULL);
+        ASSERT_NULL(p);
 
         p = "a\\:b";
         assert_se(extract_first_word(&p, &t, ":", EXTRACT_UNESCAPE_SEPARATORS) == 1);
-        assert_se(streq(t, "a:b"));
+        ASSERT_STREQ(t, "a:b");
         free(t);
-        assert_se(p == NULL);
+        ASSERT_NULL(p);
 
         p = "a\\ b:c";
         assert_se(extract_first_word(&p, &t, WHITESPACE ":", EXTRACT_UNESCAPE_SEPARATORS) == 1);
-        assert_se(streq(t, "a b"));
+        ASSERT_STREQ(t, "a b");
         free(t);
         assert_se(extract_first_word(&p, &t, WHITESPACE ":", EXTRACT_UNESCAPE_SEPARATORS) == 1);
-        assert_se(streq(t, "c"));
+        ASSERT_STREQ(t, "c");
         free(t);
-        assert_se(p == NULL);
+        ASSERT_NULL(p);
 
         p = "a\\ b:c\\x";
         assert_se(extract_first_word(&p, &t, ":", EXTRACT_UNESCAPE_SEPARATORS) == -EINVAL);
 
         p = "a\\\\ b:c\\\\x";
         assert_se(extract_first_word(&p, &t, ":", EXTRACT_UNESCAPE_SEPARATORS) == 1);
-        assert_se(streq(t, "a\\ b"));
+        ASSERT_STREQ(t, "a\\ b");
         free(t);
         assert_se(extract_first_word(&p, &t, ":", EXTRACT_UNESCAPE_SEPARATORS) == 1);
-        assert_se(streq(t, "c\\x"));
+        ASSERT_STREQ(t, "c\\x");
         free(t);
-        assert_se(p == NULL);
+        ASSERT_NULL(p);
 
         p = "\\:";
         assert_se(extract_first_word(&p, &t, ":", EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_SEPARATORS) == 1);
-        assert_se(streq(t, ":"));
+        ASSERT_STREQ(t, ":");
         free(t);
-        assert_se(p == NULL);
+        ASSERT_NULL(p);
 
         p = "a\\:b";
         assert_se(extract_first_word(&p, &t, ":", EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_SEPARATORS) == 1);
-        assert_se(streq(t, "a:b"));
+        ASSERT_STREQ(t, "a:b");
         free(t);
-        assert_se(p == NULL);
+        ASSERT_NULL(p);
 
         p = "a\\ b:c";
         assert_se(extract_first_word(&p, &t, WHITESPACE ":", EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_SEPARATORS) == 1);
-        assert_se(streq(t, "a b"));
+        ASSERT_STREQ(t, "a b");
         free(t);
         assert_se(extract_first_word(&p, &t, WHITESPACE ":", EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_SEPARATORS) == 1);
-        assert_se(streq(t, "c"));
+        ASSERT_STREQ(t, "c");
         free(t);
-        assert_se(p == NULL);
+        ASSERT_NULL(p);
 
         p = "a\\ b:c\\x";
         assert_se(extract_first_word(&p, &t, ":", EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_SEPARATORS) == -EINVAL);
 
         p = "a\\\\ b:c\\\\x";
         assert_se(extract_first_word(&p, &t, ":", EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_SEPARATORS) == 1);
-        assert_se(streq(t, "a\\ b"));
+        ASSERT_STREQ(t, "a\\ b");
         free(t);
         assert_se(extract_first_word(&p, &t, ":", EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_SEPARATORS) == 1);
-        assert_se(streq(t, "c\\x"));
+        ASSERT_STREQ(t, "c\\x");
         free(t);
-        assert_se(p == NULL);
+        ASSERT_NULL(p);
 
         p = "\\:";
         assert_se(extract_first_word(&p, &t, ":", EXTRACT_CUNESCAPE) == -EINVAL);
@@ -415,136 +415,136 @@ TEST(extract_first_word) {
         p = "a\\:b";
         assert_se(extract_first_word(&p, &t, ":", EXTRACT_CUNESCAPE) == -EINVAL);
         assert_se(extract_first_word(&p, &t, ":", EXTRACT_CUNESCAPE) == 1);
-        assert_se(streq(t, "b"));
+        ASSERT_STREQ(t, "b");
         free(t);
 
         p = "a\\ b:c";
         assert_se(extract_first_word(&p, &t, WHITESPACE ":", EXTRACT_CUNESCAPE) == -EINVAL);
         assert_se(extract_first_word(&p, &t, WHITESPACE ":", EXTRACT_CUNESCAPE) == 1);
-        assert_se(streq(t, "b"));
+        ASSERT_STREQ(t, "b");
         free(t);
         assert_se(extract_first_word(&p, &t, WHITESPACE ":", EXTRACT_CUNESCAPE) == 1);
-        assert_se(streq(t, "c"));
+        ASSERT_STREQ(t, "c");
         free(t);
-        assert_se(p == NULL);
+        ASSERT_NULL(p);
 
         p = original = "foobar=\"waldo\"maldo, baldo";
         assert_se(extract_first_word(&p, &t, "=\", ", 0) > 0);
-        assert_se(streq(t, "foobar"));
+        ASSERT_STREQ(t, "foobar");
         free(t);
         assert_se(extract_first_word(&p, &t, "=\", ", 0) > 0);
-        assert_se(streq(t, "waldo"));
+        ASSERT_STREQ(t, "waldo");
         free(t);
         assert_se(extract_first_word(&p, &t, "=\", ", 0) > 0);
-        assert_se(streq(t, "maldo"));
+        ASSERT_STREQ(t, "maldo");
         free(t);
         assert_se(extract_first_word(&p, &t, "=\", ", 0) > 0);
-        assert_se(streq(t, "baldo"));
+        ASSERT_STREQ(t, "baldo");
         free(t);
 
         p = original = "mode=\"1777\",size=\"10%\",nr_inodes=\"400\"k,uid=\"496,,107\"520,gi\"\"'d=49610,'\"\"7520,context=\"system_u:object_r:svirt_sandbox_file_t:s0:c0,c1\"";
         assert_se(extract_first_word(&p, &t, ",", EXTRACT_KEEP_QUOTE) > 0);
-        assert_se(streq(t, "mode=\"1777\""));
+        ASSERT_STREQ(t, "mode=\"1777\"");
         free(t);
         assert_se(extract_first_word(&p, &t, ",", EXTRACT_KEEP_QUOTE) > 0);
-        assert_se(streq(t, "size=\"10%\""));
+        ASSERT_STREQ(t, "size=\"10%\"");
         free(t);
         assert_se(extract_first_word(&p, &t, ",", EXTRACT_KEEP_QUOTE) > 0);
-        assert_se(streq(t, "nr_inodes=\"400\"k"));
+        ASSERT_STREQ(t, "nr_inodes=\"400\"k");
         free(t);
         assert_se(extract_first_word(&p, &t, ",", EXTRACT_KEEP_QUOTE) > 0);
-        assert_se(streq(t, "uid=\"496,,107\"520"));
+        ASSERT_STREQ(t, "uid=\"496,,107\"520");
         free(t);
         assert_se(extract_first_word(&p, &t, ",", EXTRACT_KEEP_QUOTE) > 0);
-        assert_se(streq(t, "gi\"\"'d=49610,'\"\"7520"));
+        ASSERT_STREQ(t, "gi\"\"'d=49610,'\"\"7520");
         free(t);
         assert_se(extract_first_word(&p, &t, ",", EXTRACT_KEEP_QUOTE) > 0);
-        assert_se(streq(t, "context=\"system_u:object_r:svirt_sandbox_file_t:s0:c0,c1\""));
+        ASSERT_STREQ(t, "context=\"system_u:object_r:svirt_sandbox_file_t:s0:c0,c1\"");
         free(t);
 
         p = original = "mode=\"1777\",size=\"10%\",nr_inodes=\"400\"k,uid=\"496,,107\"520,gi\"\"'d=49610,'\"\"7520,context=\"system_u:object_r:svirt_sandbox_file_t:s0:c0,c1\"";
         assert_se(extract_first_word(&p, &t, ",", EXTRACT_UNQUOTE) > 0);
-        assert_se(streq(t, "mode=1777"));
+        ASSERT_STREQ(t, "mode=1777");
         free(t);
         assert_se(extract_first_word(&p, &t, ",", EXTRACT_UNQUOTE) > 0);
-        assert_se(streq(t, "size=10%"));
+        ASSERT_STREQ(t, "size=10%");
         free(t);
         assert_se(extract_first_word(&p, &t, ",", EXTRACT_UNQUOTE) > 0);
-        assert_se(streq(t, "nr_inodes=400k"));
+        ASSERT_STREQ(t, "nr_inodes=400k");
         free(t);
         assert_se(extract_first_word(&p, &t, ",", EXTRACT_UNQUOTE) > 0);
-        assert_se(streq(t, "uid=496,,107520"));
+        ASSERT_STREQ(t, "uid=496,,107520");
         free(t);
         assert_se(extract_first_word(&p, &t, ",", EXTRACT_UNQUOTE) > 0);
-        assert_se(streq(t, "gid=49610,7520"));
+        ASSERT_STREQ(t, "gid=49610,7520");
         free(t);
         assert_se(extract_first_word(&p, &t, ",", EXTRACT_UNQUOTE) > 0);
-        assert_se(streq(t, "context=system_u:object_r:svirt_sandbox_file_t:s0:c0,c1"));
+        ASSERT_STREQ(t, "context=system_u:object_r:svirt_sandbox_file_t:s0:c0,c1");
         free(t);
 
         p = "a:b";
         assert_se(extract_first_word(&p, &t, ":", EXTRACT_RETAIN_SEPARATORS) == 1);
-        assert_se(streq(t, "a"));
-        assert_se(streq(p, ":b"));
+        ASSERT_STREQ(t, "a");
+        ASSERT_STREQ(p, ":b");
         free(t);
         assert_se(extract_first_word(&p, &t, ":", EXTRACT_RETAIN_SEPARATORS) == 1);
-        assert_se(streq(t, "b"));
+        ASSERT_STREQ(t, "b");
         free(t);
 
         p = "a>:b";
         assert_se(extract_first_word(&p, &t, ">:", EXTRACT_RETAIN_SEPARATORS) == 1);
-        assert_se(streq(t, "a"));
-        assert_se(streq(p, ">:b"));
+        ASSERT_STREQ(t, "a");
+        ASSERT_STREQ(p, ">:b");
         free(t);
         assert_se(extract_first_word(&p, &t, ">:", EXTRACT_RETAIN_SEPARATORS) == 1);
-        assert_se(streq(t, "b"));
+        ASSERT_STREQ(t, "b");
         free(t);
 
         p = "a>:b";
         assert_se(extract_first_word(&p, &t, ">:", EXTRACT_RETAIN_SEPARATORS|EXTRACT_DONT_COALESCE_SEPARATORS) == 1);
-        assert_se(streq(t, "a"));
-        assert_se(streq(p, ">:b"));
+        ASSERT_STREQ(t, "a");
+        ASSERT_STREQ(p, ">:b");
         free(t);
         assert_se(extract_first_word(&p, &t, ">:", EXTRACT_RETAIN_SEPARATORS|EXTRACT_DONT_COALESCE_SEPARATORS) == 1);
-        assert_se(streq(t, ""));
-        assert_se(streq(p, ">:b"));
+        ASSERT_STREQ(t, "");
+        ASSERT_STREQ(p, ">:b");
         free(t);
 
         p = "a\\:b";
         assert_se(extract_first_word(&p, &t, ":", EXTRACT_RETAIN_SEPARATORS|EXTRACT_RETAIN_ESCAPE) == 1);
-        assert_se(streq(t, "a\\"));
-        assert_se(streq(p, ":b"));
+        ASSERT_STREQ(t, "a\\");
+        ASSERT_STREQ(p, ":b");
         free(t);
 
         p = "a\\:b";
         assert_se(extract_first_word(&p, &t, ":", EXTRACT_RETAIN_SEPARATORS) == 1);
-        assert_se(streq(t, "a:b"));
+        ASSERT_STREQ(t, "a:b");
         assert_se(!p);
         free(t);
 
         p = "a\\:b";
         assert_se(extract_first_word(&p, &t, ":", EXTRACT_RETAIN_SEPARATORS|EXTRACT_UNESCAPE_SEPARATORS) == 1);
-        assert_se(streq(t, "a:b"));
+        ASSERT_STREQ(t, "a:b");
         assert_se(!p);
         free(t);
 
         p = "a\\:a:b";
         assert_se(extract_first_word(&p, &t, ":", EXTRACT_RETAIN_SEPARATORS|EXTRACT_UNESCAPE_SEPARATORS) == 1);
-        assert_se(streq(t, "a:a"));
-        assert_se(streq(p, ":b"));
+        ASSERT_STREQ(t, "a:a");
+        ASSERT_STREQ(p, ":b");
         free(t);
 
         p = original = "zażółcić 👊🔪💐 가너도루";
         assert_se(extract_first_word(&p, &t, NULL, 0) > 0);
-        assert_se(streq(t, "zażółcić"));
+        ASSERT_STREQ(t, "zażółcić");
         free(t);
         assert_se(p == original + 13);
 
         assert_se(extract_first_word(&p, &t, NULL, 0) > 0);
-        assert_se(streq(t, "👊🔪💐"));
+        ASSERT_STREQ(t, "👊🔪💐");
         free(t);
         assert_se(extract_first_word(&p, &t, NULL, 0) > 0);
-        assert_se(streq(t, "가너도루"));
+        ASSERT_STREQ(t, "가너도루");
         free(t);
         assert_se(isempty(p));
 }
@@ -555,12 +555,12 @@ TEST(extract_first_word_and_warn) {
 
         p = original = "foobar waldo";
         assert_se(extract_first_word_and_warn(&p, &t, NULL, 0, NULL, "fake", 1, original) > 0);
-        assert_se(streq(t, "foobar"));
+        ASSERT_STREQ(t, "foobar");
         free(t);
         assert_se(p == original + 7);
 
         assert_se(extract_first_word_and_warn(&p, &t, NULL, 0, NULL, "fake", 1, original) > 0);
-        assert_se(streq(t, "waldo"));
+        ASSERT_STREQ(t, "waldo");
         free(t);
         assert_se(isempty(p));
 
@@ -570,12 +570,12 @@ TEST(extract_first_word_and_warn) {
 
         p = original = "\"foobar\" \'waldo\'";
         assert_se(extract_first_word_and_warn(&p, &t, NULL, EXTRACT_UNQUOTE, NULL, "fake", 1, original) > 0);
-        assert_se(streq(t, "foobar"));
+        ASSERT_STREQ(t, "foobar");
         free(t);
         assert_se(p == original + 9);
 
         assert_se(extract_first_word_and_warn(&p, &t, NULL, EXTRACT_UNQUOTE, NULL, "fake", 1, original) > 0);
-        assert_se(streq(t, "waldo"));
+        ASSERT_STREQ(t, "waldo");
         free(t);
         assert_se(isempty(p));
 
@@ -597,48 +597,48 @@ TEST(extract_first_word_and_warn) {
 
         p = original = "\'fooo";
         assert_se(extract_first_word_and_warn(&p, &t, NULL, EXTRACT_UNQUOTE|EXTRACT_RELAX, NULL, "fake", 1, original) > 0);
-        assert_se(streq(t, "fooo"));
+        ASSERT_STREQ(t, "fooo");
         free(t);
         assert_se(isempty(p));
 
         p = original = " foo\\ba\\x6ar ";
         assert_se(extract_first_word_and_warn(&p, &t, NULL, EXTRACT_CUNESCAPE, NULL, "fake", 1, original) > 0);
-        assert_se(streq(t, "foo\ba\x6ar"));
+        ASSERT_STREQ(t, "foo\ba\x6ar");
         free(t);
         assert_se(isempty(p));
 
         p = original = " foo\\ba\\x6ar ";
         assert_se(extract_first_word_and_warn(&p, &t, NULL, 0, NULL, "fake", 1, original) > 0);
-        assert_se(streq(t, "foobax6ar"));
+        ASSERT_STREQ(t, "foobax6ar");
         free(t);
         assert_se(isempty(p));
 
         p = original = "    f\\u00f6o \"pi\\U0001F4A9le\"   ";
         assert_se(extract_first_word_and_warn(&p, &t, NULL, EXTRACT_CUNESCAPE, NULL, "fake", 1, original) > 0);
-        assert_se(streq(t, "föo"));
+        ASSERT_STREQ(t, "föo");
         free(t);
         assert_se(p == original + 13);
 
         assert_se(extract_first_word_and_warn(&p, &t, NULL, EXTRACT_UNQUOTE|EXTRACT_CUNESCAPE, NULL, "fake", 1, original) > 0);
-        assert_se(streq(t, "pi\360\237\222\251le"));
+        ASSERT_STREQ(t, "pi\360\237\222\251le");
         free(t);
         assert_se(isempty(p));
 
         p = original = "fooo\\";
         assert_se(extract_first_word_and_warn(&p, &t, NULL, EXTRACT_RELAX, NULL, "fake", 1, original) > 0);
-        assert_se(streq(t, "fooo"));
+        ASSERT_STREQ(t, "fooo");
         free(t);
         assert_se(isempty(p));
 
         p = original = "fooo\\";
         assert_se(extract_first_word_and_warn(&p, &t, NULL, 0, NULL, "fake", 1, original) > 0);
-        assert_se(streq(t, "fooo\\"));
+        ASSERT_STREQ(t, "fooo\\");
         free(t);
         assert_se(isempty(p));
 
         p = original = "fooo\\";
         assert_se(extract_first_word_and_warn(&p, &t, NULL, EXTRACT_CUNESCAPE, NULL, "fake", 1, original) > 0);
-        assert_se(streq(t, "fooo\\"));
+        ASSERT_STREQ(t, "fooo\\");
         free(t);
         assert_se(isempty(p));
 
@@ -648,7 +648,7 @@ TEST(extract_first_word_and_warn) {
 
         p = original = "\"foo\\";
         assert_se(extract_first_word_and_warn(&p, &t, NULL, EXTRACT_UNQUOTE|EXTRACT_RELAX, NULL, "fake", 1, original) > 0);
-        assert_se(streq(t, "foo"));
+        ASSERT_STREQ(t, "foo");
         free(t);
         assert_se(isempty(p));
 
@@ -658,37 +658,37 @@ TEST(extract_first_word_and_warn) {
 
         p = original = "\"foo\\";
         assert_se(extract_first_word_and_warn(&p, &t, NULL, EXTRACT_UNQUOTE|EXTRACT_CUNESCAPE|EXTRACT_RELAX, NULL, "fake", 1, original) > 0);
-        assert_se(streq(t, "foo"));
+        ASSERT_STREQ(t, "foo");
         free(t);
         assert_se(isempty(p));
 
         p = original = "fooo\\ bar quux";
         assert_se(extract_first_word_and_warn(&p, &t, NULL, EXTRACT_RELAX, NULL, "fake", 1, original) > 0);
-        assert_se(streq(t, "fooo bar"));
+        ASSERT_STREQ(t, "fooo bar");
         free(t);
         assert_se(p == original + 10);
 
         p = original = "fooo\\ bar quux";
         assert_se(extract_first_word_and_warn(&p, &t, NULL, 0, NULL, "fake", 1, original) > 0);
-        assert_se(streq(t, "fooo bar"));
+        ASSERT_STREQ(t, "fooo bar");
         free(t);
         assert_se(p == original + 10);
 
         p = original = "fooo\\ bar quux";
         assert_se(extract_first_word_and_warn(&p, &t, NULL, EXTRACT_CUNESCAPE, NULL, "fake", 1, original) > 0);
-        assert_se(streq(t, "fooo\\ bar"));
+        ASSERT_STREQ(t, "fooo\\ bar");
         free(t);
         assert_se(p == original + 10);
 
         p = original = "\\w+@\\K[\\d.]+";
         assert_se(extract_first_word_and_warn(&p, &t, NULL, EXTRACT_CUNESCAPE, NULL, "fake", 1, original) > 0);
-        assert_se(streq(t, "\\w+@\\K[\\d.]+"));
+        ASSERT_STREQ(t, "\\w+@\\K[\\d.]+");
         free(t);
         assert_se(isempty(p));
 
         p = original = "\\w+\\b";
         assert_se(extract_first_word_and_warn(&p, &t, NULL, EXTRACT_CUNESCAPE, NULL, "fake", 1, original) > 0);
-        assert_se(streq(t, "\\w+\b"));
+        ASSERT_STREQ(t, "\\w+\b");
         free(t);
         assert_se(isempty(p));
 }
@@ -700,9 +700,9 @@ TEST(extract_many_words) {
         p = original = "foobar waldi piep";
         assert_se(extract_many_words(&p, NULL, 0, &a, &b, &c) == 3);
         assert_se(isempty(p));
-        assert_se(streq_ptr(a, "foobar"));
-        assert_se(streq_ptr(b, "waldi"));
-        assert_se(streq_ptr(c, "piep"));
+        ASSERT_STREQ(a, "foobar");
+        ASSERT_STREQ(b, "waldi");
+        ASSERT_STREQ(c, "piep");
         free(a);
         free(b);
         free(c);
@@ -710,13 +710,13 @@ TEST(extract_many_words) {
         p = original = "foobar:waldi:piep ba1:ba2";
         assert_se(extract_many_words(&p, ":" WHITESPACE, 0, &a, &b, &c) == 3);
         assert_se(!isempty(p));
-        assert_se(streq_ptr(a, "foobar"));
-        assert_se(streq_ptr(b, "waldi"));
-        assert_se(streq_ptr(c, "piep"));
+        ASSERT_STREQ(a, "foobar");
+        ASSERT_STREQ(b, "waldi");
+        ASSERT_STREQ(c, "piep");
         assert_se(extract_many_words(&p, ":" WHITESPACE, 0, &d, &e, &f) == 2);
         assert_se(isempty(p));
-        assert_se(streq_ptr(d, "ba1"));
-        assert_se(streq_ptr(e, "ba2"));
+        ASSERT_STREQ(d, "ba1");
+        ASSERT_STREQ(e, "ba2");
         assert_se(isempty(f));
         free(a);
         free(b);
@@ -728,34 +728,34 @@ TEST(extract_many_words) {
         p = original = "'foobar' wa\"ld\"i   ";
         assert_se(extract_many_words(&p, NULL, 0, &a, &b, &c) == 2);
         assert_se(isempty(p));
-        assert_se(streq_ptr(a, "'foobar'"));
-        assert_se(streq_ptr(b, "wa\"ld\"i"));
-        assert_se(streq_ptr(c, NULL));
+        ASSERT_STREQ(a, "'foobar'");
+        ASSERT_STREQ(b, "wa\"ld\"i");
+        ASSERT_STREQ(c, NULL);
         free(a);
         free(b);
 
         p = original = "'foobar' wa\"ld\"i   ";
         assert_se(extract_many_words(&p, NULL, EXTRACT_UNQUOTE, &a, &b, &c) == 2);
         assert_se(isempty(p));
-        assert_se(streq_ptr(a, "foobar"));
-        assert_se(streq_ptr(b, "waldi"));
-        assert_se(streq_ptr(c, NULL));
+        ASSERT_STREQ(a, "foobar");
+        ASSERT_STREQ(b, "waldi");
+        ASSERT_STREQ(c, NULL);
         free(a);
         free(b);
 
         p = original = "";
         assert_se(extract_many_words(&p, NULL, 0, &a, &b, &c) == 0);
         assert_se(isempty(p));
-        assert_se(streq_ptr(a, NULL));
-        assert_se(streq_ptr(b, NULL));
-        assert_se(streq_ptr(c, NULL));
+        ASSERT_STREQ(a, NULL);
+        ASSERT_STREQ(b, NULL);
+        ASSERT_STREQ(c, NULL);
 
         p = original = "  ";
         assert_se(extract_many_words(&p, NULL, 0, &a, &b, &c) == 0);
         assert_se(isempty(p));
-        assert_se(streq_ptr(a, NULL));
-        assert_se(streq_ptr(b, NULL));
-        assert_se(streq_ptr(c, NULL));
+        ASSERT_STREQ(a, NULL);
+        ASSERT_STREQ(b, NULL);
+        ASSERT_STREQ(c, NULL);
 
         p = original = "foobar";
         assert_se(extract_many_words(&p, NULL, 0) == 0);
@@ -764,21 +764,21 @@ TEST(extract_many_words) {
         p = original = "foobar waldi";
         assert_se(extract_many_words(&p, NULL, 0, &a) == 1);
         assert_se(p == original+7);
-        assert_se(streq_ptr(a, "foobar"));
+        ASSERT_STREQ(a, "foobar");
         free(a);
 
         p = original = "     foobar    ";
         assert_se(extract_many_words(&p, NULL, 0, &a) == 1);
         assert_se(isempty(p));
-        assert_se(streq_ptr(a, "foobar"));
+        ASSERT_STREQ(a, "foobar");
         free(a);
 
         p = original = "gęślą:👊🔪💐 가너도루";
         assert_se(extract_many_words(&p, ":" WHITESPACE, 0, &a, &b, &c) == 3);
         assert_se(isempty(p));
-        assert_se(streq(a, "gęślą"));
-        assert_se(streq(b, "👊🔪💐"));
-        assert_se(streq(c, "가너도루"));
+        ASSERT_STREQ(a, "gęślą");
+        ASSERT_STREQ(b, "👊🔪💐");
+        ASSERT_STREQ(c, "가너도루");
         free(a);
         free(b);
         free(c);
index cd4ce8359739dc2ac14664dc1b62030935e7eaad..2796fe888238a5dbe1734b5fee5b5ccd21c08d3d 100644 (file)
@@ -138,6 +138,7 @@ TEST(rearrange_stdio) {
 
         if (r == 0) {
                 _cleanup_free_ char *path = NULL;
+                int pipe_read_fd, pair[2];
                 char buffer[10];
 
                 /* Child */
@@ -145,6 +146,10 @@ TEST(rearrange_stdio) {
                 safe_close(STDERR_FILENO); /* Let's close an fd < 2, to make it more interesting */
 
                 assert_se(rearrange_stdio(-EBADF, -EBADF, -EBADF) >= 0);
+                /* Reconfigure logging after rearranging stdout/stderr, so we still log to somewhere if the
+                 * following tests fail, making it slightly less annoying to debug */
+                log_set_target(LOG_TARGET_KMSG);
+                log_open();
 
                 assert_se(fd_get_path(STDIN_FILENO, &path) >= 0);
                 assert_se(path_equal(path, "/dev/null"));
@@ -162,13 +167,12 @@ TEST(rearrange_stdio) {
                 safe_close(STDOUT_FILENO);
                 safe_close(STDERR_FILENO);
 
-                {
-                        int pair[2];
-                        assert_se(pipe(pair) >= 0);
-                        assert_se(pair[0] == 0);
-                        assert_se(pair[1] == 1);
-                        assert_se(fd_move_above_stdio(0) == 3);
-                }
+                assert_se(pipe(pair) >= 0);
+                assert_se(pair[0] == 0);
+                assert_se(pair[1] == 1);
+                pipe_read_fd = fd_move_above_stdio(0);
+                assert_se(pipe_read_fd >= 3);
+
                 assert_se(open("/dev/full", O_WRONLY|O_CLOEXEC) == 0);
                 assert_se(acquire_data_fd("foobar") == 2);
 
@@ -176,7 +180,7 @@ TEST(rearrange_stdio) {
 
                 assert_se(write(1, "x", 1) < 0 && errno == ENOSPC);
                 assert_se(write(2, "z", 1) == 1);
-                assert_se(read(3, buffer, sizeof(buffer)) == 1);
+                assert_se(read(pipe_read_fd, buffer, sizeof(buffer)) == 1);
                 assert_se(buffer[0] == 'z');
                 assert_se(read(0, buffer, sizeof(buffer)) == 6);
                 assert_se(memcmp(buffer, "foobar", 6) == 0);
@@ -184,7 +188,7 @@ TEST(rearrange_stdio) {
                 assert_se(rearrange_stdio(-EBADF, 1, 2) >= 0);
                 assert_se(write(1, "a", 1) < 0 && errno == ENOSPC);
                 assert_se(write(2, "y", 1) == 1);
-                assert_se(read(3, buffer, sizeof(buffer)) == 1);
+                assert_se(read(pipe_read_fd, buffer, sizeof(buffer)) == 1);
                 assert_se(buffer[0] == 'y');
 
                 assert_se(fd_get_path(0, &path) >= 0);
@@ -393,11 +397,11 @@ TEST(close_all_fds) {
 }
 
 TEST(format_proc_fd_path) {
-        assert_se(streq_ptr(FORMAT_PROC_FD_PATH(0), "/proc/self/fd/0"));
-        assert_se(streq_ptr(FORMAT_PROC_FD_PATH(1), "/proc/self/fd/1"));
-        assert_se(streq_ptr(FORMAT_PROC_FD_PATH(2), "/proc/self/fd/2"));
-        assert_se(streq_ptr(FORMAT_PROC_FD_PATH(3), "/proc/self/fd/3"));
-        assert_se(streq_ptr(FORMAT_PROC_FD_PATH(2147483647), "/proc/self/fd/2147483647"));
+        ASSERT_STREQ(FORMAT_PROC_FD_PATH(0), "/proc/self/fd/0");
+        ASSERT_STREQ(FORMAT_PROC_FD_PATH(1), "/proc/self/fd/1");
+        ASSERT_STREQ(FORMAT_PROC_FD_PATH(2), "/proc/self/fd/2");
+        ASSERT_STREQ(FORMAT_PROC_FD_PATH(3), "/proc/self/fd/3");
+        ASSERT_STREQ(FORMAT_PROC_FD_PATH(2147483647), "/proc/self/fd/2147483647");
 }
 
 TEST(fd_reopen) {
@@ -409,7 +413,7 @@ TEST(fd_reopen) {
         fd1 = open("/proc", O_DIRECTORY|O_PATH|O_CLOEXEC);
         assert_se(fd1 >= 0);
 
-        assert_se(fstat(fd1, &st1) >= 0);
+        ASSERT_OK_ERRNO(fstat(fd1, &st1));
         assert_se(S_ISDIR(st1.st_mode));
 
         fl = fcntl(fd1, F_GETFL);
@@ -424,7 +428,7 @@ TEST(fd_reopen) {
         fd2 = fd_reopen(fd1, O_RDONLY|O_DIRECTORY|O_CLOEXEC);  /* drop the O_PATH */
         assert_se(fd2 >= 0);
 
-        assert_se(fstat(fd2, &st2) >= 0);
+        ASSERT_OK_ERRNO(fstat(fd2, &st2));
         assert_se(S_ISDIR(st2.st_mode));
         assert_se(stat_inode_same(&st1, &st2));
 
@@ -438,7 +442,7 @@ TEST(fd_reopen) {
         fd1 = fd_reopen(fd2, O_DIRECTORY|O_PATH|O_CLOEXEC);  /* reacquire the O_PATH */
         assert_se(fd1 >= 0);
 
-        assert_se(fstat(fd1, &st1) >= 0);
+        ASSERT_OK_ERRNO(fstat(fd1, &st1));
         assert_se(S_ISDIR(st1.st_mode));
         assert_se(stat_inode_same(&st1, &st2));
 
@@ -453,7 +457,7 @@ TEST(fd_reopen) {
         fd1 = open("/proc/version", O_PATH|O_CLOEXEC);
         assert_se(fd1 >= 0);
 
-        assert_se(fstat(fd1, &st1) >= 0);
+        ASSERT_OK_ERRNO(fstat(fd1, &st1));
         assert_se(S_ISREG(st1.st_mode));
 
         fl = fcntl(fd1, F_GETFL);
@@ -465,7 +469,7 @@ TEST(fd_reopen) {
         fd2 = fd_reopen(fd1, O_RDONLY|O_CLOEXEC);  /* drop the O_PATH */
         assert_se(fd2 >= 0);
 
-        assert_se(fstat(fd2, &st2) >= 0);
+        ASSERT_OK_ERRNO(fstat(fd2, &st2));
         assert_se(S_ISREG(st2.st_mode));
         assert_se(stat_inode_same(&st1, &st2));
 
@@ -480,7 +484,7 @@ TEST(fd_reopen) {
         fd1 = fd_reopen(fd2, O_PATH|O_CLOEXEC);  /* reacquire the O_PATH */
         assert_se(fd1 >= 0);
 
-        assert_se(fstat(fd1, &st1) >= 0);
+        ASSERT_OK_ERRNO(fstat(fd1, &st1));
         assert_se(S_ISREG(st1.st_mode));
         assert_se(stat_inode_same(&st1, &st2));
 
@@ -497,12 +501,12 @@ TEST(fd_reopen) {
         /* Validate what happens if we reopen a symlink */
         fd1 = open("/proc/self", O_PATH|O_CLOEXEC|O_NOFOLLOW);
         assert_se(fd1 >= 0);
-        assert_se(fstat(fd1, &st1) >= 0);
+        ASSERT_OK_ERRNO(fstat(fd1, &st1));
         assert_se(S_ISLNK(st1.st_mode));
 
         fd2 = fd_reopen(fd1, O_PATH|O_CLOEXEC);
         assert_se(fd2 >= 0);
-        assert_se(fstat(fd2, &st2) >= 0);
+        ASSERT_OK_ERRNO(fstat(fd2, &st2));
         assert_se(S_ISLNK(st2.st_mode));
         assert_se(stat_inode_same(&st1, &st2));
         fd2 = safe_close(fd2);
@@ -672,7 +676,7 @@ TEST(fd_get_path) {
         tfd = mkdtemp_open(NULL, O_PATH, &t);
         assert_se(tfd >= 0);
         assert_se(fd_get_path(tfd, &p) >= 0);
-        assert_se(streq(p, t));
+        ASSERT_STREQ(p, t);
 
         p = mfree(p);
 
@@ -680,7 +684,7 @@ TEST(fd_get_path) {
         assert_se(chdir(t) >= 0);
 
         assert_se(fd_get_path(AT_FDCWD, &p) >= 0);
-        assert_se(streq(p, t));
+        ASSERT_STREQ(p, t);
 
         p = mfree(p);
 
@@ -693,7 +697,7 @@ TEST(fd_get_path) {
         fd = openat(tfd, "regular", O_CLOEXEC|O_PATH);
         assert_se(fd >= 0);
         assert_se(fd_get_path(fd, &p) >= 0);
-        assert_se(streq(p, q));
+        ASSERT_STREQ(p, q);
 
         p = mfree(p);
         fd = safe_close(fd);
@@ -701,7 +705,7 @@ TEST(fd_get_path) {
         fd = openat(AT_FDCWD, "regular", O_CLOEXEC|O_PATH);
         assert_se(fd >= 0);
         assert_se(fd_get_path(fd, &p) >= 0);
-        assert_se(streq(p, q));
+        ASSERT_STREQ(p, q);
 
         p = mfree(p);
         fd = safe_close(fd);
@@ -710,7 +714,7 @@ TEST(fd_get_path) {
         assert_se(fd >= 0);
         assert_se(fd_verify_regular(fd) >= 0);
         assert_se(fd_get_path(fd, &p) >= 0);
-        assert_se(streq(p, q));
+        ASSERT_STREQ(p, q);
 
         p = mfree(p);
         fd = safe_close(fd);
@@ -719,7 +723,7 @@ TEST(fd_get_path) {
         assert_se(fd >= 0);
         assert_se(fd_verify_regular(fd) >= 0);
         assert_se(fd_get_path(fd, &p) >= 0);
-        assert_se(streq(p, q));
+        ASSERT_STREQ(p, q);
 
         p = mfree(p);
         fd = safe_close(fd);
@@ -728,7 +732,7 @@ TEST(fd_get_path) {
         assert_se(fd >= 0);
         assert_se(fd_verify_regular(fd) >= 0);
         assert_se(fd_get_path(fd, &p) >= 0);
-        assert_se(streq(p, q));
+        ASSERT_STREQ(p, q);
 
         p = mfree(p);
         fd = safe_close(fd);
@@ -737,7 +741,7 @@ TEST(fd_get_path) {
         assert_se(fd >= 0);
         assert_se(fd_verify_regular(fd) >= 0);
         assert_se(fd_get_path(fd, &p) >= 0);
-        assert_se(streq(p, q));
+        ASSERT_STREQ(p, q);
 
         p = mfree(p);
         q = mfree(q);
@@ -748,7 +752,7 @@ TEST(fd_get_path) {
         assert_se(fd >= 0);
         assert_se(fd_verify_regular(fd) == -ELOOP);
         assert_se(fd_get_path(fd, &p) >= 0);
-        assert_se(streq(p, q));
+        ASSERT_STREQ(p, q);
 
         p = mfree(p);
         fd = safe_close(fd);
@@ -757,7 +761,7 @@ TEST(fd_get_path) {
         assert_se(fd >= 0);
         assert_se(fd_verify_regular(fd) == -ELOOP);
         assert_se(fd_get_path(fd, &p) >= 0);
-        assert_se(streq(p, q));
+        ASSERT_STREQ(p, q);
 
         p = mfree(p);
         fd = safe_close(fd);
@@ -766,7 +770,7 @@ TEST(fd_get_path) {
         assert_se(fd >= 0);
         assert_se(fd_verify_regular(fd) == -ELOOP);
         assert_se(fd_get_path(fd, &p) >= 0);
-        assert_se(streq(p, q));
+        ASSERT_STREQ(p, q);
 
         p = mfree(p);
         fd = safe_close(fd);
@@ -775,7 +779,7 @@ TEST(fd_get_path) {
         assert_se(fd >= 0);
         assert_se(fd_verify_regular(fd) == -ELOOP);
         assert_se(fd_get_path(fd, &p) >= 0);
-        assert_se(streq(p, q));
+        ASSERT_STREQ(p, q);
 
         assert_se(chdir(saved_cwd) >= 0);
 }
index ad98a920601d613e32da0fddc1dd9dc3a7292ce5..474eacaf0468df1f25b3657c27cded178815ba5e 100644 (file)
@@ -71,27 +71,27 @@ TEST(parse_env_file) {
         STRV_FOREACH(i, a)
                 log_info("Got: <%s>", *i);
 
-        assert_se(streq_ptr(a[0], "one=BAR"));
-        assert_se(streq_ptr(a[1], "two=bar"));
-        assert_se(streq_ptr(a[2], "three=333\nxxxx"));
-        assert_se(streq_ptr(a[3], "four=44\\\"44"));
-        assert_se(streq_ptr(a[4], "five=55\"55FIVEcinco"));
-        assert_se(streq_ptr(a[5], "six=seis sechs sis"));
-        assert_se(streq_ptr(a[6], "seven=sevenval#nocomment"));
-        assert_se(streq_ptr(a[7], "eight=eightval #nocomment"));
-        assert_se(streq_ptr(a[8], "export nine=nineval"));
-        assert_se(streq_ptr(a[9], "ten="));
-        assert_se(streq_ptr(a[10], "eleven=value"));
-        assert_se(streq_ptr(a[11], "twelve=\\value"));
-        assert_se(streq_ptr(a[12], "thirteen=\\value"));
-        assert_se(a[13] == NULL);
+        ASSERT_STREQ(a[0], "one=BAR");
+        ASSERT_STREQ(a[1], "two=bar");
+        ASSERT_STREQ(a[2], "three=333\nxxxx");
+        ASSERT_STREQ(a[3], "four=44\\\"44");
+        ASSERT_STREQ(a[4], "five=55\"55FIVEcinco");
+        ASSERT_STREQ(a[5], "six=seis sechs sis");
+        ASSERT_STREQ(a[6], "seven=sevenval#nocomment");
+        ASSERT_STREQ(a[7], "eight=eightval #nocomment");
+        ASSERT_STREQ(a[8], "export nine=nineval");
+        ASSERT_STREQ(a[9], "ten=");
+        ASSERT_STREQ(a[10], "eleven=value");
+        ASSERT_STREQ(a[11], "twelve=\\value");
+        ASSERT_STREQ(a[12], "thirteen=\\value");
+        ASSERT_NULL(a[13]);
 
         strv_env_clean(a);
 
         k = 0;
         STRV_FOREACH(i, b) {
                 log_info("Got2: <%s>", *i);
-                assert_se(streq(*i, a[k++]));
+                ASSERT_STREQ(*i, a[k++]);
         }
 
         r = parse_env_file(
@@ -125,19 +125,19 @@ TEST(parse_env_file) {
         log_info("twelve=[%s]", strna(twelve));
         log_info("thirteen=[%s]", strna(thirteen));
 
-        assert_se(streq(one, "BAR"));
-        assert_se(streq(two, "bar"));
-        assert_se(streq(three, "333\nxxxx"));
-        assert_se(streq(four, "44\\\"44"));
-        assert_se(streq(five, "55\"55FIVEcinco"));
-        assert_se(streq(six, "seis sechs sis"));
-        assert_se(streq(seven, "sevenval#nocomment"));
-        assert_se(streq(eight, "eightval #nocomment"));
-        assert_se(streq(nine, "nineval"));
-        assert_se(ten == NULL);
-        assert_se(streq(eleven, "value"));
-        assert_se(streq(twelve, "\\value"));
-        assert_se(streq(thirteen, "\\value"));
+        ASSERT_STREQ(one, "BAR");
+        ASSERT_STREQ(two, "bar");
+        ASSERT_STREQ(three, "333\nxxxx");
+        ASSERT_STREQ(four, "44\\\"44");
+        ASSERT_STREQ(five, "55\"55FIVEcinco");
+        ASSERT_STREQ(six, "seis sechs sis");
+        ASSERT_STREQ(seven, "sevenval#nocomment");
+        ASSERT_STREQ(eight, "eightval #nocomment");
+        ASSERT_STREQ(nine, "nineval");
+        ASSERT_NULL(ten);
+        ASSERT_STREQ(eleven, "value");
+        ASSERT_STREQ(twelve, "\\value");
+        ASSERT_STREQ(thirteen, "\\value");
 
         {
                 /* prepare a temporary file to write the environment to */
@@ -161,7 +161,7 @@ static void test_one_shell_var(const char *file, const char *variable, const cha
         assert_se(f = popen(cmd, "re"));
         assert_se(read_full_stream(f, &from_shell, &sz) >= 0);
         assert_se(sz == strlen(value));
-        assert_se(streq(from_shell, value));
+        ASSERT_STREQ(from_shell, value);
 }
 
 TEST(parse_multiline_env_file) {
@@ -198,10 +198,10 @@ TEST(parse_multiline_env_file) {
         STRV_FOREACH(i, a)
                 log_info("Got: <%s>", *i);
 
-        assert_se(streq_ptr(a[0], "one=BAR    VAR\tGAR"));
-        assert_se(streq_ptr(a[1], "two=bar    var\tgar"));
-        assert_se(streq_ptr(a[2], "tri=bar     var \tgar "));
-        assert_se(a[3] == NULL);
+        ASSERT_STREQ(a[0], "one=BAR    VAR\tGAR");
+        ASSERT_STREQ(a[1], "two=bar    var\tgar");
+        ASSERT_STREQ(a[2], "tri=bar     var \tgar ");
+        ASSERT_NULL(a[3]);
 
         {
                 _cleanup_close_ int fd = mkostemp_safe(p);
@@ -246,17 +246,17 @@ TEST(merge_env_file) {
         STRV_FOREACH(i, a)
                 log_info("Got: <%s>", *i);
 
-        assert_se(streq(a[0], "one=2"));
-        assert_se(streq(a[1], "twelve=12"));
-        assert_se(streq(a[2], "twentyone=21"));
-        assert_se(streq(a[3], "twentytwo=22"));
-        assert_se(streq(a[4], "xxx=0x222"));
-        assert_se(streq(a[5], "xxx_minus_three= - 3"));
-        assert_se(streq(a[6], "yyy=2"));
-        assert_se(streq(a[7], "zzz=replacement"));
-        assert_se(streq(a[8], "zzzz="));
-        assert_se(streq(a[9], "zzzzz="));
-        assert_se(a[10] == NULL);
+        ASSERT_STREQ(a[0], "one=2");
+        ASSERT_STREQ(a[1], "twelve=12");
+        ASSERT_STREQ(a[2], "twentyone=21");
+        ASSERT_STREQ(a[3], "twentytwo=22");
+        ASSERT_STREQ(a[4], "xxx=0x222");
+        ASSERT_STREQ(a[5], "xxx_minus_three= - 3");
+        ASSERT_STREQ(a[6], "yyy=2");
+        ASSERT_STREQ(a[7], "zzz=replacement");
+        ASSERT_STREQ(a[8], "zzzz=");
+        ASSERT_STREQ(a[9], "zzzzz=");
+        ASSERT_NULL(a[10]);
 
         r = merge_env_file(&a, NULL, t);
         assert_se(r >= 0);
@@ -265,17 +265,17 @@ TEST(merge_env_file) {
         STRV_FOREACH(i, a)
                 log_info("Got2: <%s>", *i);
 
-        assert_se(streq(a[0], "one=2"));
-        assert_se(streq(a[1], "twelve=12"));
-        assert_se(streq(a[2], "twentyone=21"));
-        assert_se(streq(a[3], "twentytwo=22"));
-        assert_se(streq(a[4], "xxx=0x222"));
-        assert_se(streq(a[5], "xxx_minus_three=0x222 - 3"));
-        assert_se(streq(a[6], "yyy=2"));
-        assert_se(streq(a[7], "zzz=replacement"));
-        assert_se(streq(a[8], "zzzz="));
-        assert_se(streq(a[9], "zzzzz="));
-        assert_se(a[10] == NULL);
+        ASSERT_STREQ(a[0], "one=2");
+        ASSERT_STREQ(a[1], "twelve=12");
+        ASSERT_STREQ(a[2], "twentyone=21");
+        ASSERT_STREQ(a[3], "twentytwo=22");
+        ASSERT_STREQ(a[4], "xxx=0x222");
+        ASSERT_STREQ(a[5], "xxx_minus_three=0x222 - 3");
+        ASSERT_STREQ(a[6], "yyy=2");
+        ASSERT_STREQ(a[7], "zzz=replacement");
+        ASSERT_STREQ(a[8], "zzzz=");
+        ASSERT_STREQ(a[9], "zzzzz=");
+        ASSERT_NULL(a[10]);
 }
 
 TEST(merge_env_file_invalid) {
@@ -323,7 +323,7 @@ TEST(executable_is_script) {
 
         r = executable_is_script(t, &command);
         assert_se(r > 0);
-        assert_se(streq(command, "/bin/script"));
+        ASSERT_STREQ(command, "/bin/script");
         free(command);
 
         r = executable_is_script("/bin/sh", &command);
@@ -398,27 +398,27 @@ TEST(read_one_line_file) {
         assert_se(f);
 
         assert_se(read_one_line_file(fn, &buf) == 0);
-        assert_se(streq_ptr(buf, ""));
+        ASSERT_STREQ(buf, "");
         assert_se(read_one_line_file(fn, &buf2) == 0);
-        assert_se(streq_ptr(buf2, ""));
+        ASSERT_STREQ(buf2, "");
 
         assert_se(write_string_stream(f, "x", WRITE_STRING_FILE_AVOID_NEWLINE) >= 0);
         fflush(f);
 
         assert_se(read_one_line_file(fn, &buf3) == 1);
-        assert_se(streq_ptr(buf3, "x"));
+        ASSERT_STREQ(buf3, "x");
 
         assert_se(write_string_stream(f, "\n", WRITE_STRING_FILE_AVOID_NEWLINE) >= 0);
         fflush(f);
 
         assert_se(read_one_line_file(fn, &buf4) == 2);
-        assert_se(streq_ptr(buf4, "x"));
+        ASSERT_STREQ(buf4, "x");
 
         assert_se(write_string_stream(f, "\n", WRITE_STRING_FILE_AVOID_NEWLINE) >= 0);
         fflush(f);
 
         assert_se(read_one_line_file(fn, &buf5) == 2);
-        assert_se(streq_ptr(buf5, "x"));
+        ASSERT_STREQ(buf5, "x");
 }
 
 TEST(write_string_stream) {
@@ -442,7 +442,7 @@ TEST(write_string_stream) {
         rewind(f);
 
         assert_se(fgets(buf, sizeof(buf), f));
-        assert_se(streq(buf, "boohoo\n"));
+        ASSERT_STREQ(buf, "boohoo\n");
         f = safe_fclose(f);
 
         f = fopen(fn, "w+");
@@ -453,7 +453,7 @@ TEST(write_string_stream) {
 
         assert_se(fgets(buf, sizeof(buf), f));
         printf(">%s<", buf);
-        assert_se(streq(buf, "boohoo"));
+        ASSERT_STREQ(buf, "boohoo");
 }
 
 TEST(write_string_file) {
@@ -467,7 +467,7 @@ TEST(write_string_file) {
         assert_se(write_string_file(fn, "boohoo", WRITE_STRING_FILE_CREATE) == 0);
 
         assert_se(read(fd, buf, sizeof(buf)) == 7);
-        assert_se(streq(buf, "boohoo\n"));
+        ASSERT_STREQ(buf, "boohoo\n");
 }
 
 TEST(write_string_file_no_create) {
@@ -482,7 +482,7 @@ TEST(write_string_file_no_create) {
         assert_se(write_string_file(fn, "boohoo", 0) == 0);
 
         assert_se(read(fd, buf, sizeof buf) == (ssize_t) strlen("boohoo\n"));
-        assert_se(streq(buf, "boohoo\n"));
+        ASSERT_STREQ(buf, "boohoo\n");
 }
 
 TEST(write_string_file_verify) {
@@ -578,14 +578,14 @@ TEST(search_and_fopen) {
         r = search_and_fopen(basename(name), "re", NULL, (const char**) dirs, &f, &p);
         assert_se(r >= 0);
         assert_se(e = path_startswith(p, "/tmp/"));
-        assert_se(streq(basename(name), e));
+        ASSERT_STREQ(basename(name), e);
         f = safe_fclose(f);
         p = mfree(p);
 
         r = search_and_fopen(basename(name), NULL, NULL, (const char**) dirs, NULL, &p);
         assert_se(r >= 0);
         assert_se(e = path_startswith(p, "/tmp/"));
-        assert_se(streq(basename(name), e));
+        ASSERT_STREQ(basename(name), e);
         p = mfree(p);
 
         r = search_and_fopen(name, "re", NULL, (const char**) dirs, &f, &p);
@@ -602,14 +602,14 @@ TEST(search_and_fopen) {
         r = search_and_fopen(basename(name), "re", "/", (const char**) dirs, &f, &p);
         assert_se(r >= 0);
         assert_se(e = path_startswith(p, "/tmp/"));
-        assert_se(streq(basename(name), e));
+        ASSERT_STREQ(basename(name), e);
         f = safe_fclose(f);
         p = mfree(p);
 
         r = search_and_fopen(basename(name), NULL, "/", (const char**) dirs, NULL, &p);
         assert_se(r >= 0);
         assert_se(e = path_startswith(p, "/tmp/"));
-        assert_se(streq(basename(name), e));
+        ASSERT_STREQ(basename(name), e);
         p = mfree(p);
 
         r = search_and_fopen("/a/file/which/does/not/exist/i/guess", "re", NULL, (const char**) dirs, &f, &p);
@@ -649,7 +649,7 @@ TEST(search_and_fopen_nulstr) {
         r = search_and_fopen_nulstr(basename(name), "re", NULL, dirs, &f, &p);
         assert_se(r >= 0);
         assert_se(e = path_startswith(p, "/tmp/"));
-        assert_se(streq(basename(name), e));
+        ASSERT_STREQ(basename(name), e);
         f = safe_fclose(f);
         p = mfree(p);
 
@@ -693,18 +693,18 @@ TEST(writing_tmpfile) {
         r = read_full_file(name, &contents, &size);
         assert_se(r == 0);
         printf("contents: %s", contents);
-        assert_se(streq(contents, "abc\n" ALPHANUMERICAL "\n"));
+        ASSERT_STREQ(contents, "abc\n" ALPHANUMERICAL "\n");
 }
 
 TEST(tempfn) {
         char *ret = NULL, *p;
 
         assert_se(tempfn_xxxxxx("/foo/bar/waldo", NULL, &ret) >= 0);
-        assert_se(streq_ptr(ret, "/foo/bar/.#waldoXXXXXX"));
+        ASSERT_STREQ(ret, "/foo/bar/.#waldoXXXXXX");
         free(ret);
 
         assert_se(tempfn_xxxxxx("/foo/bar/waldo", "[miau]", &ret) >= 0);
-        assert_se(streq_ptr(ret, "/foo/bar/.#[miau]waldoXXXXXX"));
+        ASSERT_STREQ(ret, "/foo/bar/.#[miau]waldoXXXXXX");
         free(ret);
 
         assert_se(tempfn_random("/foo/bar/waldo", NULL, &ret) >= 0);
@@ -900,7 +900,7 @@ TEST(read_line4) {
 
                 r = read_line(f, SIZE_MAX, &s);
                 assert_se((size_t) r == eof_endings[i].length);
-                assert_se(streq_ptr(s, "foo"));
+                ASSERT_STREQ(s, "foo");
 
                 assert_se(read_line(f, SIZE_MAX, NULL) == 0); /* Ensure we hit EOF */
         }
@@ -985,7 +985,7 @@ TEST(read_full_file_socket) {
                 assert_se(peer.un.sun_family == AF_UNIX);
                 assert_se(peerlen > offsetof(struct sockaddr_un, sun_path));
                 assert_se(peer.un.sun_path[0] == 0);
-                assert_se(streq(peer.un.sun_path + 1, clientname + 1));
+                ASSERT_STREQ(peer.un.sun_path + 1, clientname + 1);
 
 #define TEST_STR "This is a test\nreally."
 
@@ -996,7 +996,7 @@ TEST(read_full_file_socket) {
         assert_se(read_full_file_full(AT_FDCWD, jj, UINT64_MAX, SIZE_MAX, 0, NULL, &data, &size) == -ENXIO);
         assert_se(read_full_file_full(AT_FDCWD, jj, UINT64_MAX, SIZE_MAX, READ_FULL_FILE_CONNECT_SOCKET, clientname, &data, &size) >= 0);
         assert_se(size == strlen(TEST_STR));
-        assert_se(streq(data, TEST_STR));
+        ASSERT_STREQ(data, TEST_STR);
 
         assert_se(wait_for_terminate_and_check("(server)", pid, WAIT_LOG) >= 0);
 #undef TEST_STR
@@ -1125,7 +1125,7 @@ TEST(fdopen_independent) {
         assert_se(fdopen_independent(fd, "re", &f) >= 0);
         zero(buf);
         assert_se(fread(buf, 1, sizeof(buf), f) == strlen(TEST_TEXT));
-        assert_se(streq(buf, TEST_TEXT));
+        ASSERT_STREQ(buf, TEST_TEXT);
         assert_se((fcntl(fileno(f), F_GETFL) & O_ACCMODE) == O_RDONLY);
         assert_se(FLAGS_SET(fcntl(fileno(f), F_GETFD), FD_CLOEXEC));
         f = safe_fclose(f);
@@ -1133,7 +1133,7 @@ TEST(fdopen_independent) {
         assert_se(fdopen_independent(fd, "r", &f) >= 0);
         zero(buf);
         assert_se(fread(buf, 1, sizeof(buf), f) == strlen(TEST_TEXT));
-        assert_se(streq(buf, TEST_TEXT));
+        ASSERT_STREQ(buf, TEST_TEXT);
         assert_se((fcntl(fileno(f), F_GETFL) & O_ACCMODE) == O_RDONLY);
         assert_se(!FLAGS_SET(fcntl(fileno(f), F_GETFD), FD_CLOEXEC));
         f = safe_fclose(f);
@@ -1141,7 +1141,7 @@ TEST(fdopen_independent) {
         assert_se(fdopen_independent(fd, "r+e", &f) >= 0);
         zero(buf);
         assert_se(fread(buf, 1, sizeof(buf), f) == strlen(TEST_TEXT));
-        assert_se(streq(buf, TEST_TEXT));
+        ASSERT_STREQ(buf, TEST_TEXT);
         assert_se((fcntl(fileno(f), F_GETFL) & O_ACCMODE) == O_RDWR);
         assert_se(FLAGS_SET(fcntl(fileno(f), F_GETFD), FD_CLOEXEC));
         f = safe_fclose(f);
index 7d544b18fdde42db0f14bcc49ff5e5703d59bae3..c16a5b793b89d40ce7904a1c09df5a4b9c21e2c2 100644 (file)
@@ -580,7 +580,7 @@ TEST(path_basename) {
 
         assert_se(table_format(t, &formatted) >= 0);
 
-        assert_se(streq(formatted, "bar\nbar\nbaz\n"));
+        ASSERT_STREQ(formatted, "bar\nbar\nbaz\n");
 }
 
 TEST(dup_cell) {
index 306350937a78e49002105a293c1917d75215c9e0..94feb6cafc0b5a31d8b51ee30fc1a6d819f6fe15 100644 (file)
@@ -34,10 +34,10 @@ static void test_format_bytes_one(uint64_t val, bool trailing_B, const char *iec
                                   const char *si_with_p, const char *si_without_p) {
         char buf[FORMAT_BYTES_MAX];
 
-        assert_se(streq_ptr(format_bytes_full(buf, sizeof buf, val, FORMAT_BYTES_USE_IEC | FORMAT_BYTES_BELOW_POINT | (trailing_B ? FORMAT_BYTES_TRAILING_B : 0)), iec_with_p));
-        assert_se(streq_ptr(format_bytes_full(buf, sizeof buf, val, FORMAT_BYTES_USE_IEC | (trailing_B ? FORMAT_BYTES_TRAILING_B : 0)), iec_without_p));
-        assert_se(streq_ptr(format_bytes_full(buf, sizeof buf, val, FORMAT_BYTES_BELOW_POINT | (trailing_B ? FORMAT_BYTES_TRAILING_B : 0)), si_with_p));
-        assert_se(streq_ptr(format_bytes_full(buf, sizeof buf, val, trailing_B ? FORMAT_BYTES_TRAILING_B : 0), si_without_p));
+        ASSERT_STREQ(format_bytes_full(buf, sizeof buf, val, FORMAT_BYTES_USE_IEC | FORMAT_BYTES_BELOW_POINT | (trailing_B ? FORMAT_BYTES_TRAILING_B : 0)), iec_with_p);
+        ASSERT_STREQ(format_bytes_full(buf, sizeof buf, val, FORMAT_BYTES_USE_IEC | (trailing_B ? FORMAT_BYTES_TRAILING_B : 0)), iec_without_p);
+        ASSERT_STREQ(format_bytes_full(buf, sizeof buf, val, FORMAT_BYTES_BELOW_POINT | (trailing_B ? FORMAT_BYTES_TRAILING_B : 0)), si_with_p);
+        ASSERT_STREQ(format_bytes_full(buf, sizeof buf, val, trailing_B ? FORMAT_BYTES_TRAILING_B : 0), si_without_p);
 }
 
 TEST(format_bytes) {
index b27c79fdc93d1ac8ae69ed22abb05e10d1bd6428..d44e0437f5fa9c520c2df85a400e2b89b251b54b 100644 (file)
@@ -45,7 +45,7 @@ TEST(readlink_and_make_absolute) {
                 log_tests_skipped_errno(errno, "symlink() not possible");
         } else {
                 assert_se(readlink_and_make_absolute(name_alias, &r1) >= 0);
-                assert_se(streq(r1, name));
+                ASSERT_STREQ(r1, name);
                 assert_se(unlink(name_alias) >= 0);
 
                 assert_se(safe_getcwd(&pwd) >= 0);
@@ -53,7 +53,7 @@ TEST(readlink_and_make_absolute) {
                 assert_se(chdir(tempdir) >= 0);
                 assert_se(symlink(name2, name_alias) >= 0);
                 assert_se(readlink_and_make_absolute(name_alias, &r2) >= 0);
-                assert_se(streq(r2, name));
+                ASSERT_STREQ(r2, name);
                 assert_se(unlink(name_alias) >= 0);
 
                 assert_se(chdir(pwd) >= 0);
@@ -97,33 +97,33 @@ TEST(var_tmp) {
         assert_se(unsetenv("TMP") >= 0);
 
         assert_se(var_tmp_dir(&tmp_dir) >= 0);
-        assert_se(streq(tmp_dir, "/var/tmp"));
+        ASSERT_STREQ(tmp_dir, "/var/tmp");
 
         assert_se(setenv("TMPDIR", "/tmp", true) >= 0);
-        assert_se(streq(getenv("TMPDIR"), "/tmp"));
+        ASSERT_STREQ(getenv("TMPDIR"), "/tmp");
 
         assert_se(var_tmp_dir(&tmp_dir) >= 0);
-        assert_se(streq(tmp_dir, "/tmp"));
+        ASSERT_STREQ(tmp_dir, "/tmp");
 
         assert_se(setenv("TMPDIR", "/88_does_not_exist_88", true) >= 0);
-        assert_se(streq(getenv("TMPDIR"), "/88_does_not_exist_88"));
+        ASSERT_STREQ(getenv("TMPDIR"), "/88_does_not_exist_88");
 
         assert_se(var_tmp_dir(&tmp_dir) >= 0);
-        assert_se(streq(tmp_dir, "/var/tmp"));
+        ASSERT_STREQ(tmp_dir, "/var/tmp");
 
         if (tmpdir_backup)  {
                 assert_se(setenv("TMPDIR", tmpdir_backup, true) >= 0);
-                assert_se(streq(getenv("TMPDIR"), tmpdir_backup));
+                ASSERT_STREQ(getenv("TMPDIR"), tmpdir_backup);
         }
 
         if (temp_backup)  {
                 assert_se(setenv("TEMP", temp_backup, true) >= 0);
-                assert_se(streq(getenv("TEMP"), temp_backup));
+                ASSERT_STREQ(getenv("TEMP"), temp_backup);
         }
 
         if (tmp_backup)  {
                 assert_se(setenv("TMP", tmp_backup, true) >= 0);
-                assert_se(streq(getenv("TMP"), tmp_backup));
+                ASSERT_STREQ(getenv("TMP"), tmp_backup);
         }
 }
 
@@ -275,14 +275,14 @@ TEST(unlinkat_deallocate) {
 
         assert_se(write(fd, "hallo\n", 6) == 6);
 
-        assert_se(fstat(fd, &st) >= 0);
+        ASSERT_OK_ERRNO(fstat(fd, &st));
         assert_se(st.st_size == 6);
         assert_se(st.st_blocks > 0);
         assert_se(st.st_nlink == 1);
 
         assert_se(unlinkat_deallocate(AT_FDCWD, p, UNLINK_ERASE) >= 0);
 
-        assert_se(fstat(fd, &st) >= 0);
+        ASSERT_OK_ERRNO(fstat(fd, &st));
         assert_se(IN_SET(st.st_size, 0, 6)); /* depending on whether hole punching worked the size will be 6
                                                 (it worked) or 0 (we had to resort to truncation) */
         assert_se(st.st_blocks == 0);
@@ -522,9 +522,9 @@ static void test_parse_cifs_service_one(const char *f, const char *h, const char
         _cleanup_free_ char *a = NULL, *b = NULL, *c = NULL;
 
         assert_se(parse_cifs_service(f, &a, &b, &c) == ret);
-        assert_se(streq_ptr(a, h));
-        assert_se(streq_ptr(b, s));
-        assert_se(streq_ptr(c, d));
+        ASSERT_STREQ(a, h);
+        ASSERT_STREQ(b, s);
+        ASSERT_STREQ(c, d);
 }
 
 TEST(parse_cifs_service) {
@@ -557,13 +557,13 @@ TEST(open_mkdir_at) {
         fd = open_mkdir_at(AT_FDCWD, "/", O_CLOEXEC, 0);
         assert_se(fd >= 0);
         assert_se(stat("/", &sta) >= 0);
-        assert_se(fstat(fd, &stb) >= 0);
+        ASSERT_OK_ERRNO(fstat(fd, &stb));
         assert_se(stat_inode_same(&sta, &stb));
         fd = safe_close(fd);
 
         fd = open_mkdir_at(AT_FDCWD, ".", O_CLOEXEC, 0);
         assert_se(stat(".", &sta) >= 0);
-        assert_se(fstat(fd, &stb) >= 0);
+        ASSERT_OK_ERRNO(fstat(fd, &stb));
         assert_se(stat_inode_same(&sta, &stb));
         fd = safe_close(fd);
 
@@ -800,25 +800,25 @@ TEST(readlinkat_malloc) {
         assert_se(symlinkat(expect, tfd, "linkname") >= 0);
 
         assert_se(readlinkat_malloc(tfd, "linkname", &p) >= 0);
-        assert_se(streq(p, expect));
+        ASSERT_STREQ(p, expect);
         p = mfree(p);
 
         fd = openat(tfd, "linkname", O_PATH | O_NOFOLLOW | O_CLOEXEC);
         assert_se(fd >= 0);
         assert_se(readlinkat_malloc(fd, NULL, &p) >= 0);
-        assert_se(streq(p, expect));
+        ASSERT_STREQ(p, expect);
         p = mfree(p);
         assert_se(readlinkat_malloc(fd, "", &p) >= 0);
-        assert_se(streq(p, expect));
+        ASSERT_STREQ(p, expect);
         p = mfree(p);
         fd = safe_close(fd);
 
         assert_se(q = path_join(t, "linkname"));
         assert_se(readlinkat_malloc(AT_FDCWD, q, &p) >= 0);
-        assert_se(streq(p, expect));
+        ASSERT_STREQ(p, expect);
         p = mfree(p);
         assert_se(readlinkat_malloc(INT_MAX, q, &p) >= 0);
-        assert_se(streq(p, expect));
+        ASSERT_STREQ(p, expect);
         p = mfree(p);
         q = mfree(q);
 }
index 49561a21d7077dbd790f54f1cbf9770836294569..773b1f9799fd7d88687664c1643fe48fae74bd8a 100644 (file)
@@ -39,9 +39,9 @@ static void do_fstab_filter_options(const char *opts,
                  opts, r, strnull(name), value, filtered,
                  r_expected, strnull(name_expected), strnull(value_expected), filtered_expected ?: opts);
         assert_se(r == r_expected);
-        assert_se(streq_ptr(name, name_expected));
-        assert_se(streq_ptr(value, value_expected));
-        assert_se(streq_ptr(filtered, filtered_expected ?: opts));
+        ASSERT_STREQ(name, name_expected);
+        ASSERT_STREQ(value, value_expected);
+        ASSERT_STREQ(filtered, filtered_expected ?: opts);
 
         /* test mode which returns all the values */
 
@@ -51,8 +51,8 @@ static void do_fstab_filter_options(const char *opts,
                  opts, r, strnull(name), joined,
                  r_values_expected, strnull(name_expected), strnull(values_expected));
         assert_se(r == r_values_expected);
-        assert_se(streq_ptr(name, r_values_expected > 0 ? name_expected : NULL));
-        assert_se(streq_ptr(joined, values_expected));
+        ASSERT_STREQ(name, r_values_expected > 0 ? name_expected : NULL);
+        ASSERT_STREQ(joined, values_expected);
 
         /* also test the malloc-less mode */
         r = fstab_filter_options(opts, remove, &name, NULL, NULL, NULL);
@@ -60,7 +60,7 @@ static void do_fstab_filter_options(const char *opts,
                  opts, r, strnull(name),
                  r_expected, strnull(name_expected));
         assert_se(r == r_expected);
-        assert_se(streq_ptr(name, name_expected));
+        ASSERT_STREQ(name, name_expected);
 }
 
 TEST(fstab_filter_options) {
@@ -160,32 +160,32 @@ TEST(fstab_node_to_udev_node) {
 
         n = fstab_node_to_udev_node("LABEL=applé/jack");
         puts(n);
-        assert_se(streq(n, "/dev/disk/by-label/applé\\x2fjack"));
+        ASSERT_STREQ(n, "/dev/disk/by-label/applé\\x2fjack");
         free(n);
 
         n = fstab_node_to_udev_node("PARTLABEL=pinkié pie");
         puts(n);
-        assert_se(streq(n, "/dev/disk/by-partlabel/pinkié\\x20pie"));
+        ASSERT_STREQ(n, "/dev/disk/by-partlabel/pinkié\\x20pie");
         free(n);
 
         n = fstab_node_to_udev_node("UUID=037b9d94-148e-4ee4-8d38-67bfe15bb535");
         puts(n);
-        assert_se(streq(n, "/dev/disk/by-uuid/037b9d94-148e-4ee4-8d38-67bfe15bb535"));
+        ASSERT_STREQ(n, "/dev/disk/by-uuid/037b9d94-148e-4ee4-8d38-67bfe15bb535");
         free(n);
 
         n = fstab_node_to_udev_node("PARTUUID=037b9d94-148e-4ee4-8d38-67bfe15bb535");
         puts(n);
-        assert_se(streq(n, "/dev/disk/by-partuuid/037b9d94-148e-4ee4-8d38-67bfe15bb535"));
+        ASSERT_STREQ(n, "/dev/disk/by-partuuid/037b9d94-148e-4ee4-8d38-67bfe15bb535");
         free(n);
 
         n = fstab_node_to_udev_node("PONIES=awesome");
         puts(n);
-        assert_se(streq(n, "PONIES=awesome"));
+        ASSERT_STREQ(n, "PONIES=awesome");
         free(n);
 
         n = fstab_node_to_udev_node("/dev/xda1");
         puts(n);
-        assert_se(streq(n, "/dev/xda1"));
+        ASSERT_STREQ(n, "/dev/xda1");
         free(n);
 }
 
index 9b3e73cce017231ae6ef6a1164619a7daf46738e..49d71f15c714cb7591e864b9f35499567c718c58 100644 (file)
@@ -24,14 +24,14 @@ TEST(glob_first) {
 
         r = glob_first("/tmp/test-glob_first*", &first);
         assert_se(r == 1);
-        assert_se(streq(name, first));
+        ASSERT_STREQ(name, first);
         first = mfree(first);
 
         r = unlink(name);
         assert_se(r == 0);
         r = glob_first("/tmp/test-glob_first*", &first);
         assert_se(r == 0);
-        assert_se(first == NULL);
+        ASSERT_NULL(first);
 }
 
 TEST(glob_exists) {
@@ -109,8 +109,8 @@ TEST(safe_glob) {
         r = safe_glob(fn2, GLOB_NOSORT|GLOB_BRACE, &g);
         assert_se(r == 0);
         assert_se(g.gl_pathc == 1);
-        assert_se(streq(g.gl_pathv[0], fname));
-        assert_se(g.gl_pathv[1] == NULL);
+        ASSERT_STREQ(g.gl_pathv[0], fname);
+        ASSERT_NULL(g.gl_pathv[1]);
 
         (void) rm_rf(template, REMOVE_ROOT|REMOVE_PHYSICAL);
 }
@@ -119,7 +119,7 @@ static void test_glob_non_glob_prefix_one(const char *path, const char *expected
         _cleanup_free_ char *t;
 
         assert_se(glob_non_glob_prefix(path, &t) == 0);
-        assert_se(streq(t, expected));
+        ASSERT_STREQ(t, expected);
 }
 
 TEST(glob_non_glob) {
index 825aac65080db8fe9f0c454474a908360240ba8d..28b53d479a0ef77da3564b75b02a58b837128ca8 100644 (file)
@@ -94,7 +94,7 @@ TEST(override_architecture) {
         ASSERT_EQ(x.arch, y.arch);
         ASSERT_EQ(x.designator, y.designator);
         assert_se(sd_id128_equal(x.uuid, y.uuid));
-        assert_se(streq(x.name, y.name));
+        ASSERT_STREQ(x.name, y.name);
 
         /* If the partition type does not have an architecture, nothing should change. */
 
@@ -105,7 +105,7 @@ TEST(override_architecture) {
         ASSERT_EQ(x.arch, y.arch);
         ASSERT_EQ(x.designator, y.designator);
         assert_se(sd_id128_equal(x.uuid, y.uuid));
-        assert_se(streq(x.name, y.name));
+        ASSERT_STREQ(x.name, y.name);
 }
 
 DEFINE_TEST_MAIN(LOG_INFO);
index 0d1f84579442f060e3dae5c71f173a1fed2e3a5f..f2d2d4f75da019000f7e568d7dff6425f2e742df 100644 (file)
 #include "tests.h"
 #include "time-util.h"
 
+/* PROJECT_FILE, which is used by ASSERT_XYZ(), cannot be used in generated files, as the build directory
+ * may be outside of the source directory. */
+#ifdef ORDERED
+#  undef PROJECT_FILE
+#  define PROJECT_FILE __FILE__
+#endif
+
 TEST(hashmap_replace) {
         _cleanup_hashmap_free_ Hashmap *m = NULL;
         _cleanup_free_ char *val1 = NULL, *val2 = NULL, *val3 = NULL, *val4 = NULL, *val5 = NULL;
@@ -35,11 +42,11 @@ TEST(hashmap_replace) {
 
         hashmap_replace(m, "key 3", val1);
         r = hashmap_get(m, "key 3");
-        assert_se(streq(r, "val1"));
+        ASSERT_STREQ(r, "val1");
 
         hashmap_replace(m, "key 5", val5);
         r = hashmap_get(m, "key 5");
-        assert_se(streq(r, "val5"));
+        ASSERT_STREQ(r, "val5");
 }
 
 TEST(hashmap_copy) {
@@ -66,13 +73,13 @@ TEST(hashmap_copy) {
         copy = hashmap_copy(m);
 
         r = hashmap_get(copy, "key 1");
-        assert_se(streq(r, "val1"));
+        ASSERT_STREQ(r, "val1");
         r = hashmap_get(copy, "key 2");
-        assert_se(streq(r, "val2"));
+        ASSERT_STREQ(r, "val2");
         r = hashmap_get(copy, "key 3");
-        assert_se(streq(r, "val3"));
+        ASSERT_STREQ(r, "val3");
         r = hashmap_get(copy, "key 4");
-        assert_se(streq(r, "val4"));
+        ASSERT_STREQ(r, "val4");
 }
 
 TEST(hashmap_get_strv) {
@@ -102,10 +109,10 @@ TEST(hashmap_get_strv) {
         strv = strv_sort(strv);
 #endif
 
-        assert_se(streq(strv[0], "val1"));
-        assert_se(streq(strv[1], "val2"));
-        assert_se(streq(strv[2], "val3"));
-        assert_se(streq(strv[3], "val4"));
+        ASSERT_STREQ(strv[0], "val1");
+        ASSERT_STREQ(strv[1], "val2");
+        ASSERT_STREQ(strv[2], "val3");
+        ASSERT_STREQ(strv[3], "val4");
 }
 
 TEST(hashmap_move_one) {
@@ -196,15 +203,15 @@ TEST(hashmap_update) {
 
         hashmap_put(m, "key 1", val1);
         r = hashmap_get(m, "key 1");
-        assert_se(streq(r, "old_value"));
+        ASSERT_STREQ(r, "old_value");
 
         assert_se(hashmap_update(m, "key 2", val2) == -ENOENT);
         r = hashmap_get(m, "key 1");
-        assert_se(streq(r, "old_value"));
+        ASSERT_STREQ(r, "old_value");
 
         assert_se(hashmap_update(m, "key 1", val2) == 0);
         r = hashmap_get(m, "key 1");
-        assert_se(streq(r, "new_value"));
+        ASSERT_STREQ(r, "new_value");
 }
 
 TEST(hashmap_put) {
@@ -231,22 +238,22 @@ TEST(hashmap_remove1) {
         char *r;
 
         r = hashmap_remove(NULL, "key 1");
-        assert_se(r == NULL);
+        ASSERT_NULL(r);
 
         m = hashmap_new(&string_hash_ops);
         assert_se(m);
 
         r = hashmap_remove(m, "no such key");
-        assert_se(r == NULL);
+        ASSERT_NULL(r);
 
         hashmap_put(m, "key 1", (void*) "val 1");
         hashmap_put(m, "key 2", (void*) "val 2");
 
         r = hashmap_remove(m, "key 1");
-        assert_se(streq(r, "val 1"));
+        ASSERT_STREQ(r, "val 1");
 
         r = hashmap_get(m, "key 2");
-        assert_se(streq(r, "val 2"));
+        ASSERT_STREQ(r, "val 2");
         assert_se(!hashmap_get(m, "key 1"));
 }
 
@@ -259,25 +266,25 @@ TEST(hashmap_remove2) {
         void *r, *r2;
 
         r = hashmap_remove2(NULL, "key 1", &r2);
-        assert_se(r == NULL);
+        ASSERT_NULL(r);
 
         m = hashmap_new(&string_hash_ops);
         assert_se(m);
 
         r = hashmap_remove2(m, "no such key", &r2);
-        assert_se(r == NULL);
+        ASSERT_NULL(r);
 
         hashmap_put(m, strdup(key1), strdup(val1));
         hashmap_put(m, strdup(key2), strdup(val2));
 
         r = hashmap_remove2(m, key1, &r2);
-        assert_se(streq(r, val1));
-        assert_se(streq(r2, key1));
+        ASSERT_STREQ(r, val1);
+        ASSERT_STREQ(r2, key1);
         free(r);
         free(r2);
 
         r = hashmap_get(m, key2);
-        assert_se(streq(r, val2));
+        ASSERT_STREQ(r, val2);
         assert_se(!hashmap_get(m, key1));
 }
 
@@ -289,29 +296,29 @@ TEST(hashmap_remove_value) {
         char val2[] = "val 2";
 
         r = hashmap_remove_value(NULL, "key 1", val1);
-        assert_se(r == NULL);
+        ASSERT_NULL(r);
 
         m = hashmap_new(&string_hash_ops);
         assert_se(m);
 
         r = hashmap_remove_value(m, "key 1", val1);
-        assert_se(r == NULL);
+        ASSERT_NULL(r);
 
         hashmap_put(m, "key 1", val1);
         hashmap_put(m, "key 2", val2);
 
         r = hashmap_remove_value(m, "key 1", val1);
-        assert_se(streq(r, "val 1"));
+        ASSERT_STREQ(r, "val 1");
 
         r = hashmap_get(m, "key 2");
-        assert_se(streq(r, "val 2"));
+        ASSERT_STREQ(r, "val 2");
         assert_se(!hashmap_get(m, "key 1"));
 
         r = hashmap_remove_value(m, "key 2", val1);
-        assert_se(r == NULL);
+        ASSERT_NULL(r);
 
         r = hashmap_get(m, "key 2");
-        assert_se(streq(r, "val 2"));
+        ASSERT_STREQ(r, "val 2");
         assert_se(!hashmap_get(m, "key 1"));
 }
 
@@ -336,7 +343,7 @@ TEST(hashmap_remove_and_put) {
         assert_se(valid == 0);
 
         r = hashmap_get(m, "key 2");
-        assert_se(streq(r, "val 2"));
+        ASSERT_STREQ(r, "val 2");
         assert_se(!hashmap_get(m, "key 1"));
 
         valid = hashmap_put(m, "key 3", (void*) (const char *) "val 3");
@@ -599,17 +606,17 @@ TEST(hashmap_get) {
         assert_se(val);
 
         r = hashmap_get(NULL, "Key 1");
-        assert_se(r == NULL);
+        ASSERT_NULL(r);
 
         m = hashmap_new(&string_hash_ops);
 
         hashmap_put(m, "Key 1", val);
 
         r = hashmap_get(m, "Key 1");
-        assert_se(streq(r, val));
+        ASSERT_STREQ(r, val);
 
         r = hashmap_get(m, "no such key");
-        assert_se(r == NULL);
+        ASSERT_NULL(r);
 
         assert_se(m);
 }
@@ -628,7 +635,7 @@ TEST(hashmap_get2) {
         assert_se(key_copy);
 
         r = hashmap_get2(NULL, key_orig, &key_copy);
-        assert_se(r == NULL);
+        ASSERT_NULL(r);
 
         m = hashmap_new(&string_hash_ops);
 
@@ -636,12 +643,12 @@ TEST(hashmap_get2) {
         key_copy = NULL;
 
         r = hashmap_get2(m, key_orig, &key_copy);
-        assert_se(streq(r, val));
+        ASSERT_STREQ(r, val);
         assert_se(key_orig != key_copy);
-        assert_se(streq(key_orig, key_copy));
+        ASSERT_STREQ(key_orig, key_copy);
 
         r = hashmap_get2(m, "no such key", NULL);
-        assert_se(r == NULL);
+        ASSERT_NULL(r);
 
         assert_se(m);
 }
@@ -780,12 +787,12 @@ TEST(hashmap_first) {
 
         assert_se(!hashmap_first(m));
         assert_se(hashmap_put(m, "key 1", (void*) "val 1") == 1);
-        assert_se(streq(hashmap_first(m), "val 1"));
+        ASSERT_STREQ(hashmap_first(m), "val 1");
         assert_se(hashmap_put(m, "key 2", (void*) "val 2") == 1);
 #ifdef ORDERED
-        assert_se(streq(hashmap_first(m), "val 1"));
+        ASSERT_STREQ(hashmap_first(m), "val 1");
         assert_se(hashmap_remove(m, "key 1"));
-        assert_se(streq(hashmap_first(m), "val 2"));
+        ASSERT_STREQ(hashmap_first(m), "val 2");
 #endif
 }
 
@@ -797,12 +804,12 @@ TEST(hashmap_first_key) {
 
         assert_se(!hashmap_first_key(m));
         assert_se(hashmap_put(m, "key 1", NULL) == 1);
-        assert_se(streq(hashmap_first_key(m), "key 1"));
+        ASSERT_STREQ(hashmap_first_key(m), "key 1");
         assert_se(hashmap_put(m, "key 2", NULL) == 1);
 #ifdef ORDERED
-        assert_se(streq(hashmap_first_key(m), "key 1"));
-        assert_se(hashmap_remove(m, "key 1") == NULL);
-        assert_se(streq(hashmap_first_key(m), "key 2"));
+        ASSERT_STREQ(hashmap_first_key(m), "key 1");
+        ASSERT_NULL(hashmap_remove(m, "key 1"));
+        ASSERT_STREQ(hashmap_first_key(m), "key 2");
 #endif
 }
 
@@ -814,7 +821,7 @@ TEST(hashmap_steal_first_key) {
 
         assert_se(!hashmap_steal_first_key(m));
         assert_se(hashmap_put(m, "key 1", NULL) == 1);
-        assert_se(streq(hashmap_steal_first_key(m), "key 1"));
+        ASSERT_STREQ(hashmap_steal_first_key(m), "key 1");
 
         assert_se(hashmap_isempty(m));
 }
@@ -990,7 +997,7 @@ TEST(hashmap_dump_sorted) {
         assert_se(hashmap_dump_keys_sorted(m, &vals, &n) >= 0);
         assert_se(n == ELEMENTSOF(expected_keys));
         for (size_t i = 0; i < n; i++)
-                assert_se(streq(vals[i], expected_keys[i]));
+                ASSERT_STREQ(vals[i], expected_keys[i]);
 
         vals = mfree(vals);
         m = hashmap_free(m);
index cf4fca27e3f7aef5b55cf9658004d449eddff689..74e68a473d9e8130c93972d989f49d4dcdaf43c8 100644 (file)
@@ -98,8 +98,8 @@ TEST(iterated_cache) {
         hashmap_clear(m);
         compare_cache(m, c);
 
-        assert_se(hashmap_free(m) == NULL);
-        assert_se(iterated_cache_free(c) == NULL);
+        ASSERT_NULL(hashmap_free(m));
+        ASSERT_NULL(iterated_cache_free(c));
 }
 
 TEST(hashmap_put_strdup) {
@@ -116,7 +116,7 @@ TEST(hashmap_put_strdup) {
         assert_se(hashmap_contains(m, "foo"));
 
         s = hashmap_get(m, "foo");
-        assert_se(streq(s, "bar"));
+        ASSERT_STREQ(s, "bar");
 
         assert_se(hashmap_put_strdup(&m, "xxx", "bar") == 1);
         assert_se(hashmap_put_strdup(&m, "xxx", "bar") == 0);
@@ -125,7 +125,7 @@ TEST(hashmap_put_strdup) {
         assert_se(hashmap_contains(m, "xxx"));
 
         s = hashmap_get(m, "xxx");
-        assert_se(streq(s, "bar"));
+        ASSERT_STREQ(s, "bar");
 }
 
 TEST(hashmap_put_strdup_null) {
@@ -139,7 +139,7 @@ TEST(hashmap_put_strdup_null) {
         assert_se(hashmap_contains(m, "foo"));
 
         s = hashmap_get(m, "foo");
-        assert_se(streq(s, "bar"));
+        ASSERT_STREQ(s, "bar");
 
         assert_se(hashmap_put_strdup(&m, "xxx", NULL) == 1);
         assert_se(hashmap_put_strdup(&m, "xxx", "bar") == -EEXIST);
@@ -147,7 +147,7 @@ TEST(hashmap_put_strdup_null) {
         assert_se(hashmap_contains(m, "xxx"));
 
         s = hashmap_get(m, "xxx");
-        assert_se(s == NULL);
+        ASSERT_NULL(s);
 }
 
 /* This file tests in test-hashmap-plain.c, and tests in test-hashmap-ordered.c, which is generated
index c5abf54513aaaf59abe4aec18df6fd9fdbe5363a..5c39fc7cc2269c8d423d703e4423087c9603bd59 100644 (file)
@@ -80,7 +80,7 @@ static void test_hexmem_one(const char *in, const char *expected) {
 
         assert_se(result = hexmem(in, strlen_ptr(in)));
         log_debug("hexmem(\"%s\") → \"%s\" (expected: \"%s\")", strnull(in), result, expected);
-        assert_se(streq(result, expected));
+        ASSERT_STREQ(result, expected);
 
         assert_se(unhexmem(result, &mem, &len) >= 0);
         assert_se(memcmp_safe(mem, in, len) == 0);
@@ -106,7 +106,7 @@ static void test_unhexmem_one(const char *s, size_t l, int retval) {
 
                 assert_se(hex = hexmem(mem, len));
                 answer = strndupa_safe(strempty(s), l);
-                assert_se(streq(delete_chars(answer, WHITESPACE), hex));
+                ASSERT_STREQ(delete_chars(answer, WHITESPACE), hex);
         }
 }
 
@@ -134,72 +134,72 @@ TEST(base32hexmem) {
 
         b32 = base32hexmem("", STRLEN(""), true);
         assert_se(b32);
-        assert_se(streq(b32, ""));
+        ASSERT_STREQ(b32, "");
         free(b32);
 
         b32 = base32hexmem("f", STRLEN("f"), true);
         assert_se(b32);
-        assert_se(streq(b32, "CO======"));
+        ASSERT_STREQ(b32, "CO======");
         free(b32);
 
         b32 = base32hexmem("fo", STRLEN("fo"), true);
         assert_se(b32);
-        assert_se(streq(b32, "CPNG===="));
+        ASSERT_STREQ(b32, "CPNG====");
         free(b32);
 
         b32 = base32hexmem("foo", STRLEN("foo"), true);
         assert_se(b32);
-        assert_se(streq(b32, "CPNMU==="));
+        ASSERT_STREQ(b32, "CPNMU===");
         free(b32);
 
         b32 = base32hexmem("foob", STRLEN("foob"), true);
         assert_se(b32);
-        assert_se(streq(b32, "CPNMUOG="));
+        ASSERT_STREQ(b32, "CPNMUOG=");
         free(b32);
 
         b32 = base32hexmem("fooba", STRLEN("fooba"), true);
         assert_se(b32);
-        assert_se(streq(b32, "CPNMUOJ1"));
+        ASSERT_STREQ(b32, "CPNMUOJ1");
         free(b32);
 
         b32 = base32hexmem("foobar", STRLEN("foobar"), true);
         assert_se(b32);
-        assert_se(streq(b32, "CPNMUOJ1E8======"));
+        ASSERT_STREQ(b32, "CPNMUOJ1E8======");
         free(b32);
 
         b32 = base32hexmem("", STRLEN(""), false);
         assert_se(b32);
-        assert_se(streq(b32, ""));
+        ASSERT_STREQ(b32, "");
         free(b32);
 
         b32 = base32hexmem("f", STRLEN("f"), false);
         assert_se(b32);
-        assert_se(streq(b32, "CO"));
+        ASSERT_STREQ(b32, "CO");
         free(b32);
 
         b32 = base32hexmem("fo", STRLEN("fo"), false);
         assert_se(b32);
-        assert_se(streq(b32, "CPNG"));
+        ASSERT_STREQ(b32, "CPNG");
         free(b32);
 
         b32 = base32hexmem("foo", STRLEN("foo"), false);
         assert_se(b32);
-        assert_se(streq(b32, "CPNMU"));
+        ASSERT_STREQ(b32, "CPNMU");
         free(b32);
 
         b32 = base32hexmem("foob", STRLEN("foob"), false);
         assert_se(b32);
-        assert_se(streq(b32, "CPNMUOG"));
+        ASSERT_STREQ(b32, "CPNMUOG");
         free(b32);
 
         b32 = base32hexmem("fooba", STRLEN("fooba"), false);
         assert_se(b32);
-        assert_se(streq(b32, "CPNMUOJ1"));
+        ASSERT_STREQ(b32, "CPNMUOJ1");
         free(b32);
 
         b32 = base32hexmem("foobar", STRLEN("foobar"), false);
         assert_se(b32);
-        assert_se(streq(b32, "CPNMUOJ1E8"));
+        ASSERT_STREQ(b32, "CPNMUOJ1E8");
         free(b32);
 }
 
@@ -212,7 +212,7 @@ static void test_unbase32hexmem_one(const char *hex, bool padding, int retval, c
                 char *str;
 
                 str = strndupa_safe(mem, len);
-                assert_se(streq(str, ans));
+                ASSERT_STREQ(str, ans);
         }
 }
 
@@ -268,31 +268,31 @@ TEST(base64mem) {
         char *b64;
 
         assert_se(base64mem("", STRLEN(""), &b64) == 0);
-        assert_se(streq(b64, ""));
+        ASSERT_STREQ(b64, "");
         free(b64);
 
         assert_se(base64mem("f", STRLEN("f"), &b64) == 4);
-        assert_se(streq(b64, "Zg=="));
+        ASSERT_STREQ(b64, "Zg==");
         free(b64);
 
         assert_se(base64mem("fo", STRLEN("fo"), &b64) == 4);
-        assert_se(streq(b64, "Zm8="));
+        ASSERT_STREQ(b64, "Zm8=");
         free(b64);
 
         assert_se(base64mem("foo", STRLEN("foo"), &b64) == 4);
-        assert_se(streq(b64, "Zm9v"));
+        ASSERT_STREQ(b64, "Zm9v");
         free(b64);
 
         assert_se(base64mem("foob", STRLEN("foob"), &b64) == 8);
-        assert_se(streq(b64, "Zm9vYg=="));
+        ASSERT_STREQ(b64, "Zm9vYg==");
         free(b64);
 
         assert_se(base64mem("fooba", STRLEN("fooba"), &b64) == 8);
-        assert_se(streq(b64, "Zm9vYmE="));
+        ASSERT_STREQ(b64, "Zm9vYmE=");
         free(b64);
 
         assert_se(base64mem("foobar", STRLEN("foobar"), &b64) == 8);
-        assert_se(streq(b64, "Zm9vYmFy"));
+        ASSERT_STREQ(b64, "Zm9vYmFy");
         free(b64);
 }
 
@@ -341,7 +341,7 @@ static void test_base64_append_one(char **buf, size_t *len, const char *in, cons
         assert_se(new_len >= 0);
         log_debug("base64_append_one(\"%s\")\nresult:\n%s\nexpected:\n%s", in, strnull(*buf), strnull(expected));
         assert_se((size_t) new_len == strlen_ptr(*buf));
-        assert_se(streq_ptr(*buf, expected));
+        ASSERT_STREQ(*buf, expected);
         *len = new_len;
 }
 
index 18c81adbcdbfbdb9df3a792be946561f11c10597..28f7ab9460e6503a13fe10fd460a7328965a067e 100644 (file)
@@ -26,42 +26,42 @@ TEST(hmac) {
                               "baldohaldo",
                               result);
         hex_result = hexmem(result, sizeof(result));
-        assert_se(streq_ptr(hex_result, "c47ad5031ba21605e52c6ca68090d66a2dd5ccf84efa4bace15361a8cba63cda"));
+        ASSERT_STREQ(hex_result, "c47ad5031ba21605e52c6ca68090d66a2dd5ccf84efa4bace15361a8cba63cda");
         hex_result = mfree(hex_result);
 
         hmac_sha256_by_string("waldo",
                               "baldo haldo",
                               result);
         hex_result = hexmem(result, sizeof(result));
-        assert_se(streq_ptr(hex_result, "4e8974ad6c08b98cc2519cd1e27aa7195769fcf86db1dd7ceaab4d44c490ad69"));
+        ASSERT_STREQ(hex_result, "4e8974ad6c08b98cc2519cd1e27aa7195769fcf86db1dd7ceaab4d44c490ad69");
         hex_result = mfree(hex_result);
 
         hmac_sha256_by_string("waldo",
                               "baldo 4e8974ad6c08b98cc2519cd1e27aa7195769fcf86db1dd7ceaab4d44c490ad69 haldo",
                               result);
         hex_result = hexmem(result, sizeof(result));
-        assert_se(streq_ptr(hex_result, "039f3df430b19753ffb493e5b90708f75c5210b63c6bcbef3374eb3f0a3f97f7"));
+        ASSERT_STREQ(hex_result, "039f3df430b19753ffb493e5b90708f75c5210b63c6bcbef3374eb3f0a3f97f7");
         hex_result = mfree(hex_result);
 
         hmac_sha256_by_string("4e8974ad6c08b98cc2519cd1e27aa7195769fcf86db1dd7ceaab4d44c490ad69",
                               "baldo haldo",
                               result);
         hex_result = hexmem(result, sizeof(result));
-        assert_se(streq_ptr(hex_result, "c4cfaf48077cbb0bbd177a09e59ec4c248f4ca771503410f5b54b98d88d2f47b"));
+        ASSERT_STREQ(hex_result, "c4cfaf48077cbb0bbd177a09e59ec4c248f4ca771503410f5b54b98d88d2f47b");
         hex_result = mfree(hex_result);
 
         hmac_sha256_by_string("4e8974ad6c08b98cc2519cd1e27aa7195769fcf86db1dd7ceaab4d44c490ad69",
                               "supercalifragilisticexpialidocious",
                               result);
         hex_result = hexmem(result, sizeof(result));
-        assert_se(streq_ptr(hex_result, "2c059e7a63c4c3b23f47966a65fd2f8a2f5d7161e2e90d78ff68866b5c375cb7"));
+        ASSERT_STREQ(hex_result, "2c059e7a63c4c3b23f47966a65fd2f8a2f5d7161e2e90d78ff68866b5c375cb7");
         hex_result = mfree(hex_result);
 
         hmac_sha256_by_string("4e8974ad6c08b98cc2519cd1e27aa7195769fcf86db1dd7ceaab4d44c490ad69c47ad5031ba21605e52c6ca68090d66a2dd5ccf84efa4bace15361a8cba63cda",
                               "supercalifragilisticexpialidocious",
                               result);
         hex_result = hexmem(result, sizeof(result));
-        assert_se(streq_ptr(hex_result, "1dd1d1d45b9d9f9673dc9983c968c46ff3168e03cfeb4156a219eba1af4cff5f"));
+        ASSERT_STREQ(hex_result, "1dd1d1d45b9d9f9673dc9983c968c46ff3168e03cfeb4156a219eba1af4cff5f");
         hex_result = mfree(hex_result);
 }
 
index 94e5ece5602aa155d04d0f5a239f14f1c740d162..2365a5edc1102b64eae044bd83c299993b7bebc7 100644 (file)
@@ -22,28 +22,28 @@ TEST(read_etc_hostname) {
         /* simple hostname */
         assert_se(write_string_file(path, "foo", WRITE_STRING_FILE_CREATE) == 0);
         assert_se(read_etc_hostname(path, &hostname) == 0);
-        assert_se(streq(hostname, "foo"));
+        ASSERT_STREQ(hostname, "foo");
         hostname = mfree(hostname);
 
         /* with comment */
         assert_se(write_string_file(path, "# comment\nfoo", WRITE_STRING_FILE_CREATE) == 0);
         assert_se(read_etc_hostname(path, &hostname) == 0);
         assert_se(hostname);
-        assert_se(streq(hostname, "foo"));
+        ASSERT_STREQ(hostname, "foo");
         hostname = mfree(hostname);
 
         /* with comment and extra whitespace */
         assert_se(write_string_file(path, "# comment\n\n foo ", WRITE_STRING_FILE_CREATE) == 0);
         assert_se(read_etc_hostname(path, &hostname) == 0);
         assert_se(hostname);
-        assert_se(streq(hostname, "foo"));
+        ASSERT_STREQ(hostname, "foo");
         hostname = mfree(hostname);
 
         /* cleans up name */
         assert_se(write_string_file(path, "!foo/bar.com", WRITE_STRING_FILE_CREATE) == 0);
         assert_se(read_etc_hostname(path, &hostname) == 0);
         assert_se(hostname);
-        assert_se(streq(hostname, "foobar.com"));
+        ASSERT_STREQ(hostname, "foobar.com");
         hostname = mfree(hostname);
 
         /* no value set */
index 77e9a1992f24401eff10a5225da1011038f436f4..a7eccf8b35bf9996b92fb6c0a3fcab1a3fa66886 100644 (file)
@@ -50,27 +50,27 @@ TEST(hostname_cleanup) {
         char *s;
 
         s = strdupa_safe("foobar");
-        assert_se(streq(hostname_cleanup(s), "foobar"));
+        ASSERT_STREQ(hostname_cleanup(s), "foobar");
         s = strdupa_safe("foobar.com");
-        assert_se(streq(hostname_cleanup(s), "foobar.com"));
+        ASSERT_STREQ(hostname_cleanup(s), "foobar.com");
         s = strdupa_safe("foobar.com.");
-        assert_se(streq(hostname_cleanup(s), "foobar.com"));
+        ASSERT_STREQ(hostname_cleanup(s), "foobar.com");
         s = strdupa_safe("foo-bar.-com-.");
-        assert_se(streq(hostname_cleanup(s), "foo-bar.com"));
+        ASSERT_STREQ(hostname_cleanup(s), "foo-bar.com");
         s = strdupa_safe("foo-bar-.-com-.");
-        assert_se(streq(hostname_cleanup(s), "foo-bar--com"));
+        ASSERT_STREQ(hostname_cleanup(s), "foo-bar--com");
         s = strdupa_safe("--foo-bar.-com");
-        assert_se(streq(hostname_cleanup(s), "foo-bar.com"));
+        ASSERT_STREQ(hostname_cleanup(s), "foo-bar.com");
         s = strdupa_safe("fooBAR");
-        assert_se(streq(hostname_cleanup(s), "fooBAR"));
+        ASSERT_STREQ(hostname_cleanup(s), "fooBAR");
         s = strdupa_safe("fooBAR.com");
-        assert_se(streq(hostname_cleanup(s), "fooBAR.com"));
+        ASSERT_STREQ(hostname_cleanup(s), "fooBAR.com");
         s = strdupa_safe("fooBAR.");
-        assert_se(streq(hostname_cleanup(s), "fooBAR"));
+        ASSERT_STREQ(hostname_cleanup(s), "fooBAR");
         s = strdupa_safe("fooBAR.com.");
-        assert_se(streq(hostname_cleanup(s), "fooBAR.com"));
+        ASSERT_STREQ(hostname_cleanup(s), "fooBAR.com");
         s = strdupa_safe("fööbar");
-        assert_se(streq(hostname_cleanup(s), "fbar"));
+        ASSERT_STREQ(hostname_cleanup(s), "fbar");
         s = strdupa_safe("");
         assert_se(isempty(hostname_cleanup(s)));
         s = strdupa_safe(".");
@@ -78,17 +78,17 @@ TEST(hostname_cleanup) {
         s = strdupa_safe("..");
         assert_se(isempty(hostname_cleanup(s)));
         s = strdupa_safe("foobar.");
-        assert_se(streq(hostname_cleanup(s), "foobar"));
+        ASSERT_STREQ(hostname_cleanup(s), "foobar");
         s = strdupa_safe(".foobar");
-        assert_se(streq(hostname_cleanup(s), "foobar"));
+        ASSERT_STREQ(hostname_cleanup(s), "foobar");
         s = strdupa_safe("foo..bar");
-        assert_se(streq(hostname_cleanup(s), "foo.bar"));
+        ASSERT_STREQ(hostname_cleanup(s), "foo.bar");
         s = strdupa_safe("foo.bar..");
-        assert_se(streq(hostname_cleanup(s), "foo.bar"));
+        ASSERT_STREQ(hostname_cleanup(s), "foo.bar");
         s = strdupa_safe("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
-        assert_se(streq(hostname_cleanup(s), "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"));
+        ASSERT_STREQ(hostname_cleanup(s), "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
         s = strdupa_safe("xxxx........xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
-        assert_se(streq(hostname_cleanup(s), "xxxx.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"));
+        ASSERT_STREQ(hostname_cleanup(s), "xxxx.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
 }
 
 TEST(hostname_malloc) {
index f1159ecb15e7d0976c533cd6df1b4c8cb2737a74..3ddbeec0fc78c509cc0f302b8468d33b4ab627d6 100644 (file)
@@ -50,19 +50,19 @@ TEST(id128) {
         }
 
         printf("waldi: %s\n", sd_id128_to_string(ID128_WALDI, t));
-        assert_se(streq(t, STR_WALDI));
+        ASSERT_STREQ(t, STR_WALDI);
 
         assert_se(asprintf(&b, SD_ID128_FORMAT_STR, SD_ID128_FORMAT_VAL(ID128_WALDI)) == 32);
         printf("waldi2: %s\n", b);
-        assert_se(streq(t, b));
+        ASSERT_STREQ(t, b);
 
         printf("waldi3: %s\n", sd_id128_to_uuid_string(ID128_WALDI, q));
-        assert_se(streq(q, UUID_WALDI));
+        ASSERT_STREQ(q, UUID_WALDI);
 
         b = mfree(b);
         assert_se(asprintf(&b, SD_ID128_UUID_FORMAT_STR, SD_ID128_FORMAT_VAL(ID128_WALDI)) == 36);
         printf("waldi4: %s\n", b);
-        assert_se(streq(q, b));
+        ASSERT_STREQ(q, b);
 
         assert_se(sd_id128_from_string(STR_WALDI, &id) >= 0);
         assert_se(sd_id128_equal(id, ID128_WALDI));
index 1572093c9dce2d168eb3e9ffc0d64886ec4a4991..12fc10acf0d31d674eeb930b1ef107bae8abf3fe 100644 (file)
@@ -130,4 +130,36 @@ TEST(extend) {
         assert_se(partition_policy_flags_extend(PARTITION_POLICY_GROWFS_ON) == (PARTITION_POLICY_GROWFS_ON|_PARTITION_POLICY_USE_MASK|_PARTITION_POLICY_READ_ONLY_MASK));
 }
 
+static void test_policy_intersect_one(const char *a, const char *b, const char *c) {
+        _cleanup_(image_policy_freep) ImagePolicy *x = NULL, *y = NULL, *z = NULL, *t = NULL;
+
+        assert_se(image_policy_from_string(a, &x) >= 0);
+        assert_se(image_policy_from_string(b, &y) >= 0);
+        assert_se(image_policy_from_string(c, &z) >= 0);
+
+        assert_se(image_policy_intersect(x, y, &t) >= 0);
+
+        _cleanup_free_ char *s1 = NULL, *s2 = NULL, *s3 = NULL, *s4 = NULL;
+        assert_se(image_policy_to_string(x, false, &s1) >= 0);
+        assert_se(image_policy_to_string(y, false, &s2) >= 0);
+        assert_se(image_policy_to_string(z, false, &s3) >= 0);
+        assert_se(image_policy_to_string(t, false, &s4) >= 0);
+
+        log_info("%s ^ %s → %s vs. %s", s1, s2, s3, s4);
+
+        assert_se(image_policy_equivalent(z, t) > 0);
+}
+
+TEST(image_policy_intersect) {
+        test_policy_intersect_one("", "", "");
+        test_policy_intersect_one("-", "-", "-");
+        test_policy_intersect_one("*", "*", "*");
+        test_policy_intersect_one("~", "~", "~");
+        test_policy_intersect_one("root=verity+signed", "root=signed+verity", "root=verity+signed");
+        test_policy_intersect_one("root=verity+signed", "root=signed", "root=signed");
+        test_policy_intersect_one("root=verity+signed", "root=verity", "root=verity");
+        test_policy_intersect_one("root=open", "root=verity", "root=verity");
+        test_policy_intersect_one("root=open", "=verity+ignore", "root=verity+ignore:=ignore");
+}
+
 DEFINE_TEST_MAIN(LOG_INFO);
index 7930fe508d99505b43ae7f831fa4109785cf1033..cc7b139655528fc88128e59b5dd3a9bd4060df48 100644 (file)
@@ -10,7 +10,7 @@ static void test_import_url_last_component_one(const char *input, const char *ou
         _cleanup_free_ char *s = NULL;
 
         assert_se(import_url_last_component(input, &s) == ret);
-        assert_se(streq_ptr(output, s));
+        ASSERT_STREQ(output, s);
 }
 
 TEST(import_url_last_component) {
@@ -37,7 +37,7 @@ static void test_import_url_change_suffix_one(const char *input, size_t n, const
         _cleanup_free_ char *s = NULL;
 
         assert_se(import_url_change_suffix(input, n, suffix, &s) == ret);
-        assert_se(streq_ptr(output, s));
+        ASSERT_STREQ(output, s);
 }
 
 TEST(import_url_change_suffix) {
index 661ca8fc81328e89af1a7d90f424ff9a8fad9e2b..2b44babf8dd33611665200810a8cb850083c9008 100644 (file)
@@ -12,8 +12,8 @@ static void test_in_addr_prefix_to_string_one(int f, const char *addr, unsigned
         printf("%s: %s/%u == %s\n", __func__, addr, prefixlen, r);
         assert_se(startswith(r, addr));
 
-        assert_se(streq(r, IN_ADDR_PREFIX_TO_STRING(f, &ua, prefixlen)));
-        assert_se(streq(IN_ADDR_PREFIX_TO_STRING(f, &ua, prefixlen), r));
+        ASSERT_STREQ(r, IN_ADDR_PREFIX_TO_STRING(f, &ua, prefixlen));
+        ASSERT_STREQ(IN_ADDR_PREFIX_TO_STRING(f, &ua, prefixlen), r);
 }
 
 TEST(in_addr_to_string_prefix) {
@@ -35,8 +35,8 @@ static void test_config_parse_in_addr_prefixes_one(int family, const union in_ad
 
         assert_se(config_parse_in_addr_prefixes("unit", "filename", 1, "Service", 1, "IPAddressAllow", 0, str, prefixes, NULL) >= 0);
 
-        assert_se(streq(str, IN_ADDR_PREFIX_TO_STRING(family, addr, prefixlen)));
-        assert_se(streq(IN_ADDR_PREFIX_TO_STRING(family, addr, prefixlen), str));
+        ASSERT_STREQ(str, IN_ADDR_PREFIX_TO_STRING(family, addr, prefixlen));
+        ASSERT_STREQ(IN_ADDR_PREFIX_TO_STRING(family, addr, prefixlen), str);
 }
 
 static void test_config_parse_in_addr_prefixes(Set **ret) {
index 93ab1c5d48feee1b2286adced1ba4cff18ca478a..0d159669402199e50a87ad376d0fbc11cd5b2198 100644 (file)
@@ -81,7 +81,7 @@ static void test_in_addr_prefix_to_string_valid(int family, const char *p) {
         log_info("%s: %s", __func__, p);
 
         assert_se(in_addr_prefix_from_string(p, family, &u, &l) >= 0);
-        assert_se(streq(p, IN_ADDR_PREFIX_TO_STRING(family, &u, l)));
+        ASSERT_STREQ(p, IN_ADDR_PREFIX_TO_STRING(family, &u, l));
 }
 
 static void test_in_addr_prefix_to_string_unoptimized(int family, const char *p) {
@@ -97,7 +97,7 @@ static void test_in_addr_prefix_to_string_unoptimized(int family, const char *p)
         const char *str2 = IN_ADDR_PREFIX_TO_STRING(family, &u2, len2);
         assert_se(str2);
 
-        assert_se(streq(str1, str2));
+        ASSERT_STREQ(str1, str2);
         assert_se(len1 == len2);
         assert_se(in_addr_equal(family, &u1, &u2) > 0);
 }
@@ -340,9 +340,9 @@ static void test_in_addr_to_string_one(int f, const char *addr) {
         assert_se(in_addr_from_string(f, addr, &ua) >= 0);
         assert_se(in_addr_to_string(f, &ua, &r) >= 0);
         printf("%s: %s == %s\n", __func__, addr, r);
-        assert_se(streq(addr, r));
+        ASSERT_STREQ(addr, r);
 
-        assert_se(streq(r, IN_ADDR_TO_STRING(f, &ua)));
+        ASSERT_STREQ(r, IN_ADDR_TO_STRING(f, &ua));
 }
 
 TEST(in_addr_to_string) {
@@ -391,7 +391,7 @@ TEST(in_addr_prefixlen_to_netmask) {
                 assert_se(in_addr_prefixlen_to_netmask(AF_INET, &addr, prefixlen) >= 0);
                 assert_se(in_addr_to_string(AF_INET, &addr, &result) >= 0);
                 printf("test_in_addr_prefixlen_to_netmask: %s == %s\n", ipv4_netmasks[prefixlen], result);
-                assert_se(streq(ipv4_netmasks[prefixlen], result));
+                ASSERT_STREQ(ipv4_netmasks[prefixlen], result);
         }
 
         for (unsigned char prefixlen = 0; prefixlen <= 128; prefixlen++) {
@@ -401,7 +401,7 @@ TEST(in_addr_prefixlen_to_netmask) {
                 assert_se(in_addr_to_string(AF_INET6, &addr, &result) >= 0);
                 printf("test_in_addr_prefixlen_to_netmask: %s\n", result);
                 if (ipv6_netmasks[prefixlen])
-                        assert_se(streq(ipv6_netmasks[prefixlen], result));
+                        ASSERT_STREQ(ipv6_netmasks[prefixlen], result);
         }
 }
 
index c55445079c6c6fc62f19033c4966074b629d2a40..1e7ed27f63b4fb90a7a6710f286d24fe0d8a24f2 100644 (file)
@@ -60,9 +60,9 @@ TEST(basic_mask_and_enable) {
         assert_se(unit_file_mask(RUNTIME_SCOPE_SYSTEM, 0, root, STRV_MAKE("a.service"), &changes, &n_changes) >= 0);
         assert_se(n_changes == 1);
         assert_se(changes[0].type == INSTALL_CHANGE_SYMLINK);
-        assert_se(streq(changes[0].source, "/dev/null"));
+        ASSERT_STREQ(changes[0].source, "/dev/null");
         p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/a.service");
-        assert_se(streq(changes[0].path, p));
+        ASSERT_STREQ(changes[0].path, p);
 
         install_changes_free(changes, n_changes);
         changes = NULL; n_changes = 0;
@@ -81,16 +81,16 @@ TEST(basic_mask_and_enable) {
         assert_se(n_changes == 1);
         assert_se(changes[0].type == INSTALL_CHANGE_UNLINK);
         p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/a.service");
-        assert_se(streq(changes[0].path, p));
+        ASSERT_STREQ(changes[0].path, p);
         install_changes_free(changes, n_changes);
         changes = NULL; n_changes = 0;
 
         assert_se(unit_file_enable(RUNTIME_SCOPE_SYSTEM, 0, root, STRV_MAKE("a.service"), &changes, &n_changes) == 1);
         assert_se(n_changes == 1);
         assert_se(changes[0].type == INSTALL_CHANGE_SYMLINK);
-        assert_se(streq(changes[0].source, "/usr/lib/systemd/system/a.service"));
+        ASSERT_STREQ(changes[0].source, "/usr/lib/systemd/system/a.service");
         p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/a.service");
-        assert_se(streq(changes[0].path, p));
+        ASSERT_STREQ(changes[0].path, p);
         install_changes_free(changes, n_changes);
         changes = NULL; n_changes = 0;
 
@@ -109,7 +109,7 @@ TEST(basic_mask_and_enable) {
         assert_se(n_changes == 1);
         assert_se(changes[0].type == INSTALL_CHANGE_UNLINK);
         p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/a.service");
-        assert_se(streq(changes[0].path, p));
+        ASSERT_STREQ(changes[0].path, p);
         install_changes_free(changes, n_changes);
         changes = NULL; n_changes = 0;
 
@@ -128,9 +128,9 @@ TEST(basic_mask_and_enable) {
         assert_se(unit_file_enable(RUNTIME_SCOPE_SYSTEM, 0, root, STRV_MAKE("d.service"), &changes, &n_changes) >= 0);
         assert_se(n_changes == 1);
         assert_se(changes[0].type == INSTALL_CHANGE_SYMLINK);
-        assert_se(streq(changes[0].source, "/usr/lib/systemd/system/a.service"));
+        ASSERT_STREQ(changes[0].source, "/usr/lib/systemd/system/a.service");
         p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/a.service");
-        assert_se(streq(changes[0].path, p));
+        ASSERT_STREQ(changes[0].path, p);
         install_changes_free(changes, n_changes);
         changes = NULL; n_changes = 0;
 
@@ -145,10 +145,10 @@ TEST(basic_mask_and_enable) {
         assert_se(n_changes == 2);
         assert_se(changes[0].type == INSTALL_CHANGE_UNLINK);
         p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/a.service");
-        assert_se(streq(changes[0].path, p));
+        ASSERT_STREQ(changes[0].path, p);
         assert_se(changes[1].type == INSTALL_CHANGE_SYMLINK);
-        assert_se(streq(changes[1].source, "/usr/lib/systemd/system/a.service"));
-        assert_se(streq(changes[1].path, p));
+        ASSERT_STREQ(changes[1].source, "/usr/lib/systemd/system/a.service");
+        ASSERT_STREQ(changes[1].path, p);
         install_changes_free(changes, n_changes);
         changes = NULL; n_changes = 0;
 
@@ -186,13 +186,13 @@ TEST(basic_mask_and_enable) {
         assert_se(unit_file_enable(RUNTIME_SCOPE_SYSTEM, 0, root, STRV_MAKE("f.service"), &changes, &n_changes) == 1);
         assert_se(n_changes == 2);
         assert_se(changes[0].type == INSTALL_CHANGE_SYMLINK);
-        assert_se(streq(changes[0].source, "/usr/lib/systemd/system/f.service"));
+        ASSERT_STREQ(changes[0].source, "/usr/lib/systemd/system/f.service");
         p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/x.target.wants/f.service");
-        assert_se(streq(changes[0].path, p));
+        ASSERT_STREQ(changes[0].path, p);
         assert_se(changes[1].type == INSTALL_CHANGE_DESTINATION_NOT_PRESENT);
         p = strjoina(root, "/usr/lib/systemd/system/f.service");
-        assert_se(streq(changes[1].source, p));
-        assert_se(streq(changes[1].path, "x.target"));
+        ASSERT_STREQ(changes[1].source, p);
+        ASSERT_STREQ(changes[1].path, "x.target");
         install_changes_free(changes, n_changes);
         changes = NULL; n_changes = 0;
 
@@ -255,9 +255,9 @@ TEST(linked_units) {
         assert_se(unit_file_link(RUNTIME_SCOPE_SYSTEM, 0, root, STRV_MAKE("/opt/linked.service"), &changes, &n_changes) >= 0);
         assert_se(n_changes == 1);
         assert_se(changes[0].type == INSTALL_CHANGE_SYMLINK);
-        assert_se(streq(changes[0].source, "/opt/linked.service"));
+        ASSERT_STREQ(changes[0].source, "/opt/linked.service");
         p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/linked.service");
-        assert_se(streq(changes[0].path, p));
+        ASSERT_STREQ(changes[0].path, p);
         install_changes_free(changes, n_changes);
         changes = NULL; n_changes = 0;
 
@@ -268,7 +268,7 @@ TEST(linked_units) {
         assert_se(n_changes == 1);
         assert_se(changes[0].type == INSTALL_CHANGE_UNLINK);
         p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/linked.service");
-        assert_se(streq(changes[0].path, p));
+        ASSERT_STREQ(changes[0].path, p);
         install_changes_free(changes, n_changes);
         changes = NULL; n_changes = 0;
 
@@ -285,9 +285,9 @@ TEST(linked_units) {
 
                 if (s && streq(changes[i].path, s))
                         /* The alias symlink should point within the search path. */
-                        assert_se(streq(changes[i].source, SYSTEM_CONFIG_UNIT_DIR"/linked.service"));
+                        ASSERT_STREQ(changes[i].source, SYSTEM_CONFIG_UNIT_DIR"/linked.service");
                 else
-                        assert_se(streq(changes[i].source, "/opt/linked.service"));
+                        ASSERT_STREQ(changes[i].source, "/opt/linked.service");
 
                 if (p && streq(changes[i].path, p))
                         p = NULL;
@@ -335,7 +335,7 @@ TEST(linked_units) {
         q = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/linked2.service");
         for (i = 0 ; i < n_changes; i++) {
                 assert_se(changes[i].type == INSTALL_CHANGE_SYMLINK);
-                assert_se(streq(changes[i].source, "/opt/linked2.service"));
+                ASSERT_STREQ(changes[i].source, "/opt/linked2.service");
 
                 if (p && streq(changes[i].path, p))
                         p = NULL;
@@ -353,7 +353,7 @@ TEST(linked_units) {
         assert_se(changes[0].type == INSTALL_CHANGE_SYMLINK);
         assert_se(startswith(changes[0].path, root));
         assert_se(endswith(changes[0].path, "linked3.service"));
-        assert_se(streq(changes[0].source, "/opt/linked3.service"));
+        ASSERT_STREQ(changes[0].source, "/opt/linked3.service");
         install_changes_free(changes, n_changes);
         changes = NULL; n_changes = 0;
 }
@@ -375,7 +375,7 @@ TEST(default) {
         assert_se(unit_file_set_default(RUNTIME_SCOPE_SYSTEM, 0, root, "idontexist.target", &changes, &n_changes) == -ENOENT);
         assert_se(n_changes == 1);
         assert_se(changes[0].type == -ENOENT);
-        assert_se(streq_ptr(changes[0].path, "idontexist.target"));
+        ASSERT_STREQ(changes[0].path, "idontexist.target");
         install_changes_free(changes, n_changes);
         changes = NULL; n_changes = 0;
 
@@ -384,14 +384,14 @@ TEST(default) {
         assert_se(unit_file_set_default(RUNTIME_SCOPE_SYSTEM, 0, root, "test-default.target", &changes, &n_changes) >= 0);
         assert_se(n_changes == 1);
         assert_se(changes[0].type == INSTALL_CHANGE_SYMLINK);
-        assert_se(streq(changes[0].source, "/usr/lib/systemd/system/test-default-real.target"));
+        ASSERT_STREQ(changes[0].source, "/usr/lib/systemd/system/test-default-real.target");
         p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR "/" SPECIAL_DEFAULT_TARGET);
-        assert_se(streq(changes[0].path, p));
+        ASSERT_STREQ(changes[0].path, p);
         install_changes_free(changes, n_changes);
         changes = NULL; n_changes = 0;
 
         assert_se(unit_file_get_default(RUNTIME_SCOPE_SYSTEM, root, &def) >= 0);
-        assert_se(streq_ptr(def, "test-default-real.target"));
+        ASSERT_STREQ(def, "test-default-real.target");
 }
 
 TEST(add_dependency) {
@@ -414,9 +414,9 @@ TEST(add_dependency) {
         assert_se(unit_file_add_dependency(RUNTIME_SCOPE_SYSTEM, 0, root, STRV_MAKE("add-dependency-test-service.service"), "add-dependency-test-target.target", UNIT_WANTS, &changes, &n_changes) >= 0);
         assert_se(n_changes == 1);
         assert_se(changes[0].type == INSTALL_CHANGE_SYMLINK);
-        assert_se(streq(changes[0].source, "/usr/lib/systemd/system/real-add-dependency-test-service.service"));
+        ASSERT_STREQ(changes[0].source, "/usr/lib/systemd/system/real-add-dependency-test-service.service");
         p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/real-add-dependency-test-target.target.wants/real-add-dependency-test-service.service");
-        assert_se(streq(changes[0].path, p));
+        ASSERT_STREQ(changes[0].path, p);
         install_changes_free(changes, n_changes);
         changes = NULL; n_changes = 0;
 }
@@ -455,9 +455,9 @@ TEST(template_enable) {
         assert_se(unit_file_enable(RUNTIME_SCOPE_SYSTEM, 0, root, STRV_MAKE("template@.service"), &changes, &n_changes) >= 0);
         assert_se(n_changes == 1);
         assert_se(changes[0].type == INSTALL_CHANGE_SYMLINK);
-        assert_se(streq(changes[0].source, "/usr/lib/systemd/system/template@.service"));
+        ASSERT_STREQ(changes[0].source, "/usr/lib/systemd/system/template@.service");
         p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/template@def.service");
-        assert_se(streq(changes[0].path, p));
+        ASSERT_STREQ(changes[0].path, p);
         install_changes_free(changes, n_changes);
         changes = NULL; n_changes = 0;
 
@@ -471,7 +471,7 @@ TEST(template_enable) {
         assert_se(unit_file_disable(RUNTIME_SCOPE_SYSTEM, 0, root, STRV_MAKE("template@.service"), &changes, &n_changes) >= 0);
         assert_se(n_changes == 1);
         assert_se(changes[0].type == INSTALL_CHANGE_UNLINK);
-        assert_se(streq(changes[0].path, p));
+        ASSERT_STREQ(changes[0].path, p);
         install_changes_free(changes, n_changes);
         changes = NULL; n_changes = 0;
 
@@ -486,9 +486,9 @@ TEST(template_enable) {
 
         assert_se(unit_file_enable(RUNTIME_SCOPE_SYSTEM, 0, root, STRV_MAKE("template@foo.service"), &changes, &n_changes) >= 0);
         assert_se(changes[0].type == INSTALL_CHANGE_SYMLINK);
-        assert_se(streq(changes[0].source, "/usr/lib/systemd/system/template@.service"));
+        ASSERT_STREQ(changes[0].source, "/usr/lib/systemd/system/template@.service");
         p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/template@foo.service");
-        assert_se(streq(changes[0].path, p));
+        ASSERT_STREQ(changes[0].path, p);
         install_changes_free(changes, n_changes);
         changes = NULL; n_changes = 0;
 
@@ -502,7 +502,7 @@ TEST(template_enable) {
         assert_se(unit_file_disable(RUNTIME_SCOPE_SYSTEM, 0, root, STRV_MAKE("template@foo.service"), &changes, &n_changes) >= 0);
         assert_se(n_changes == 1);
         assert_se(changes[0].type == INSTALL_CHANGE_UNLINK);
-        assert_se(streq(changes[0].path, p));
+        ASSERT_STREQ(changes[0].path, p);
         install_changes_free(changes, n_changes);
         changes = NULL; n_changes = 0;
 
@@ -519,9 +519,9 @@ TEST(template_enable) {
 
         assert_se(unit_file_enable(RUNTIME_SCOPE_SYSTEM, 0, root, STRV_MAKE("template-symlink@quux.service"), &changes, &n_changes) >= 0);
         assert_se(changes[0].type == INSTALL_CHANGE_SYMLINK);
-        assert_se(streq(changes[0].source, "/usr/lib/systemd/system/template@.service"));
+        ASSERT_STREQ(changes[0].source, "/usr/lib/systemd/system/template@.service");
         p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/template@quux.service");
-        assert_se(streq(changes[0].path, p));
+        ASSERT_STREQ(changes[0].path, p);
         install_changes_free(changes, n_changes);
         changes = NULL; n_changes = 0;
 
@@ -565,9 +565,9 @@ TEST(indirect) {
         assert_se(unit_file_enable(RUNTIME_SCOPE_SYSTEM, 0, root, STRV_MAKE("indirectc.service"), &changes, &n_changes) >= 0);
         assert_se(n_changes == 1);
         assert_se(changes[0].type == INSTALL_CHANGE_SYMLINK);
-        assert_se(streq(changes[0].source, "/usr/lib/systemd/system/indirectb.service"));
+        ASSERT_STREQ(changes[0].source, "/usr/lib/systemd/system/indirectb.service");
         p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/indirectb.service");
-        assert_se(streq(changes[0].path, p));
+        ASSERT_STREQ(changes[0].path, p);
         install_changes_free(changes, n_changes);
         changes = NULL; n_changes = 0;
 
@@ -579,7 +579,7 @@ TEST(indirect) {
         assert_se(n_changes == 1);
         assert_se(changes[0].type == INSTALL_CHANGE_UNLINK);
         p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/indirectb.service");
-        assert_se(streq(changes[0].path, p));
+        ASSERT_STREQ(changes[0].path, p);
         install_changes_free(changes, n_changes);
         changes = NULL; n_changes = 0;
 }
@@ -627,9 +627,9 @@ TEST(preset_and_list) {
         assert_se(unit_file_preset(RUNTIME_SCOPE_SYSTEM, 0, root, STRV_MAKE("preset-yes.service"), UNIT_FILE_PRESET_FULL, &changes, &n_changes) >= 0);
         assert_se(n_changes == 1);
         assert_se(changes[0].type == INSTALL_CHANGE_SYMLINK);
-        assert_se(streq(changes[0].source, "/usr/lib/systemd/system/preset-yes.service"));
+        ASSERT_STREQ(changes[0].source, "/usr/lib/systemd/system/preset-yes.service");
         p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/preset-yes.service");
-        assert_se(streq(changes[0].path, p));
+        ASSERT_STREQ(changes[0].path, p);
         install_changes_free(changes, n_changes);
         changes = NULL; n_changes = 0;
 
@@ -641,7 +641,7 @@ TEST(preset_and_list) {
         assert_se(n_changes == 1);
         assert_se(changes[0].type == INSTALL_CHANGE_UNLINK);
         p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/preset-yes.service");
-        assert_se(streq(changes[0].path, p));
+        ASSERT_STREQ(changes[0].path, p);
         install_changes_free(changes, n_changes);
         changes = NULL; n_changes = 0;
 
@@ -667,8 +667,8 @@ TEST(preset_and_list) {
         for (i = 0; i < n_changes; i++) {
 
                 if (changes[i].type == INSTALL_CHANGE_SYMLINK) {
-                        assert_se(streq(changes[i].source, "/usr/lib/systemd/system/preset-yes.service"));
-                        assert_se(streq(changes[i].path, p));
+                        ASSERT_STREQ(changes[i].source, "/usr/lib/systemd/system/preset-yes.service");
+                        ASSERT_STREQ(changes[i].path, p);
                 } else
                         assert_se(changes[i].type == INSTALL_CHANGE_UNLINK);
         }
@@ -735,7 +735,7 @@ TEST(revert) {
         assert_se(unit_file_revert(RUNTIME_SCOPE_SYSTEM, root, STRV_MAKE("xx.service"), &changes, &n_changes) >= 0);
         assert_se(n_changes == 1);
         assert_se(changes[0].type == INSTALL_CHANGE_UNLINK);
-        assert_se(streq(changes[0].path, p));
+        ASSERT_STREQ(changes[0].path, p);
         install_changes_free(changes, n_changes);
         changes = NULL; n_changes = 0;
 
@@ -746,11 +746,11 @@ TEST(revert) {
         assert_se(unit_file_revert(RUNTIME_SCOPE_SYSTEM, root, STRV_MAKE("xx.service"), &changes, &n_changes) >= 0);
         assert_se(n_changes == 2);
         assert_se(changes[0].type == INSTALL_CHANGE_UNLINK);
-        assert_se(streq(changes[0].path, p));
+        ASSERT_STREQ(changes[0].path, p);
 
         p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/xx.service.d");
         assert_se(changes[1].type == INSTALL_CHANGE_UNLINK);
-        assert_se(streq(changes[1].path, p));
+        ASSERT_STREQ(changes[1].path, p);
         install_changes_free(changes, n_changes);
         changes = NULL; n_changes = 0;
 }
@@ -786,9 +786,9 @@ TEST(preset_order) {
         assert_se(unit_file_preset(RUNTIME_SCOPE_SYSTEM, 0, root, STRV_MAKE("prefix-1.service"), UNIT_FILE_PRESET_FULL, &changes, &n_changes) >= 0);
         assert_se(n_changes == 1);
         assert_se(changes[0].type == INSTALL_CHANGE_SYMLINK);
-        assert_se(streq(changes[0].source, "/usr/lib/systemd/system/prefix-1.service"));
+        ASSERT_STREQ(changes[0].source, "/usr/lib/systemd/system/prefix-1.service");
         p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/prefix-1.service");
-        assert_se(streq(changes[0].path, p));
+        ASSERT_STREQ(changes[0].path, p);
         install_changes_free(changes, n_changes);
         changes = NULL; n_changes = 0;
 
@@ -895,12 +895,12 @@ TEST(with_dropin) {
         assert_se(n_changes == 2);
         assert_se(changes[0].type == INSTALL_CHANGE_SYMLINK);
         assert_se(changes[1].type == INSTALL_CHANGE_SYMLINK);
-        assert_se(streq(changes[0].source, "/usr/lib/systemd/system/with-dropin-1.service"));
-        assert_se(streq(changes[1].source, "/usr/lib/systemd/system/with-dropin-1.service"));
+        ASSERT_STREQ(changes[0].source, "/usr/lib/systemd/system/with-dropin-1.service");
+        ASSERT_STREQ(changes[1].source, "/usr/lib/systemd/system/with-dropin-1.service");
         p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/with-dropin-1.service");
-        assert_se(streq(changes[0].path, p));
+        ASSERT_STREQ(changes[0].path, p);
         p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/graphical.target.wants/with-dropin-1.service");
-        assert_se(streq(changes[1].path, p));
+        ASSERT_STREQ(changes[1].path, p);
         install_changes_free(changes, n_changes);
         changes = NULL; n_changes = 0;
 
@@ -909,12 +909,12 @@ TEST(with_dropin) {
         assert_se(n_changes == 2);
         assert_se(changes[0].type == INSTALL_CHANGE_SYMLINK);
         assert_se(changes[1].type == INSTALL_CHANGE_SYMLINK);
-        assert_se(streq(changes[0].source, SYSTEM_CONFIG_UNIT_DIR"/with-dropin-2.service"));
-        assert_se(streq(changes[1].source, SYSTEM_CONFIG_UNIT_DIR"/with-dropin-2.service"));
+        ASSERT_STREQ(changes[0].source, SYSTEM_CONFIG_UNIT_DIR"/with-dropin-2.service");
+        ASSERT_STREQ(changes[1].source, SYSTEM_CONFIG_UNIT_DIR"/with-dropin-2.service");
         p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/with-dropin-2.service");
-        assert_se(streq(changes[0].path, p));
+        ASSERT_STREQ(changes[0].path, p);
         p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/graphical.target.wants/with-dropin-2.service");
-        assert_se(streq(changes[1].path, p));
+        ASSERT_STREQ(changes[1].path, p);
         install_changes_free(changes, n_changes);
         changes = NULL; n_changes = 0;
 
@@ -923,12 +923,12 @@ TEST(with_dropin) {
         assert_se(n_changes == 2);
         assert_se(changes[0].type == INSTALL_CHANGE_SYMLINK);
         assert_se(changes[1].type == INSTALL_CHANGE_SYMLINK);
-        assert_se(streq(changes[0].source, "/usr/lib/systemd/system/with-dropin-3.service"));
-        assert_se(streq(changes[1].source, "/usr/lib/systemd/system/with-dropin-3.service"));
+        ASSERT_STREQ(changes[0].source, "/usr/lib/systemd/system/with-dropin-3.service");
+        ASSERT_STREQ(changes[1].source, "/usr/lib/systemd/system/with-dropin-3.service");
         p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/with-dropin-3.service");
-        assert_se(streq(changes[0].path, p));
+        ASSERT_STREQ(changes[0].path, p);
         p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/graphical.target.wants/with-dropin-3.service");
-        assert_se(streq(changes[1].path, p));
+        ASSERT_STREQ(changes[1].path, p);
         install_changes_free(changes, n_changes);
         changes = NULL; n_changes = 0;
 
@@ -937,12 +937,12 @@ TEST(with_dropin) {
         assert_se(n_changes == 2);
         assert_se(changes[0].type == INSTALL_CHANGE_SYMLINK);
         assert_se(changes[1].type == INSTALL_CHANGE_SYMLINK);
-        assert_se(streq(changes[0].source, "/usr/lib/systemd/system/with-dropin-4a.service"));
-        assert_se(streq(changes[1].source, "/usr/lib/systemd/system/with-dropin-4b.service"));
+        ASSERT_STREQ(changes[0].source, "/usr/lib/systemd/system/with-dropin-4a.service");
+        ASSERT_STREQ(changes[1].source, "/usr/lib/systemd/system/with-dropin-4b.service");
         p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/with-dropin-4a.service");
-        assert_se(streq(changes[0].path, p));
+        ASSERT_STREQ(changes[0].path, p);
         p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/with-dropin-4b.service");
-        assert_se(streq(changes[1].path, p));
+        ASSERT_STREQ(changes[1].path, p);
         install_changes_free(changes, n_changes);
         changes = NULL; n_changes = 0;
 
@@ -1004,12 +1004,12 @@ TEST(with_dropin_template) {
         assert_se(n_changes == 2);
         assert_se(changes[0].type == INSTALL_CHANGE_SYMLINK);
         assert_se(changes[1].type == INSTALL_CHANGE_SYMLINK);
-        assert_se(streq(changes[0].source, "/usr/lib/systemd/system/with-dropin-1@.service"));
-        assert_se(streq(changes[1].source, "/usr/lib/systemd/system/with-dropin-1@.service"));
+        ASSERT_STREQ(changes[0].source, "/usr/lib/systemd/system/with-dropin-1@.service");
+        ASSERT_STREQ(changes[1].source, "/usr/lib/systemd/system/with-dropin-1@.service");
         p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/with-dropin-1@instance-1.service");
-        assert_se(streq(changes[0].path, p));
+        ASSERT_STREQ(changes[0].path, p);
         p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/graphical.target.wants/with-dropin-1@instance-1.service");
-        assert_se(streq(changes[1].path, p));
+        ASSERT_STREQ(changes[1].path, p);
         install_changes_free(changes, n_changes);
         changes = NULL; n_changes = 0;
 
@@ -1017,30 +1017,30 @@ TEST(with_dropin_template) {
         assert_se(n_changes == 2);
         assert_se(changes[0].type == INSTALL_CHANGE_SYMLINK);
         assert_se(changes[1].type == INSTALL_CHANGE_SYMLINK);
-        assert_se(streq(changes[0].source, "/usr/lib/systemd/system/with-dropin-2@.service"));
-        assert_se(streq(changes[1].source, "/usr/lib/systemd/system/with-dropin-2@.service"));
+        ASSERT_STREQ(changes[0].source, "/usr/lib/systemd/system/with-dropin-2@.service");
+        ASSERT_STREQ(changes[1].source, "/usr/lib/systemd/system/with-dropin-2@.service");
         p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/with-dropin-2@instance-1.service");
-        assert_se(streq(changes[0].path, p));
+        ASSERT_STREQ(changes[0].path, p);
         p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/graphical.target.wants/with-dropin-2@instance-1.service");
-        assert_se(streq(changes[1].path, p));
+        ASSERT_STREQ(changes[1].path, p);
         install_changes_free(changes, n_changes);
         changes = NULL; n_changes = 0;
 
         assert_se(unit_file_enable(RUNTIME_SCOPE_SYSTEM, 0, root, STRV_MAKE("with-dropin-2@instance-2.service"), &changes, &n_changes) == 1);
         assert_se(n_changes == 1);
         assert_se(changes[0].type == INSTALL_CHANGE_SYMLINK);
-        assert_se(streq(changes[0].source, "/usr/lib/systemd/system/with-dropin-2@.service"));
+        ASSERT_STREQ(changes[0].source, "/usr/lib/systemd/system/with-dropin-2@.service");
         p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/with-dropin-2@instance-2.service");
-        assert_se(streq(changes[0].path, p));
+        ASSERT_STREQ(changes[0].path, p);
         install_changes_free(changes, n_changes);
         changes = NULL; n_changes = 0;
 
         assert_se(unit_file_enable(RUNTIME_SCOPE_SYSTEM, 0, root, STRV_MAKE("with-dropin-3@.service"), &changes, &n_changes) == 1);
         assert_se(n_changes == 1);
         assert_se(changes[0].type == INSTALL_CHANGE_SYMLINK);
-        assert_se(streq(changes[0].source, "/usr/lib/systemd/system/with-dropin-3@.service"));
+        ASSERT_STREQ(changes[0].source, "/usr/lib/systemd/system/with-dropin-3@.service");
         p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/with-dropin-3@instance-2.service");
-        assert_se(streq(changes[0].path, p));
+        ASSERT_STREQ(changes[0].path, p);
         install_changes_free(changes, n_changes);
         changes = NULL; n_changes = 0;
 
@@ -1080,7 +1080,7 @@ TEST(preset_multiple_instances) {
         assert_se(n_changes == 1);
         assert_se(changes[0].type == INSTALL_CHANGE_SYMLINK);
         p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/foo@bar0.service");
-        assert_se(streq(changes[0].path, p));
+        ASSERT_STREQ(changes[0].path, p);
         install_changes_free(changes, n_changes);
         changes = NULL; n_changes = 0;
 
@@ -1088,7 +1088,7 @@ TEST(preset_multiple_instances) {
         assert_se(n_changes == 1);
         assert_se(changes[0].type == INSTALL_CHANGE_UNLINK);
         p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/foo@bar0.service");
-        assert_se(streq(changes[0].path, p));
+        ASSERT_STREQ(changes[0].path, p);
         install_changes_free(changes, n_changes);
         changes = NULL; n_changes = 0;
 
@@ -1131,7 +1131,7 @@ static void verify_one(
          * RequiredBy= settings, and less so for Alias=. The only case where it should happen is when we have
          * an Alias=alias@.service an instantiated template template@instance. In that case the instance name
          * should be propagated into the alias as alias@instance. */
-        assert_se(streq_ptr(alias2, updated_name));
+        ASSERT_STREQ(alias2, updated_name);
 }
 
 TEST(verify_alias) {
index dfff015f53e44310f83e268825732b4c5f8865df..9d0403c2f9d71f78a3768f9e54d0f3d8a3bf7279 100644 (file)
@@ -27,8 +27,8 @@ static void test_int_fail(int i, int error) {
 }
 
 static void test_str(const char *s) {
-        assert_se(streq(ip_protocol_to_name(ip_protocol_from_name(s)), s));
-        assert_se(streq(ip_protocol_to_name(parse_ip_protocol(s)), s));
+        ASSERT_STREQ(ip_protocol_to_name(ip_protocol_from_name(s)), s);
+        ASSERT_STREQ(ip_protocol_to_name(parse_ip_protocol(s)), s);
 }
 
 static void test_str_fail(const char *s, int error) {
index cb0adc2643774d90640ff6bdb5168661bacbd765..7d9a7852f0eb93a7fff01d573f5fabc59279620c 100644 (file)
@@ -44,7 +44,7 @@ static void test_tokenizer_one(const char *data, ...) {
                         const char *nn;
 
                         nn = va_arg(ap, const char *);
-                        assert_se(streq_ptr(nn, str));
+                        ASSERT_STREQ(nn, str);
 
                 } else if (t == JSON_TOKEN_REAL) {
                         double d;
@@ -109,7 +109,7 @@ static void test_variant_one(const char *data, Test test) {
         s = mfree(s);
         r = json_variant_format(w, JSON_FORMAT_CENSOR_SENSITIVE, &s);
         assert_se(s);
-        assert_se(streq_ptr(s, "\"<sensitive data>\""));
+        ASSERT_STREQ(s, "\"<sensitive data>\"");
 
         s = mfree(s);
         r = json_variant_format(w, JSON_FORMAT_PRETTY, &s);
@@ -167,7 +167,7 @@ static void test_1(JsonVariant *v) {
         assert_se(p && json_variant_type(p) == JSON_VARIANT_STRING);
 
         /* k equals v */
-        assert_se(streq(json_variant_string(p), "v"));
+        ASSERT_STREQ(json_variant_string(p), "v");
 
         /* has foo */
         p = json_variant_by_key(v, "foo");
@@ -334,7 +334,7 @@ TEST(build) {
         assert_se(json_variant_format(b, 0, &t) >= 0);
         log_info("GOT: %s", t);
 
-        assert_se(streq(s, t));
+        ASSERT_STREQ(s, t);
 
         a = json_variant_unref(a);
         b = json_variant_unref(b);
@@ -362,7 +362,7 @@ TEST(json_parse_file_empty) {
 
         assert_se(fopen_unlocked("/dev/null", "re", &f) >= 0);
         assert_se(json_parse_file(f, "waldo", 0, &v, NULL, NULL) == -ENODATA);
-        assert_se(v == NULL);
+        ASSERT_NULL(v);
 }
 
 TEST(json_parse_file_invalid) {
@@ -371,7 +371,7 @@ TEST(json_parse_file_invalid) {
 
         assert_se(f = fmemopen_unlocked((void*) "kookoo", 6, "r"));
         assert_se(json_parse_file(f, "waldo", 0, &v, NULL, NULL) == -EINVAL);
-        assert_se(v == NULL);
+        ASSERT_NULL(v);
 }
 
 TEST(source) {
@@ -462,7 +462,7 @@ TEST(normalize) {
         assert_se(!json_variant_is_normalized(v));
 
         assert_se(json_variant_format(v, 0, &t) >= 0);
-        assert_se(streq(t, "{\"b\":\"x\",\"c\":\"y\",\"a\":\"z\"}"));
+        ASSERT_STREQ(t, "{\"b\":\"x\",\"c\":\"y\",\"a\":\"z\"}");
         t = mfree(t);
 
         assert_se(json_build(&w, JSON_BUILD_OBJECT(
@@ -473,7 +473,7 @@ TEST(normalize) {
         assert_se(!json_variant_is_normalized(w));
 
         assert_se(json_variant_format(w, 0, &t) >= 0);
-        assert_se(streq(t, "{\"bar\":\"zzz\",\"foo\":{\"b\":\"x\",\"c\":\"y\",\"a\":\"z\"}}"));
+        ASSERT_STREQ(t, "{\"bar\":\"zzz\",\"foo\":{\"b\":\"x\",\"c\":\"y\",\"a\":\"z\"}}");
         t = mfree(t);
 
         assert_se(json_variant_sort(&v) >= 0);
@@ -481,7 +481,7 @@ TEST(normalize) {
         assert_se(json_variant_is_normalized(v));
 
         assert_se(json_variant_format(v, 0, &t) >= 0);
-        assert_se(streq(t, "{\"a\":\"z\",\"b\":\"x\",\"c\":\"y\"}"));
+        ASSERT_STREQ(t, "{\"a\":\"z\",\"b\":\"x\",\"c\":\"y\"}");
         t = mfree(t);
 
         assert_se(json_variant_normalize(&w) >= 0);
@@ -489,7 +489,7 @@ TEST(normalize) {
         assert_se(json_variant_is_normalized(w));
 
         assert_se(json_variant_format(w, 0, &t) >= 0);
-        assert_se(streq(t, "{\"bar\":\"zzz\",\"foo\":{\"a\":\"z\",\"b\":\"x\",\"c\":\"y\"}}"));
+        ASSERT_STREQ(t, "{\"bar\":\"zzz\",\"foo\":{\"a\":\"z\",\"b\":\"x\",\"c\":\"y\"}}");
         t = mfree(t);
 }
 
@@ -531,7 +531,7 @@ TEST(bisect) {
                 assert_se(json_variant_is_string(k));
 
                 z = (char[5]){ '<', c, c, '>', 0};
-                assert_se(streq(json_variant_string(k), z));
+                ASSERT_STREQ(json_variant_string(k), z);
         }
 }
 
@@ -698,8 +698,8 @@ static void json_array_append_with_source_one(bool source) {
         assert_se(json_variant_get_source(a, &s1, &line1, &col1) >= 0);
         assert_se(json_variant_get_source(b, &s2, &line2, &col2) >= 0);
 
-        assert_se(streq_ptr(s1, source ? "string 1" : NULL));
-        assert_se(streq_ptr(s2, source ? "string 2" : NULL));
+        ASSERT_STREQ(s1, source ? "string 1" : NULL);
+        ASSERT_STREQ(s2, source ? "string 2" : NULL);
         assert_se(line1 == 1);
         assert_se(col1 == 2);
         assert_se(line2 == 3);
@@ -723,8 +723,8 @@ static void json_array_append_with_source_one(bool source) {
         assert_se(elem = json_variant_by_index(a, 1));
         assert_se(json_variant_get_source(elem, &s2, &line2, &col2) >= 0);
 
-        assert_se(streq_ptr(s1, source ? "string 2" : NULL));
-        assert_se(streq_ptr(s2, source ? "string 2" : NULL));
+        ASSERT_STREQ(s1, source ? "string 2" : NULL);
+        ASSERT_STREQ(s2, source ? "string 2" : NULL);
         assert_se(line1 == 3);
         assert_se(col1 == 5);
         assert_se(line2 == 3);
@@ -887,7 +887,7 @@ TEST(json_sensitive) {
         json_variant_sensitive(a);
 
         assert_se(json_variant_format(a, JSON_FORMAT_CENSOR_SENSITIVE, &s) >= 0);
-        assert_se(streq_ptr(s, "\"<sensitive data>\""));
+        ASSERT_STREQ(s, "\"<sensitive data>\"");
         s = mfree(s);
 
         r = json_variant_format(b, JSON_FORMAT_CENSOR_SENSITIVE, &s);
@@ -932,7 +932,7 @@ TEST(json_sensitive) {
         json_variant_dump(v, JSON_FORMAT_COLOR|JSON_FORMAT_PRETTY, NULL, NULL);
 
         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\":{}}"));
+        ASSERT_STREQ(s, "{\"b\":[\"foo\",\"bar\",\"baz\",\"qux\"],\"a\":\"<sensitive data>\",\"c\":-9223372036854775808,\"d\":\"-9223372036854775808\",\"e\":{}}");
         s = mfree(s);
         v = json_variant_unref(v);
 
@@ -945,7 +945,7 @@ TEST(json_sensitive) {
         json_variant_dump(v, JSON_FORMAT_COLOR|JSON_FORMAT_PRETTY, NULL, NULL);
 
         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\":{}}"));
+        ASSERT_STREQ(s, "{\"b\":[\"foo\",\"bar\",\"baz\",\"qux\"],\"c\":-9223372036854775808,\"a\":\"<sensitive data>\",\"d\":\"-9223372036854775808\",\"e\":{}}");
         s = mfree(s);
         v = json_variant_unref(v);
 
@@ -958,7 +958,7 @@ TEST(json_sensitive) {
         json_variant_dump(v, JSON_FORMAT_COLOR|JSON_FORMAT_PRETTY, NULL, NULL);
 
         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\":{}}"));
+        ASSERT_STREQ(s, "{\"b\":[\"foo\",\"bar\",\"baz\",\"qux\"],\"c\":-9223372036854775808,\"d\":\"-9223372036854775808\",\"a\":\"<sensitive data>\",\"e\":{}}");
         s = mfree(s);
         v = json_variant_unref(v);
 
@@ -971,7 +971,7 @@ TEST(json_sensitive) {
         json_variant_dump(v, JSON_FORMAT_COLOR|JSON_FORMAT_PRETTY, NULL, NULL);
 
         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>\"}"));
+        ASSERT_STREQ(s, "{\"b\":[\"foo\",\"bar\",\"baz\",\"qux\"],\"c\":-9223372036854775808,\"d\":\"-9223372036854775808\",\"e\":{},\"a\":\"<sensitive data>\"}");
 }
 
 TEST(json_iovec) {
index 87f7c7b3d466fe4d8afed107bab640502695da38..6c6407028468b09e1779534b9b51ea553d98b987 100644 (file)
@@ -17,8 +17,8 @@ int main(int argc, const char *argv[]) {
 
         LIST_HEAD_INIT(head);
         LIST_HEAD_INIT(head2);
-        assert_se(head == NULL);
-        assert_se(head2 == NULL);
+        ASSERT_NULL(head);
+        ASSERT_NULL(head2);
 
         for (i = 0; i < ELEMENTSOF(items); i++) {
                 LIST_INIT(item_list, &items[i]);
@@ -49,7 +49,7 @@ int main(int argc, const char *argv[]) {
 
         assert_se(!LIST_JUST_US(item_list, head));
 
-        assert_se(items[0].item_list_next == NULL);
+        ASSERT_NULL(items[0].item_list_next);
         assert_se(items[1].item_list_next == &items[0]);
         assert_se(items[2].item_list_next == &items[1]);
         assert_se(items[3].item_list_next == &items[2]);
@@ -57,7 +57,7 @@ int main(int argc, const char *argv[]) {
         assert_se(items[0].item_list_prev == &items[1]);
         assert_se(items[1].item_list_prev == &items[2]);
         assert_se(items[2].item_list_prev == &items[3]);
-        assert_se(items[3].item_list_prev == NULL);
+        ASSERT_NULL(items[3].item_list_prev);
 
         list_item *cursor = LIST_FIND_HEAD(item_list, &items[0]);
         assert_se(cursor == &items[3]);
@@ -68,16 +68,16 @@ int main(int argc, const char *argv[]) {
         assert_se(LIST_REMOVE(item_list, head, &items[1]) == &items[1]);
         assert_se(LIST_JUST_US(item_list, &items[1]));
 
-        assert_se(items[0].item_list_next == NULL);
+        ASSERT_NULL(items[0].item_list_next);
         assert_se(items[2].item_list_next == &items[0]);
         assert_se(items[3].item_list_next == &items[2]);
 
         assert_se(items[0].item_list_prev == &items[2]);
         assert_se(items[2].item_list_prev == &items[3]);
-        assert_se(items[3].item_list_prev == NULL);
+        ASSERT_NULL(items[3].item_list_prev);
 
         assert_se(LIST_INSERT_AFTER(item_list, head, &items[3], &items[1]) == &items[1]);
-        assert_se(items[0].item_list_next == NULL);
+        ASSERT_NULL(items[0].item_list_next);
         assert_se(items[2].item_list_next == &items[0]);
         assert_se(items[1].item_list_next == &items[2]);
         assert_se(items[3].item_list_next == &items[1]);
@@ -85,21 +85,21 @@ int main(int argc, const char *argv[]) {
         assert_se(items[0].item_list_prev == &items[2]);
         assert_se(items[2].item_list_prev == &items[1]);
         assert_se(items[1].item_list_prev == &items[3]);
-        assert_se(items[3].item_list_prev == NULL);
+        ASSERT_NULL(items[3].item_list_prev);
 
         assert_se(LIST_REMOVE(item_list, head, &items[1]) == &items[1]);
         assert_se(LIST_JUST_US(item_list, &items[1]));
 
-        assert_se(items[0].item_list_next == NULL);
+        ASSERT_NULL(items[0].item_list_next);
         assert_se(items[2].item_list_next == &items[0]);
         assert_se(items[3].item_list_next == &items[2]);
 
         assert_se(items[0].item_list_prev == &items[2]);
         assert_se(items[2].item_list_prev == &items[3]);
-        assert_se(items[3].item_list_prev == NULL);
+        ASSERT_NULL(items[3].item_list_prev);
 
         assert_se(LIST_INSERT_BEFORE(item_list, head, &items[2], &items[1]) == &items[1]);
-        assert_se(items[0].item_list_next == NULL);
+        ASSERT_NULL(items[0].item_list_next);
         assert_se(items[2].item_list_next == &items[0]);
         assert_se(items[1].item_list_next == &items[2]);
         assert_se(items[3].item_list_next == &items[1]);
@@ -107,21 +107,21 @@ int main(int argc, const char *argv[]) {
         assert_se(items[0].item_list_prev == &items[2]);
         assert_se(items[2].item_list_prev == &items[1]);
         assert_se(items[1].item_list_prev == &items[3]);
-        assert_se(items[3].item_list_prev == NULL);
+        ASSERT_NULL(items[3].item_list_prev);
 
         assert_se(LIST_REMOVE(item_list, head, &items[0]) == &items[0]);
         assert_se(LIST_JUST_US(item_list, &items[0]));
 
-        assert_se(items[2].item_list_next == NULL);
+        ASSERT_NULL(items[2].item_list_next);
         assert_se(items[1].item_list_next == &items[2]);
         assert_se(items[3].item_list_next == &items[1]);
 
         assert_se(items[2].item_list_prev == &items[1]);
         assert_se(items[1].item_list_prev == &items[3]);
-        assert_se(items[3].item_list_prev == NULL);
+        ASSERT_NULL(items[3].item_list_prev);
 
         assert_se(LIST_INSERT_BEFORE(item_list, head, &items[3], &items[0]) == &items[0]);
-        assert_se(items[2].item_list_next == NULL);
+        ASSERT_NULL(items[2].item_list_next);
         assert_se(items[1].item_list_next == &items[2]);
         assert_se(items[3].item_list_next == &items[1]);
         assert_se(items[0].item_list_next == &items[3]);
@@ -129,22 +129,22 @@ int main(int argc, const char *argv[]) {
         assert_se(items[2].item_list_prev == &items[1]);
         assert_se(items[1].item_list_prev == &items[3]);
         assert_se(items[3].item_list_prev == &items[0]);
-        assert_se(items[0].item_list_prev == NULL);
+        ASSERT_NULL(items[0].item_list_prev);
         assert_se(head == &items[0]);
 
         assert_se(LIST_REMOVE(item_list, head, &items[0]) == &items[0]);
         assert_se(LIST_JUST_US(item_list, &items[0]));
 
-        assert_se(items[2].item_list_next == NULL);
+        ASSERT_NULL(items[2].item_list_next);
         assert_se(items[1].item_list_next == &items[2]);
         assert_se(items[3].item_list_next == &items[1]);
 
         assert_se(items[2].item_list_prev == &items[1]);
         assert_se(items[1].item_list_prev == &items[3]);
-        assert_se(items[3].item_list_prev == NULL);
+        ASSERT_NULL(items[3].item_list_prev);
 
         assert_se(LIST_INSERT_BEFORE(item_list, head, NULL, &items[0]) == &items[0]);
-        assert_se(items[0].item_list_next == NULL);
+        ASSERT_NULL(items[0].item_list_next);
         assert_se(items[2].item_list_next == &items[0]);
         assert_se(items[1].item_list_next == &items[2]);
         assert_se(items[3].item_list_next == &items[1]);
@@ -152,27 +152,27 @@ int main(int argc, const char *argv[]) {
         assert_se(items[0].item_list_prev == &items[2]);
         assert_se(items[2].item_list_prev == &items[1]);
         assert_se(items[1].item_list_prev == &items[3]);
-        assert_se(items[3].item_list_prev == NULL);
+        ASSERT_NULL(items[3].item_list_prev);
 
         assert_se(LIST_REMOVE(item_list, head, &items[0]) == &items[0]);
         assert_se(LIST_JUST_US(item_list, &items[0]));
 
-        assert_se(items[2].item_list_next == NULL);
+        ASSERT_NULL(items[2].item_list_next);
         assert_se(items[1].item_list_next == &items[2]);
         assert_se(items[3].item_list_next == &items[1]);
 
         assert_se(items[2].item_list_prev == &items[1]);
         assert_se(items[1].item_list_prev == &items[3]);
-        assert_se(items[3].item_list_prev == NULL);
+        ASSERT_NULL(items[3].item_list_prev);
 
         assert_se(LIST_REMOVE(item_list, head, &items[1]) == &items[1]);
         assert_se(LIST_JUST_US(item_list, &items[1]));
 
-        assert_se(items[2].item_list_next == NULL);
+        ASSERT_NULL(items[2].item_list_next);
         assert_se(items[3].item_list_next == &items[2]);
 
         assert_se(items[2].item_list_prev == &items[3]);
-        assert_se(items[3].item_list_prev == NULL);
+        ASSERT_NULL(items[3].item_list_prev);
 
         assert_se(LIST_REMOVE(item_list, head, &items[2]) == &items[2]);
         assert_se(LIST_JUST_US(item_list, &items[2]));
@@ -181,7 +181,7 @@ int main(int argc, const char *argv[]) {
         assert_se(LIST_REMOVE(item_list, head, &items[3]) == &items[3]);
         assert_se(LIST_JUST_US(item_list, &items[3]));
 
-        assert_se(head == NULL);
+        ASSERT_NULL(head);
 
         for (i = 0; i < ELEMENTSOF(items); i++) {
                 assert_se(LIST_JUST_US(item_list, &items[i]));
@@ -193,9 +193,9 @@ int main(int argc, const char *argv[]) {
         assert_se(items[0].item_list_next == &items[1]);
         assert_se(items[1].item_list_next == &items[2]);
         assert_se(items[2].item_list_next == &items[3]);
-        assert_se(items[3].item_list_next == NULL);
+        ASSERT_NULL(items[3].item_list_next);
 
-        assert_se(items[0].item_list_prev == NULL);
+        ASSERT_NULL(items[0].item_list_prev);
         assert_se(items[1].item_list_prev == &items[0]);
         assert_se(items[2].item_list_prev == &items[1]);
         assert_se(items[3].item_list_prev == &items[2]);
@@ -203,7 +203,7 @@ int main(int argc, const char *argv[]) {
         for (i = 0; i < ELEMENTSOF(items); i++)
                 assert_se(LIST_REMOVE(item_list, head, &items[i]) == &items[i]);
 
-        assert_se(head == NULL);
+        ASSERT_NULL(head);
 
         for (i = 0; i < ELEMENTSOF(items) / 2; i++) {
                 LIST_INIT(item_list, &items[i]);
@@ -217,20 +217,20 @@ int main(int argc, const char *argv[]) {
                 assert_se(LIST_PREPEND(item_list, head2, &items[i]) == &items[i]);
         }
 
-        assert_se(items[0].item_list_next == NULL);
+        ASSERT_NULL(items[0].item_list_next);
         assert_se(items[1].item_list_next == &items[0]);
-        assert_se(items[2].item_list_next == NULL);
+        ASSERT_NULL(items[2].item_list_next);
         assert_se(items[3].item_list_next == &items[2]);
 
         assert_se(items[0].item_list_prev == &items[1]);
-        assert_se(items[1].item_list_prev == NULL);
+        ASSERT_NULL(items[1].item_list_prev);
         assert_se(items[2].item_list_prev == &items[3]);
-        assert_se(items[3].item_list_prev == NULL);
+        ASSERT_NULL(items[3].item_list_prev);
 
         assert_se(LIST_JOIN(item_list, head2, head) == head2);
-        assert_se(head == NULL);
+        ASSERT_NULL(head);
 
-        assert_se(items[0].item_list_next == NULL);
+        ASSERT_NULL(items[0].item_list_next);
         assert_se(items[1].item_list_next == &items[0]);
         assert_se(items[2].item_list_next == &items[1]);
         assert_se(items[3].item_list_next == &items[2]);
@@ -238,16 +238,16 @@ int main(int argc, const char *argv[]) {
         assert_se(items[0].item_list_prev == &items[1]);
         assert_se(items[1].item_list_prev == &items[2]);
         assert_se(items[2].item_list_prev == &items[3]);
-        assert_se(items[3].item_list_prev == NULL);
+        ASSERT_NULL(items[3].item_list_prev);
 
         assert_se(LIST_JOIN(item_list, head, head2) == head);
-        assert_se(head2 == NULL);
+        ASSERT_NULL(head2);
         assert_se(head);
 
         for (i = 0; i < ELEMENTSOF(items); i++)
                 assert_se(LIST_REMOVE(item_list, head, &items[i]) == &items[i]);
 
-        assert_se(head == NULL);
+        ASSERT_NULL(head);
 
         assert_se(LIST_PREPEND(item_list, head, items + 0) == items + 0);
         assert_se(LIST_PREPEND(item_list, head, items + 1) == items + 1);
@@ -256,7 +256,7 @@ int main(int argc, const char *argv[]) {
         assert_se(LIST_POP(item_list, head) == items + 2);
         assert_se(LIST_POP(item_list, head) == items + 1);
         assert_se(LIST_POP(item_list, head) == items + 0);
-        assert_se(LIST_POP(item_list, head) == NULL);
+        ASSERT_NULL(LIST_POP(item_list, head));
 
         /* No-op on an empty list */
 
@@ -269,7 +269,7 @@ int main(int argc, const char *argv[]) {
 
         LIST_CLEAR(item_list, head, free);
 
-        assert_se(head == NULL);
+        ASSERT_NULL(head);
 
         /* A list can be cleared partially */
 
@@ -280,7 +280,7 @@ int main(int argc, const char *argv[]) {
         LIST_CLEAR(item_list, head->item_list_next, free);
 
         assert_se(head == items + 0);
-        assert_se(head->item_list_next == NULL);
+        ASSERT_NULL(head->item_list_next);
 
         return 0;
 }
index 8d2cec0c01bd6394c980c849cb74db4a0184ebf8..94904e531e724029c0cdd4e71250fdb555e2ad04 100644 (file)
@@ -79,12 +79,12 @@ static void check_execcommand(ExecCommand *c,
         n = strv_length(c->argv);
         log_info("actual: \"%s\" [\"%s\" \"%s\" \"%s\"]",
                  c->path, c->argv[0], n > 0 ? c->argv[1] : "(null)", n > 1 ? c->argv[2] : "(null)");
-        assert_se(streq(c->path, path));
-        assert_se(streq(c->argv[0], argv0 ?: path));
+        ASSERT_STREQ(c->path, path);
+        ASSERT_STREQ(c->argv[0], argv0 ?: path);
         if (n > 0)
-                assert_se(streq_ptr(c->argv[1], argv1));
+                ASSERT_STREQ(c->argv[1], argv1);
         if (n > 1)
-                assert_se(streq_ptr(c->argv[2], argv2));
+                ASSERT_STREQ(c->argv[2], argv2);
         assert_se(!!(c->flags & EXEC_COMMAND_IGNORE_FAILURE) == ignore);
 }
 
@@ -139,7 +139,7 @@ TEST(config_parse_exec) {
                               "LValue", 0, "/RValue/ argv0 r1",
                               &c, u);
         assert_se(r == -ENOEXEC);
-        assert_se(c1->command_next == NULL);
+        ASSERT_NULL(c1->command_next);
 
         log_info("/* honour_argv0 */");
         r = config_parse_exec(NULL, "fake", 3, "section", 1,
@@ -154,14 +154,14 @@ TEST(config_parse_exec) {
                               "LValue", 0, "@/RValue",
                               &c, u);
         assert_se(r == -ENOEXEC);
-        assert_se(c1->command_next == NULL);
+        ASSERT_NULL(c1->command_next);
 
         log_info("/* no command, whitespace only, reset */");
         r = config_parse_exec(NULL, "fake", 3, "section", 1,
                               "LValue", 0, "",
                               &c, u);
         assert_se(r == 0);
-        assert_se(c == NULL);
+        ASSERT_NULL(c);
 
         log_info("/* ignore && honour_argv0 */");
         r = config_parse_exec(NULL, "fake", 4, "section", 1,
@@ -184,14 +184,14 @@ TEST(config_parse_exec) {
                               "LValue", 0, "--/RValue argv0 r1",
                               &c, u);
         assert_se(r == 0);
-        assert_se(c1->command_next == NULL);
+        ASSERT_NULL(c1->command_next);
 
         log_info("/* ignore && ignore (2) */");
         r = config_parse_exec(NULL, "fake", 4, "section", 1,
                               "LValue", 0, "-@-/RValue argv0 r1",
                               &c, u);
         assert_se(r == 0);
-        assert_se(c1->command_next == NULL);
+        ASSERT_NULL(c1->command_next);
 
         log_info("/* semicolon */");
         r = config_parse_exec(NULL, "fake", 5, "section", 1,
@@ -227,7 +227,7 @@ TEST(config_parse_exec) {
         c1 = c1->command_next;
         check_execcommand(c1, "/RValue", "argv0", "r1", NULL, true);
 
-        assert_se(c1->command_next == NULL);
+        ASSERT_NULL(c1->command_next);
 
         log_info("/* trailing semicolon, no whitespace */");
         r = config_parse_exec(NULL, "fake", 5, "section", 1,
@@ -238,7 +238,7 @@ TEST(config_parse_exec) {
         c1 = c1->command_next;
         check_execcommand(c1, "/RValue", "argv0", "r1", NULL, true);
 
-        assert_se(c1->command_next == NULL);
+        ASSERT_NULL(c1->command_next);
 
         log_info("/* trailing semicolon in single quotes */");
         r = config_parse_exec(NULL, "fake", 5, "section", 1,
@@ -366,7 +366,7 @@ TEST(config_parse_exec) {
                                       "LValue", 0, path,
                                       &c, u);
                 assert_se(r == -ENOEXEC);
-                assert_se(c1->command_next == NULL);
+                ASSERT_NULL(c1->command_next);
         }
 
         log_info("/* valid character: \\s */");
@@ -392,35 +392,35 @@ TEST(config_parse_exec) {
                               "LValue", 0, "/path\\",
                               &c, u);
         assert_se(r == -ENOEXEC);
-        assert_se(c1->command_next == NULL);
+        ASSERT_NULL(c1->command_next);
 
         log_info("/* missing ending ' */");
         r = config_parse_exec(NULL, "fake", 4, "section", 1,
                               "LValue", 0, "/path 'foo",
                               &c, u);
         assert_se(r == -ENOEXEC);
-        assert_se(c1->command_next == NULL);
+        ASSERT_NULL(c1->command_next);
 
         log_info("/* missing ending ' with trailing backslash */");
         r = config_parse_exec(NULL, "fake", 4, "section", 1,
                               "LValue", 0, "/path 'foo\\",
                               &c, u);
         assert_se(r == -ENOEXEC);
-        assert_se(c1->command_next == NULL);
+        ASSERT_NULL(c1->command_next);
 
         log_info("/* invalid space between modifiers */");
         r = config_parse_exec(NULL, "fake", 4, "section", 1,
                               "LValue", 0, "- /path",
                               &c, u);
         assert_se(r == 0);
-        assert_se(c1->command_next == NULL);
+        ASSERT_NULL(c1->command_next);
 
         log_info("/* only modifiers, no path */");
         r = config_parse_exec(NULL, "fake", 4, "section", 1,
                               "LValue", 0, "-",
                               &c, u);
         assert_se(r == 0);
-        assert_se(c1->command_next == NULL);
+        ASSERT_NULL(c1->command_next);
 
         log_info("/* long arg */"); /* See issue #22957. */
 
@@ -442,7 +442,7 @@ TEST(config_parse_exec) {
                               "LValue", 0, "",
                               &c, u);
         assert_se(r == 0);
-        assert_se(c == NULL);
+        ASSERT_NULL(c);
 
         exec_command_free_list(c);
 }
@@ -544,7 +544,7 @@ TEST(install_printf, .sd_booted = true) {
                 memzero(i.path, strlen(i.path));                        \
                 if (result) {                                           \
                         printf("%s\n", t);                              \
-                        assert_se(streq(t, result));                    \
+                        ASSERT_STREQ(t, result);                    \
                 } else                                                  \
                         assert_se(!t);                                  \
                 strcpy(i.name, d1);                                     \
@@ -794,8 +794,8 @@ TEST(config_parse_pass_environ) {
                                       &passenv, NULL);
         assert_se(r >= 0);
         assert_se(strv_length(passenv) == 2);
-        assert_se(streq(passenv[0], "A"));
-        assert_se(streq(passenv[1], "B"));
+        ASSERT_STREQ(passenv[0], "A");
+        ASSERT_STREQ(passenv[1], "B");
 
         r = config_parse_pass_environ(NULL, "fake", 1, "section", 1,
                                       "PassEnvironment", 0, "",
@@ -808,7 +808,7 @@ TEST(config_parse_pass_environ) {
                                       &passenv, NULL);
         assert_se(r >= 0);
         assert_se(strv_length(passenv) == 1);
-        assert_se(streq(passenv[0], "normal_name"));
+        ASSERT_STREQ(passenv[0], "normal_name");
 }
 
 TEST(config_parse_unit_env_file) {
@@ -858,8 +858,8 @@ TEST(config_parse_unit_env_file) {
                                        &files, u);
         assert_se(r == 0);
         assert_se(strv_length(files) == 2);
-        assert_se(streq(files[0], "/absolute1"));
-        assert_se(streq(files[1], "/absolute2"));
+        ASSERT_STREQ(files[0], "/absolute1");
+        ASSERT_STREQ(files[1], "/absolute2");
 
         r = config_parse_unit_env_file(u->id, "fake", 1, "section", 1,
                                        "EnvironmentFile", 0, "",
@@ -872,7 +872,7 @@ TEST(config_parse_unit_env_file) {
                                        &files, u);
         assert_se(r == 0);
         assert_se(strv_length(files) == 1);
-        assert_se(streq(files[0], "/path/foobar.service.conf"));
+        ASSERT_STREQ(files[0], "/path/foobar.service.conf");
 }
 
 TEST(unit_dump_config_items) {
@@ -1073,8 +1073,8 @@ TEST(config_parse_open_file) {
                                    &of, u);
         assert_se(r >= 0);
         assert_se(of);
-        assert_se(streq(of->path, "/proc/1/ns/mnt"));
-        assert_se(streq(of->fdname, "host-mount-namespace"));
+        ASSERT_STREQ(of->path, "/proc/1/ns/mnt");
+        ASSERT_STREQ(of->fdname, "host-mount-namespace");
         assert_se(of->flags == OPENFILE_READ_ONLY);
 
         of = open_file_free(of);
@@ -1083,8 +1083,8 @@ TEST(config_parse_open_file) {
                                    &of, u);
         assert_se(r >= 0);
         assert_se(of);
-        assert_se(streq(of->path, "/proc/1/ns/mnt"));
-        assert_se(streq(of->fdname, "mnt"));
+        ASSERT_STREQ(of->path, "/proc/1/ns/mnt");
+        ASSERT_STREQ(of->fdname, "mnt");
         assert_se(of->flags == OPENFILE_READ_ONLY);
 
         r = config_parse_open_file(NULL, "fake", 1, "section", 1,
index e3d6c08d145f7d4379b0a1f96c475385889cb472..f418f3712d115b6f9ebf1d96e73c1abf71c88eec 100644 (file)
@@ -1,5 +1,7 @@
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 
+/* Make sure the net/if.h header is included before any linux/ one */
+#include <net/if.h>
 #include <linux/if.h>
 #include <stdio.h>
 
index 1bd00d1af110860d00643bcc28da0684064e3177..15c635781b31fb770af2e1e5a22dbbb1b9ee0dcc 100644 (file)
@@ -49,10 +49,10 @@ static void verify_dissected_image(DissectedImage *dissected) {
 static void verify_dissected_image_harder(DissectedImage *dissected) {
         verify_dissected_image(dissected);
 
-        assert_se(streq(dissected->partitions[PARTITION_ESP].fstype, "vfat"));
-        assert_se(streq(dissected->partitions[PARTITION_XBOOTLDR].fstype, "vfat"));
-        assert_se(streq(dissected->partitions[PARTITION_ROOT].fstype, "ext4"));
-        assert_se(streq(dissected->partitions[PARTITION_HOME].fstype, "ext4"));
+        ASSERT_STREQ(dissected->partitions[PARTITION_ESP].fstype, "vfat");
+        ASSERT_STREQ(dissected->partitions[PARTITION_XBOOTLDR].fstype, "vfat");
+        ASSERT_STREQ(dissected->partitions[PARTITION_ROOT].fstype, "ext4");
+        ASSERT_STREQ(dissected->partitions[PARTITION_HOME].fstype, "ext4");
 }
 
 static void* thread_func(void *ptr) {
@@ -328,7 +328,7 @@ static int run(int argc, char *argv[]) {
         log_notice("All threads started now.");
 
         if (arg_n_threads == 1)
-                assert_se(thread_func(FD_TO_PTR(fd)) == NULL);
+                ASSERT_NULL(thread_func(FD_TO_PTR(fd)));
         else
                 for (unsigned i = 0; i < arg_n_threads; i++) {
                         log_notice("Joining thread #%u.", i);
index 78503a56608f6e1a312832fc61c2b828b9f0cfe0..05e15c8bdcfae01e54e5433cedaf8ad821829a63 100644 (file)
@@ -500,12 +500,12 @@ TEST(FOREACH_ARGUMENT) {
         FOREACH_ARGUMENT(p, p_1, NULL, p_2, p_3, NULL, p_4, NULL) {
                 switch (i++) {
                 case 0: assert_se(p == p_1); break;
-                case 1: assert_se(p == NULL); break;
+                case 1: ASSERT_NULL(p); break;
                 case 2: assert_se(p == p_2); break;
                 case 3: assert_se(p == p_3); break;
-                case 4: assert_se(p == NULL); break;
+                case 4: ASSERT_NULL(p); break;
                 case 5: assert_se(p == p_4); break;
-                case 6: assert_se(p == NULL); break;
+                case 6: ASSERT_NULL(p); break;
                 default: assert_se(false);
                 }
         }
@@ -525,20 +525,20 @@ TEST(FOREACH_ARGUMENT) {
         FOREACH_ARGUMENT(v, v_1, NULL, u32p, v_3, p_2, p_4, v_2, NULL) {
                 switch (i++) {
                 case 0: assert_se(v == v_1); break;
-                case 1: assert_se(v == NULL); break;
+                case 1: ASSERT_NULL(v); break;
                 case 2: assert_se(v == u32p); break;
                 case 3: assert_se(v == v_3); break;
                 case 4: assert_se(v == p_2); break;
                 case 5: assert_se(v == p_4); break;
                 case 6: assert_se(v == v_2); break;
-                case 7: assert_se(v == NULL); break;
+                case 7: ASSERT_NULL(v); break;
                 default: assert_se(false);
                 }
         }
         assert_se(i == 8);
         i = 0;
         FOREACH_ARGUMENT(v, NULL) {
-                assert_se(v == NULL);
+                ASSERT_NULL(v);
                 assert_se(i++ == 0);
         }
         assert_se(i == 1);
@@ -1105,4 +1105,67 @@ TEST(u64_multiply_safe) {
         assert_se(u64_multiply_safe(UINT64_MAX, UINT64_MAX) == 0);
 }
 
+TEST(ASSERT) {
+        char *null = NULL;
+
+        ASSERT_OK(0);
+        ASSERT_OK(255);
+        ASSERT_OK(printf("Hello world\n"));
+        ASSERT_SIGNAL(ASSERT_OK(-1), SIGABRT);
+        ASSERT_SIGNAL(ASSERT_OK(-ENOANO), SIGABRT);
+
+        ASSERT_OK_ERRNO(0 >= 0);
+        ASSERT_OK_ERRNO(255 >= 0);
+        ASSERT_OK_ERRNO(printf("Hello world\n"));
+        ASSERT_SIGNAL(ASSERT_OK_ERRNO(-1), SIGABRT);
+        ASSERT_SIGNAL(ASSERT_OK_ERRNO(-ENOANO), SIGABRT);
+
+        ASSERT_TRUE(true);
+        ASSERT_TRUE(255);
+        ASSERT_TRUE(getpid());
+        ASSERT_SIGNAL(ASSERT_TRUE(1 == 0), SIGABRT);
+
+        ASSERT_FALSE(false);
+        ASSERT_FALSE(1 == 0);
+        ASSERT_SIGNAL(ASSERT_FALSE(1 > 0), SIGABRT);
+
+        ASSERT_NULL(NULL);
+        ASSERT_SIGNAL(ASSERT_NULL(signal_to_string(SIGINT)), SIGABRT);
+
+        ASSERT_NOT_NULL(signal_to_string(SIGTERM));
+        ASSERT_SIGNAL(ASSERT_NOT_NULL(NULL), SIGABRT);
+
+        ASSERT_STREQ(NULL, null);
+        ASSERT_STREQ("foo", "foo");
+        ASSERT_SIGNAL(ASSERT_STREQ(null, "bar"), SIGABRT);
+        ASSERT_SIGNAL(ASSERT_STREQ("foo", "bar"), SIGABRT);
+
+        ASSERT_EQ(0, 0);
+        ASSERT_EQ(-1, -1);
+        ASSERT_SIGNAL(ASSERT_EQ(255, -1), SIGABRT);
+
+        ASSERT_GE(0, 0);
+        ASSERT_GE(1, -1);
+        ASSERT_SIGNAL(ASSERT_GE(-1, 1), SIGABRT);
+
+        ASSERT_LE(0, 0);
+        ASSERT_LE(-1, 1);
+        ASSERT_SIGNAL(ASSERT_LE(1, -1), SIGABRT);
+
+        ASSERT_NE(0, (int64_t) UINT_MAX);
+        ASSERT_NE(-1, 1);
+        ASSERT_SIGNAL(ASSERT_NE(0, 0), SIGABRT);
+        ASSERT_SIGNAL(ASSERT_NE(-1, -1), SIGABRT);
+
+        ASSERT_GT(1, 0);
+        ASSERT_GT(1, -1);
+        ASSERT_SIGNAL(ASSERT_GT(0, 0), SIGABRT);
+        ASSERT_SIGNAL(ASSERT_GT(-1, 1), SIGABRT);
+
+        ASSERT_LT(0, 1);
+        ASSERT_LT(-1, 1);
+        ASSERT_SIGNAL(ASSERT_LT(0, 0), SIGABRT);
+        ASSERT_SIGNAL(ASSERT_LT(1, -1), SIGABRT);
+}
+
 DEFINE_TEST_MAIN(LOG_INFO);
index 1c3f6463b2d1ef020cea886fc8fa765c9c316207..44f1f4cf5f0c70d61342cdf256536d6049116206 100644 (file)
@@ -40,7 +40,7 @@ static void *fake_pressure_thread(void *p) {
         assert_se(cfd >= 0);
         char buf[STRLEN("hello")+1] = {};
         assert_se(read(cfd, buf, sizeof(buf)-1) == sizeof(buf)-1);
-        assert_se(streq(buf, "hello"));
+        ASSERT_STREQ(buf, "hello");
         assert_se(write(cfd, &(const char) { 'z' }, 1) == 1);
 
         return 0;
index 254bdcaa15dec11ec99eece12fda90bfb4a44c1f..b8cc8568f04a6b2f5ebf1c590154e76dffa9f245 100644 (file)
@@ -17,7 +17,7 @@ TEST(memstream_empty) {
 
         assert_se(memstream_init(&m));
         assert_se(memstream_finalize(&m, &buf, &sz) >= 0);
-        assert_se(streq(buf, ""));
+        ASSERT_STREQ(buf, "");
         assert_se(sz == 0);
 }
 
@@ -32,7 +32,7 @@ TEST(memstream) {
         fputs("おはよう!", f);
         fputs(u8"😀😀😀", f);
         assert_se(memstream_finalize(&m, &buf, &sz) >= 0);
-        assert_se(streq(buf, u8"hogeおはよう!😀😀😀"));
+        ASSERT_STREQ(buf, u8"hogeおはよう!😀😀😀");
         assert_se(sz == strlen(u8"hogeおはよう!😀😀😀"));
 
         buf = mfree(buf);
@@ -40,7 +40,7 @@ TEST(memstream) {
         assert_se(f = memstream_init(&m));
         fputs("second", f);
         assert_se(memstream_finalize(&m, &buf, &sz) >= 0);
-        assert_se(streq(buf, "second"));
+        ASSERT_STREQ(buf, "second");
         assert_se(sz == strlen("second"));
 }
 
index 77fce983b912af6e1504c9a6720a27cfaa147d4a..a2711f059e393b6a1ccbc2f8f3fee039805d3dac 100644 (file)
@@ -31,56 +31,56 @@ TEST(mount_option_mangle) {
 
         assert_se(mount_option_mangle(NULL, MS_RDONLY|MS_NOSUID, &f, &opts) == 0);
         assert_se(f == (MS_RDONLY|MS_NOSUID));
-        assert_se(opts == NULL);
+        ASSERT_NULL(opts);
 
         assert_se(mount_option_mangle("", MS_RDONLY|MS_NOSUID, &f, &opts) == 0);
         assert_se(f == (MS_RDONLY|MS_NOSUID));
-        assert_se(opts == NULL);
+        ASSERT_NULL(opts);
 
         assert_se(mount_option_mangle("ro,nosuid,nodev,noexec", 0, &f, &opts) == 0);
         assert_se(f == (MS_RDONLY|MS_NOSUID|MS_NODEV|MS_NOEXEC));
-        assert_se(opts == NULL);
+        ASSERT_NULL(opts);
 
         assert_se(mount_option_mangle("ro,nosuid,nodev,noexec,mode=0755", 0, &f, &opts) == 0);
         assert_se(f == (MS_RDONLY|MS_NOSUID|MS_NODEV|MS_NOEXEC));
-        assert_se(streq(opts, "mode=0755"));
+        ASSERT_STREQ(opts, "mode=0755");
         opts = mfree(opts);
 
         assert_se(mount_option_mangle("rw,nosuid,foo,hogehoge,nodev,mode=0755", 0, &f, &opts) == 0);
         assert_se(f == (MS_NOSUID|MS_NODEV));
-        assert_se(streq(opts, "foo,hogehoge,mode=0755"));
+        ASSERT_STREQ(opts, "foo,hogehoge,mode=0755");
         opts = mfree(opts);
 
         assert_se(mount_option_mangle("rw,nosuid,nodev,noexec,relatime,net_cls,net_prio", MS_RDONLY, &f, &opts) == 0);
         assert_se(f == (MS_NOSUID|MS_NODEV|MS_NOEXEC|MS_RELATIME));
-        assert_se(streq(opts, "net_cls,net_prio"));
+        ASSERT_STREQ(opts, "net_cls,net_prio");
         opts = mfree(opts);
 
         assert_se(mount_option_mangle("rw,nosuid,nodev,relatime,size=1630748k,mode=0700,uid=1000,gid=1000", MS_RDONLY, &f, &opts) == 0);
         assert_se(f == (MS_NOSUID|MS_NODEV|MS_RELATIME));
-        assert_se(streq(opts, "size=1630748k,mode=0700,uid=1000,gid=1000"));
+        ASSERT_STREQ(opts, "size=1630748k,mode=0700,uid=1000,gid=1000");
         opts = mfree(opts);
 
         assert_se(mount_option_mangle("size=1630748k,rw,gid=1000,,,nodev,relatime,,mode=0700,nosuid,uid=1000", MS_RDONLY, &f, &opts) == 0);
         assert_se(f == (MS_NOSUID|MS_NODEV|MS_RELATIME));
-        assert_se(streq(opts, "size=1630748k,gid=1000,mode=0700,uid=1000"));
+        ASSERT_STREQ(opts, "size=1630748k,gid=1000,mode=0700,uid=1000");
         opts = mfree(opts);
 
         assert_se(mount_option_mangle("rw,exec,size=8143984k,nr_inodes=2035996,mode=0755", MS_RDONLY|MS_NOSUID|MS_NOEXEC|MS_NODEV, &f, &opts) == 0);
         assert_se(f == (MS_NOSUID|MS_NODEV));
-        assert_se(streq(opts, "size=8143984k,nr_inodes=2035996,mode=0755"));
+        ASSERT_STREQ(opts, "size=8143984k,nr_inodes=2035996,mode=0755");
         opts = mfree(opts);
 
         assert_se(mount_option_mangle("rw,relatime,fmask=0022,,,dmask=0022", MS_RDONLY, &f, &opts) == 0);
         assert_se(f == MS_RELATIME);
-        assert_se(streq(opts, "fmask=0022,dmask=0022"));
+        ASSERT_STREQ(opts, "fmask=0022,dmask=0022");
         opts = mfree(opts);
 
         assert_se(mount_option_mangle("rw,relatime,fmask=0022,dmask=0022,\"hogehoge", MS_RDONLY, &f, &opts) < 0);
 
         assert_se(mount_option_mangle("mode=01777,size=10%,nr_inodes=400k,uid=496107520,gid=496107520,context=\"system_u:object_r:svirt_sandbox_file_t:s0:c0,c1\"", 0, &f, &opts) == 0);
         assert_se(f == 0);
-        assert_se(streq(opts, "mode=01777,size=10%,nr_inodes=400k,uid=496107520,gid=496107520,context=\"system_u:object_r:svirt_sandbox_file_t:s0:c0,c1\""));
+        ASSERT_STREQ(opts, "mode=01777,size=10%,nr_inodes=400k,uid=496107520,gid=496107520,context=\"system_u:object_r:svirt_sandbox_file_t:s0:c0,c1\"");
         opts = mfree(opts);
 }
 
@@ -91,7 +91,7 @@ static void test_mount_flags_to_string_one(unsigned long flags, const char *expe
         r = mount_flags_to_string(flags, &x);
         log_info("flags: %#lX → %d/\"%s\"", flags, r, strnull(x));
         assert_se(r >= 0);
-        assert_se(streq(x, expected));
+        ASSERT_STREQ(x, expected);
 }
 
 TEST(mount_flags_to_string) {
index affcaccc007fa8f961cb76f5808eb47565b792dd..6060ec2fc803b3bf7c401e4b39ac0d369db060eb 100644 (file)
@@ -33,7 +33,7 @@ static void test_mount_propagation_flag_one(const char *name, int ret, unsigned
                 if (isempty(name))
                         assert_se(isempty(c));
                 else
-                        assert_se(streq(c, name));
+                        ASSERT_STREQ(c, name);
         }
 }
 
index f7ec5a6d72fd558df281fbf9337375b49b5fc894..a339020857a6694d874a22e98eba41cfdeb23e58 100644 (file)
@@ -25,7 +25,7 @@ TEST(naming_scheme_conversions) {
         log_info("latest → %s", n->name);
 
         assert_se(n = naming_scheme_from_name("v238"));
-        assert_se(streq(n->name, "v238"));
+        ASSERT_STREQ(n->name, "v238");
 }
 
 DEFINE_TEST_MAIN(LOG_INFO);
index 6543c617c855d5bdfaab265143b5bebb937349e9..8c1b0d407e36c0275ab00ef72ce76220e61b79c6 100644 (file)
@@ -1,5 +1,7 @@
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 
+/* Make sure the net/if.h header is included before any linux/ one */
+#include <net/if.h>
 #include <arpa/inet.h>
 #include <linux/if_tunnel.h>
 #include <linux/ip.h>
 #include "tests.h"
 
 static int load_module(const char *mod_name) {
-        _cleanup_(kmod_unrefp) struct kmod_ctx *ctx = NULL;
-        _cleanup_(kmod_module_unref_listp) struct kmod_list *list = NULL;
+        _cleanup_(sym_kmod_unrefp) struct kmod_ctx *ctx = NULL;
+        _cleanup_(sym_kmod_module_unref_listp) struct kmod_list *list = NULL;
         struct kmod_list *l;
         int r;
 
-        ctx = kmod_new(NULL, NULL);
+        r = dlopen_libkmod();
+        if (r < 0)
+                return log_error_errno(r, "Failed to load libkmod: %m");
+
+        ctx = sym_kmod_new(NULL, NULL);
         if (!ctx)
                 return log_oom();
 
-        r = kmod_module_new_from_lookup(ctx, mod_name, &list);
+        r = sym_kmod_module_new_from_lookup(ctx, mod_name, &list);
         if (r < 0)
                 return r;
 
-        kmod_list_foreach(l, list) {
-                _cleanup_(kmod_module_unrefp) struct kmod_module *mod = NULL;
+        sym_kmod_list_foreach(l, list) {
+                _cleanup_(sym_kmod_module_unrefp) struct kmod_module *mod = NULL;
 
-                mod = kmod_module_get_module(l);
+                mod = sym_kmod_module_get_module(l);
 
-                r = kmod_module_probe_insert_module(mod, 0, NULL, NULL, NULL, NULL);
+                r = sym_kmod_module_probe_insert_module(mod, 0, NULL, NULL, NULL, NULL);
                 if (r > 0)
                         r = -EINVAL;
         }
@@ -78,7 +84,7 @@ static int test_tunnel_configure(sd_netlink *rtnl) {
 
         assert_se(sd_netlink_call(rtnl, m, -1, 0) == 1);
 
-        assert_se((m = sd_netlink_message_unref(m)) == NULL);
+        ASSERT_NULL((m = sd_netlink_message_unref(m)));
 
         /* sit */
         assert_se(sd_rtnl_message_new_link(rtnl, &n, RTM_NEWLINK, 0) >= 0);
@@ -104,7 +110,7 @@ static int test_tunnel_configure(sd_netlink *rtnl) {
 
         assert_se(sd_netlink_call(rtnl, n, -1, 0) == 1);
 
-        assert_se((n = sd_netlink_message_unref(n)) == NULL);
+        ASSERT_NULL((n = sd_netlink_message_unref(n)));
 
         return EXIT_SUCCESS;
 }
@@ -120,7 +126,7 @@ int main(int argc, char *argv[]) {
 
         r = test_tunnel_configure(rtnl);
 
-        assert_se((rtnl = sd_netlink_unref(rtnl)) == NULL);
+        ASSERT_NULL((rtnl = sd_netlink_unref(rtnl)));
 
         return r;
 }
index 95c25f1540cae65d731c560a3ed8aa9c89a11f53..9b13d3c339ab94cd3dc55f411612300913075539 100644 (file)
@@ -13,10 +13,10 @@ TEST(strv_split_nulstr) {
         l = strv_split_nulstr(nulstr);
         assert_se(l);
 
-        assert_se(streq(l[0], "str0"));
-        assert_se(streq(l[1], "str1"));
-        assert_se(streq(l[2], "str2"));
-        assert_se(streq(l[3], "str3"));
+        ASSERT_STREQ(l[0], "str0");
+        ASSERT_STREQ(l[1], "str1");
+        ASSERT_STREQ(l[2], "str2");
+        ASSERT_STREQ(l[3], "str3");
 }
 
 #define strv_parse_nulstr_full_one(s, n, e0, e1)                        \
@@ -97,7 +97,7 @@ static void test_strv_make_nulstr_one(char **l) {
         assert_se(memcmp_nn(b, n, c, m) == 0);
 
         NULSTR_FOREACH(s, b)
-                assert_se(streq(s, l[i++]));
+                ASSERT_STREQ(s, l[i++]);
         assert_se(i == strv_length(l));
 }
 
index 1b938ec5f73df2398cec9b9dd8cdcaa9a56f724c..22f58affcccd52ad9dc809c83b1a650e6b3016f5 100644 (file)
@@ -11,32 +11,32 @@ TEST(open_file_parse) {
         r = open_file_parse("/proc/1/ns/mnt:host-mount-namespace:read-only", &of);
 
         assert_se(r >= 0);
-        assert_se(streq(of->path, "/proc/1/ns/mnt"));
-        assert_se(streq(of->fdname, "host-mount-namespace"));
+        ASSERT_STREQ(of->path, "/proc/1/ns/mnt");
+        ASSERT_STREQ(of->fdname, "host-mount-namespace");
         assert_se(of->flags == OPENFILE_READ_ONLY);
 
         of = open_file_free(of);
         r = open_file_parse("/proc/1/ns/mnt", &of);
 
         assert_se(r >= 0);
-        assert_se(streq(of->path, "/proc/1/ns/mnt"));
-        assert_se(streq(of->fdname, "mnt"));
+        ASSERT_STREQ(of->path, "/proc/1/ns/mnt");
+        ASSERT_STREQ(of->fdname, "mnt");
         assert_se(of->flags == 0);
 
         of = open_file_free(of);
         r = open_file_parse("/proc/1/ns/mnt:host-mount-namespace", &of);
 
         assert_se(r >= 0);
-        assert_se(streq(of->path, "/proc/1/ns/mnt"));
-        assert_se(streq(of->fdname, "host-mount-namespace"));
+        ASSERT_STREQ(of->path, "/proc/1/ns/mnt");
+        ASSERT_STREQ(of->fdname, "host-mount-namespace");
         assert_se(of->flags == 0);
 
         of = open_file_free(of);
         r = open_file_parse("/proc/1/ns/mnt::read-only", &of);
 
         assert_se(r >= 0);
-        assert_se(streq(of->path, "/proc/1/ns/mnt"));
-        assert_se(streq(of->fdname, "mnt"));
+        ASSERT_STREQ(of->path, "/proc/1/ns/mnt");
+        ASSERT_STREQ(of->fdname, "mnt");
         assert_se(of->flags == OPENFILE_READ_ONLY);
 
         of = open_file_free(of);
@@ -53,16 +53,16 @@ TEST(open_file_parse) {
         r = open_file_parse("/proc/1/ns/mnt:host-mount-namespace:append", &of);
 
         assert_se(r >= 0);
-        assert_se(streq(of->path, "/proc/1/ns/mnt"));
-        assert_se(streq(of->fdname, "host-mount-namespace"));
+        ASSERT_STREQ(of->path, "/proc/1/ns/mnt");
+        ASSERT_STREQ(of->fdname, "host-mount-namespace");
         assert_se(of->flags == OPENFILE_APPEND);
 
         of = open_file_free(of);
         r = open_file_parse("/proc/1/ns/mnt:host-mount-namespace:truncate", &of);
 
         assert_se(r >= 0);
-        assert_se(streq(of->path, "/proc/1/ns/mnt"));
-        assert_se(streq(of->fdname, "host-mount-namespace"));
+        ASSERT_STREQ(of->path, "/proc/1/ns/mnt");
+        ASSERT_STREQ(of->fdname, "host-mount-namespace");
         assert_se(of->flags == OPENFILE_TRUNCATE);
 
         of = open_file_free(of);
@@ -89,16 +89,16 @@ TEST(open_file_parse) {
         r = open_file_parse("/proc/1/ns/mnt:host-mount-namespace:graceful", &of);
 
         assert_se(r >= 0);
-        assert_se(streq(of->path, "/proc/1/ns/mnt"));
-        assert_se(streq(of->fdname, "host-mount-namespace"));
+        ASSERT_STREQ(of->path, "/proc/1/ns/mnt");
+        ASSERT_STREQ(of->fdname, "host-mount-namespace");
         assert_se(of->flags == OPENFILE_GRACEFUL);
 
         of = open_file_free(of);
         r = open_file_parse("/proc/1/ns/mnt:host-mount-namespace:read-only,graceful", &of);
 
         assert_se(r >= 0);
-        assert_se(streq(of->path, "/proc/1/ns/mnt"));
-        assert_se(streq(of->fdname, "host-mount-namespace"));
+        ASSERT_STREQ(of->path, "/proc/1/ns/mnt");
+        ASSERT_STREQ(of->fdname, "host-mount-namespace");
         assert_se(of->flags == (OPENFILE_READ_ONLY | OPENFILE_GRACEFUL));
 
         of = open_file_free(of);
@@ -120,7 +120,7 @@ TEST(open_file_to_string) {
         r = open_file_to_string(of, &s);
 
         assert_se(r >= 0);
-        assert_se(streq(s, "/proc/1/ns/mnt:host-mount-namespace:read-only"));
+        ASSERT_STREQ(s, "/proc/1/ns/mnt:host-mount-namespace:read-only");
 
         s = mfree(s);
         of->flags = OPENFILE_APPEND;
@@ -128,7 +128,7 @@ TEST(open_file_to_string) {
         r = open_file_to_string(of, &s);
 
         assert_se(r >= 0);
-        assert_se(streq(s, "/proc/1/ns/mnt:host-mount-namespace:append"));
+        ASSERT_STREQ(s, "/proc/1/ns/mnt:host-mount-namespace:append");
 
         s = mfree(s);
         of->flags = OPENFILE_TRUNCATE;
@@ -136,7 +136,7 @@ TEST(open_file_to_string) {
         r = open_file_to_string(of, &s);
 
         assert_se(r >= 0);
-        assert_se(streq(s, "/proc/1/ns/mnt:host-mount-namespace:truncate"));
+        ASSERT_STREQ(s, "/proc/1/ns/mnt:host-mount-namespace:truncate");
 
         s = mfree(s);
         of->flags = OPENFILE_GRACEFUL;
@@ -144,7 +144,7 @@ TEST(open_file_to_string) {
         r = open_file_to_string(of, &s);
 
         assert_se(r >= 0);
-        assert_se(streq(s, "/proc/1/ns/mnt:host-mount-namespace:graceful"));
+        ASSERT_STREQ(s, "/proc/1/ns/mnt:host-mount-namespace:graceful");
 
         s = mfree(s);
         of->flags = OPENFILE_READ_ONLY | OPENFILE_GRACEFUL;
@@ -152,7 +152,7 @@ TEST(open_file_to_string) {
         r = open_file_to_string(of, &s);
 
         assert_se(r >= 0);
-        assert_se(streq(s, "/proc/1/ns/mnt:host-mount-namespace:read-only,graceful"));
+        ASSERT_STREQ(s, "/proc/1/ns/mnt:host-mount-namespace:read-only,graceful");
 
         s = mfree(s);
         of->flags = 0;
@@ -160,7 +160,7 @@ TEST(open_file_to_string) {
         r = open_file_to_string(of, &s);
 
         assert_se(r >= 0);
-        assert_se(streq(s, "/proc/1/ns/mnt:host-mount-namespace"));
+        ASSERT_STREQ(s, "/proc/1/ns/mnt:host-mount-namespace");
 
         s = mfree(s);
         assert_se(free_and_strdup(&of->fdname, "mnt"));
@@ -169,17 +169,15 @@ TEST(open_file_to_string) {
         r = open_file_to_string(of, &s);
 
         assert_se(r >= 0);
-        assert_se(streq(s, "/proc/1/ns/mnt::read-only"));
+        ASSERT_STREQ(s, "/proc/1/ns/mnt::read-only");
 
         s = mfree(s);
-        assert_se(free_and_strdup(&of->path, "/path:with:colon"));
-        assert_se(free_and_strdup(&of->fdname, "path:with:colon"));
+        ASSERT_OK(free_and_strdup(&of->path, "/path:with:colon"));
+        ASSERT_OK(free_and_strdup(&of->fdname, "path:with:colon"));
         of->flags = 0;
 
-        r = open_file_to_string(of, &s);
-
-        assert_se(r >= 0);
-        assert_se(streq(s, "/path\\:with\\:colon"));
+        ASSERT_OK(open_file_to_string(of, &s));
+        ASSERT_STREQ(s, "/path\\x3awith\\x3acolon");
 }
 
 DEFINE_TEST_MAIN(LOG_INFO);
index dfdd1ab5b45d4a56e2a1dd512887546f661ecb10..51dc492e965dee9bfb1155fe24d42066208983bb 100644 (file)
@@ -95,7 +95,7 @@ TEST(invalid) {
 
         DEFINE_HEX_PTR(key, "2d2d2d2d2d424547494e205055424c4943204b45592d2d2d2d2d0a4d466b7b");
         assert_se(openssl_pkey_from_pem(key, key_len, &pkey) == -EIO);
-        assert_se(pkey == NULL);
+        ASSERT_NULL(pkey);
 }
 
 static const struct {
index c055411b798d94e0a3e850c790461a458c68963a..bb1eefb08387b54f94a9ac0809335eb445e3c692 100644 (file)
@@ -81,9 +81,9 @@ TEST(set_put) {
                                                         * non-trivial hash ops. */
 
         assert_se(t = ordered_set_get_strv(m));
-        assert_se(streq(t[0], "1"));
-        assert_se(streq(t[1], "22"));
-        assert_se(streq(t[2], "333"));
+        ASSERT_STREQ(t[0], "1");
+        ASSERT_STREQ(t[1], "22");
+        ASSERT_STREQ(t[2], "333");
         assert_se(!t[3]);
 
         ordered_set_print(stdout, "FOO=", m);
index e51ab9d29086bb28f7543e3f88e1bd683962cbeb..55475a56ae96d734315864ce7c97db4829d31bf5 100644 (file)
@@ -20,10 +20,12 @@ TEST(path_is_os_tree) {
 }
 
 TEST(parse_os_release) {
-        /* Let's assume that we're running in a valid system, so os-release is available */
         _cleanup_free_ char *id = NULL, *id2 = NULL, *name = NULL, *foobar = NULL;
-        ASSERT_EQ(parse_os_release(NULL, "ID", &id), 0);
-        log_info("ID: %s", id);
+
+        if (access("/etc/os-release", F_OK) >= 0 || access("/usr/lib/os-release", F_OK) >= 0) {
+                ASSERT_EQ(parse_os_release(NULL, "ID", &id), 0);
+                log_info("ID: %s", id);
+        }
 
         ASSERT_EQ(setenv("SYSTEMD_OS_RELEASE", "/dev/null", 1), 0);
         ASSERT_EQ(parse_os_release(NULL, "ID", &id2), 0);
@@ -37,8 +39,8 @@ TEST(parse_os_release) {
         ASSERT_EQ(setenv("SYSTEMD_OS_RELEASE", tmpfile, 1), 0);
         ASSERT_EQ(parse_os_release(NULL, "ID", &id, "NAME", &name), 0);
         log_info("ID: %s NAME: %s", id, name);
-        assert_se(streq(id, "the-id"));
-        assert_se(streq(name, "the-name"));
+        ASSERT_STREQ(id, "the-id");
+        ASSERT_STREQ(name, "the-name");
 
         _cleanup_(unlink_tempfilep) char tmpfile2[] = "/tmp/test-os-util.XXXXXX";
         ASSERT_EQ(write_tmpfile(tmpfile2,
@@ -49,8 +51,8 @@ TEST(parse_os_release) {
         ASSERT_EQ(setenv("SYSTEMD_OS_RELEASE", tmpfile2, 1), 0);
         ASSERT_EQ(parse_os_release(NULL, "ID", &id, "NAME", &name), 0);
         log_info("ID: %s NAME: %s", id, name);
-        assert_se(streq(id, "the-id"));
-        assert_se(streq(name, "the-name"));
+        ASSERT_STREQ(id, "the-id");
+        ASSERT_STREQ(name, "the-name");
 
         ASSERT_EQ(parse_os_release(NULL, "FOOBAR", &foobar), 0);
         log_info("FOOBAR: %s", strnull(foobar));
@@ -78,8 +80,8 @@ TEST(parse_extension_release) {
 
         assert_se(parse_extension_release(tempdir, IMAGE_SYSEXT, "test", false, "ID", &id, "VERSION_ID", &version_id) == 0);
         log_info("ID: %s VERSION_ID: %s", id, version_id);
-        assert_se(streq(id, "the-id"));
-        assert_se(streq(version_id, "the-version-id"));
+        ASSERT_STREQ(id, "the-id");
+        ASSERT_STREQ(version_id, "the-version-id");
 
         assert_se(b = path_join(tempdir, "/etc/extension-release.d/extension-release.tester"));
         assert_se(mkdir_parents(b, 0777) >= 0);
@@ -90,8 +92,8 @@ TEST(parse_extension_release) {
 
         ASSERT_EQ(parse_extension_release(tempdir, IMAGE_CONFEXT, "tester", false, "ID", &id, "VERSION_ID", &version_id), 0);
         log_info("ID: %s VERSION_ID: %s", id, version_id);
-        assert_se(streq(id, "the-id"));
-        assert_se(streq(version_id, "the-version-id"));
+        ASSERT_STREQ(id, "the-id");
+        ASSERT_STREQ(version_id, "the-version-id");
 
         assert_se(parse_extension_release(tempdir, IMAGE_CONFEXT, "tester", false, "FOOBAR", &foobar) == 0);
         log_info("FOOBAR: %s", strnull(foobar));
index cf3d54288abb9899a2aaaa76f72d923abd00b8e4..c07b2d9afe762cd6fcd7cc1b74661691232255cd 100644 (file)
@@ -20,13 +20,13 @@ TEST(parse_path_argument) {
         _cleanup_free_ char *path = NULL;
 
         assert_se(parse_path_argument("help", false, &path) == 0);
-        assert_se(streq(basename(path), "help"));
+        ASSERT_STREQ(basename(path), "help");
 
         assert_se(parse_path_argument("/", false, &path) == 0);
-        assert_se(streq(path, "/"));
+        ASSERT_STREQ(path, "/");
 
         assert_se(parse_path_argument("/", true, &path) == 0);
-        assert_se(path == NULL);
+        ASSERT_NULL(path);
 }
 
 TEST(parse_signal_argument) {
index cb13fad7c5113f1f783f656d202acf07eb5794f9..cff27749317caed58e2a9c8ab7d3a312b1f4ae56 100644 (file)
@@ -28,7 +28,7 @@ static void test_paths_one(RuntimeScope scope) {
         assert_se(setenv("SYSTEMD_UNIT_PATH", systemd_unit_path, 1) == 0);
         assert_se(lookup_paths_init(&lp_with_env, scope, 0, NULL) == 0);
         assert_se(strv_length(lp_with_env.search_path) == 1);
-        assert_se(streq(lp_with_env.search_path[0], systemd_unit_path));
+        ASSERT_STREQ(lp_with_env.search_path[0], systemd_unit_path);
         lookup_paths_log(&lp_with_env);
         assert_se(strv_equal(lp_with_env.search_path, STRV_MAKE(systemd_unit_path)));
 }
index 5f36ae49f05ff3e7f9fc7ab10c0e11aa14836044..72b92b7a9aaf2fd02c6e27288246fe7e5a11f68d 100644 (file)
@@ -26,10 +26,10 @@ TEST(path) {
         assert_se( path_is_absolute("/"));
         assert_se(!path_is_absolute("./"));
 
-        assert_se(streq(basename("./aa/bb/../file.da."), "file.da."));
-        assert_se(streq(basename("/aa///.file"), ".file"));
-        assert_se(streq(basename("/aa///file..."), "file..."));
-        assert_se(streq(basename("file.../"), ""));
+        ASSERT_STREQ(basename("./aa/bb/../file.da."), "file.da.");
+        ASSERT_STREQ(basename("/aa///.file"), ".file");
+        ASSERT_STREQ(basename("/aa///file..."), "file...");
+        ASSERT_STREQ(basename("file.../"), "");
 
         assert_se( PATH_IN_SET("/bin", "/", "/bin", "/foo"));
         assert_se( PATH_IN_SET("/bin", "/bin"));
@@ -146,7 +146,7 @@ static void test_path_simplify_one(const char *in, const char *out, PathSimplify
         p = strdupa_safe(in);
         path_simplify_full(p, flags);
         log_debug("/* test_path_simplify(%s) → %s (expected: %s) */", in, p, out);
-        assert_se(streq(p, out));
+        ASSERT_STREQ(p, out);
 }
 
 TEST(path_simplify) {
@@ -379,12 +379,12 @@ TEST(find_executable_full) {
 
         assert_se(find_executable_full("sh", NULL, NULL, true, &p, NULL) == 0);
         puts(p);
-        assert_se(streq(basename(p), "sh"));
+        ASSERT_STREQ(basename(p), "sh");
         free(p);
 
         assert_se(find_executable_full("sh", NULL, NULL, false, &p, NULL) == 0);
         puts(p);
-        assert_se(streq(basename(p), "sh"));
+        ASSERT_STREQ(basename(p), "sh");
         free(p);
 
         _cleanup_free_ char *oldpath = NULL;
@@ -396,12 +396,12 @@ TEST(find_executable_full) {
 
         assert_se(find_executable_full("sh", NULL, NULL, true, &p, NULL) == 0);
         puts(p);
-        assert_se(streq(basename(p), "sh"));
+        ASSERT_STREQ(basename(p), "sh");
         free(p);
 
         assert_se(find_executable_full("sh", NULL, NULL, false, &p, NULL) == 0);
         puts(p);
-        assert_se(streq(basename(p), "sh"));
+        ASSERT_STREQ(basename(p), "sh");
         free(p);
 
         if (oldpath)
@@ -414,7 +414,7 @@ TEST(find_executable_full) {
 
         assert_se(find_executable_full(test_file_name, NULL, STRV_MAKE("/doesnotexist", "/tmp", "/bin"), false, &p, NULL) == 0);
         puts(p);
-        assert_se(streq(p, fn));
+        ASSERT_STREQ(p, fn);
         free(p);
 
         (void) unlink(fn);
@@ -442,12 +442,12 @@ TEST(find_executable) {
         free(p);
 
         assert_se(find_executable("/bin/touch", &p) == 0);
-        assert_se(streq(p, "/bin/touch"));
+        ASSERT_STREQ(p, "/bin/touch");
         free(p);
 
         assert_se(find_executable("touch", &p) == 0);
         assert_se(path_is_absolute(p));
-        assert_se(streq(basename(p), "touch"));
+        ASSERT_STREQ(basename(p), "touch");
         free(p);
 
         assert_se(find_executable("xxxx-xxxx", &p) == -ENOENT);
@@ -468,7 +468,7 @@ static void test_find_executable_exec_one(const char *path) {
         assert_se(fd > STDERR_FILENO);
         assert_se(path_is_absolute(t));
         if (path_is_absolute(path))
-                assert_se(streq(t, path));
+                ASSERT_STREQ(t, path);
 
         pid = fork();
         assert_se(pid >= 0);
@@ -506,25 +506,25 @@ TEST(prefixes) {
         i = 0;
         PATH_FOREACH_PREFIX_MORE(s, "/a/b/c/d") {
                 log_error("---%s---", s);
-                assert_se(streq(s, values[i++]));
+                ASSERT_STREQ(s, values[i++]);
         }
         ASSERT_NULL(values[i]);
 
         i = 1;
         PATH_FOREACH_PREFIX(s, "/a/b/c/d") {
                 log_error("---%s---", s);
-                assert_se(streq(s, values[i++]));
+                ASSERT_STREQ(s, values[i++]);
         }
         ASSERT_NULL(values[i]);
 
         i = 0;
         PATH_FOREACH_PREFIX_MORE(s, "////a////b////c///d///////")
-                assert_se(streq(s, values[i++]));
+                ASSERT_STREQ(s, values[i++]);
         ASSERT_NULL(values[i]);
 
         i = 1;
         PATH_FOREACH_PREFIX(s, "////a////b////c///d///////")
-                assert_se(streq(s, values[i++]));
+                ASSERT_STREQ(s, values[i++]);
         ASSERT_NULL(values[i]);
 
         PATH_FOREACH_PREFIX(s, "////")
@@ -533,7 +533,7 @@ TEST(prefixes) {
         b = false;
         PATH_FOREACH_PREFIX_MORE(s, "////") {
                 assert_se(!b);
-                assert_se(streq(s, ""));
+                ASSERT_STREQ(s, "");
                 b = true;
         }
         assert_se(b);
@@ -544,7 +544,7 @@ TEST(prefixes) {
         b = false;
         PATH_FOREACH_PREFIX_MORE(s, "") {
                 assert_se(!b);
-                assert_se(streq(s, ""));
+                ASSERT_STREQ(s, "");
                 b = true;
         }
 }
@@ -554,7 +554,7 @@ TEST(path_join) {
                 _cleanup_free_ char *z = NULL;   \
                 z = path_join(__VA_ARGS__); \
                 log_debug("got \"%s\", expected \"%s\"", z, expected); \
-                assert_se(streq(z, expected));   \
+                ASSERT_STREQ(z, expected);   \
         }
 
         test_join("/root/a/b/c", "/root", "/a/b", "/c");
@@ -597,25 +597,25 @@ TEST(path_extend) {
         _cleanup_free_ char *p = NULL;
 
         assert_se(path_extend(&p, "foo", "bar", "baz") == p);
-        assert_se(streq(p, "foo/bar/baz"));
+        ASSERT_STREQ(p, "foo/bar/baz");
 
         assert_se(path_extend(&p, "foo", "bar", "baz") == p);
-        assert_se(streq(p, "foo/bar/baz/foo/bar/baz"));
+        ASSERT_STREQ(p, "foo/bar/baz/foo/bar/baz");
 
         p = mfree(p);
         assert_se(path_extend(&p, "foo") == p);
-        assert_se(streq(p, "foo"));
+        ASSERT_STREQ(p, "foo");
 
         assert_se(path_extend(&p, "/foo") == p);
-        assert_se(streq(p, "foo/foo"));
+        ASSERT_STREQ(p, "foo/foo");
         assert_se(path_extend(&p, "/waaaah/wahhh//") == p);
-        assert_se(streq(p, "foo/foo/waaaah/wahhh//")); /* path_extend() does not drop redundant slashes */
+        ASSERT_STREQ(p, "foo/foo/waaaah/wahhh//"); /* path_extend() does not drop redundant slashes */
         assert_se(path_extend(&p, "/aaa/bbb/") == p);
-        assert_se(streq(p, "foo/foo/waaaah/wahhh///aaa/bbb/")); /* but not add an extra slash */
+        ASSERT_STREQ(p, "foo/foo/waaaah/wahhh///aaa/bbb/"); /* but not add an extra slash */
 
         assert_se(free_and_strdup(&p, "/") >= 0);
         assert_se(path_extend(&p, "foo") == p);
-        assert_se(streq(p, "/foo"));
+        ASSERT_STREQ(p, "/foo");
 }
 
 TEST(fsck_exists) {
@@ -637,7 +637,7 @@ static void test_path_make_relative_one(const char *from, const char *to, const
 
         r = path_make_relative(from, to, &z);
         assert_se((r >= 0) == !!expected);
-        assert_se(streq_ptr(z, expected));
+        ASSERT_STREQ(z, expected);
 }
 
 TEST(path_make_relative) {
@@ -663,7 +663,7 @@ static void test_path_make_relative_parent_one(const char *from, const char *to,
 
         r = path_make_relative_parent(from, to, &z);
         assert_se((r >= 0) == !!expected);
-        assert_se(streq_ptr(z, expected));
+        ASSERT_STREQ(z, expected);
 }
 
 TEST(path_make_relative_parent) {
@@ -687,7 +687,7 @@ TEST(path_strv_resolve) {
         _cleanup_strv_free_ char **search_dirs = NULL;
         _cleanup_strv_free_ char **absolute_dirs = NULL;
 
-        assert_se(mkdtemp(tmp_dir) != NULL);
+        ASSERT_NOT_NULL(mkdtemp(tmp_dir));
 
         search_dirs = strv_new("/dir1", "/dir2", "/dir3");
         assert_se(search_dirs);
@@ -702,9 +702,9 @@ TEST(path_strv_resolve) {
         assert_se(symlink("dir2", absolute_dirs[2]) == 0);
 
         path_strv_resolve(search_dirs, tmp_dir);
-        assert_se(streq(search_dirs[0], "/dir1"));
-        assert_se(streq(search_dirs[1], "/dir2"));
-        assert_se(streq(search_dirs[2], "/dir2"));
+        ASSERT_STREQ(search_dirs[0], "/dir1");
+        ASSERT_STREQ(search_dirs[1], "/dir2");
+        ASSERT_STREQ(search_dirs[2], "/dir2");
 
         assert_se(rm_rf(tmp_dir, REMOVE_ROOT|REMOVE_PHYSICAL) == 0);
 }
@@ -715,10 +715,10 @@ static void test_path_startswith_one(const char *path, const char *prefix, const
         log_debug("/* %s(%s, %s) */", __func__, path, prefix);
 
         p = path_startswith(path, prefix);
-        assert_se(streq_ptr(p, expected));
+        ASSERT_STREQ(p, expected);
         if (p) {
                 q = strjoina(skipped, p);
-                assert_se(streq(q, path));
+                ASSERT_STREQ(q, path);
                 assert_se(p == path + strlen(skipped));
         }
 }
@@ -793,17 +793,17 @@ TEST(file_in_same_dir) {
         assert_se(file_in_same_dir("/", "a", &t) == -EADDRNOTAVAIL);
 
         assert_se(file_in_same_dir("/", "/a", &t) >= 0);
-        assert_se(streq(t, "/a"));
+        ASSERT_STREQ(t, "/a");
         free(t);
 
         assert_se(file_in_same_dir("", "a", &t) == -EINVAL);
 
         assert_se(file_in_same_dir("a/", "x", &t) >= 0);
-        assert_se(streq(t, "x"));
+        ASSERT_STREQ(t, "x");
         free(t);
 
         assert_se(file_in_same_dir("bar/foo", "bar", &t) >= 0);
-        assert_se(streq(t, "bar/bar"));
+        ASSERT_STREQ(t, "bar/bar");
         free(t);
 }
 
@@ -986,23 +986,23 @@ TEST(path_find_last_component) {
 }
 
 TEST(last_path_component) {
-        assert_se(last_path_component(NULL) == NULL);
-        assert_se(streq(last_path_component("a/b/c"), "c"));
-        assert_se(streq(last_path_component("a/b/c/"), "c/"));
-        assert_se(streq(last_path_component("/"), "/"));
-        assert_se(streq(last_path_component("//"), "/"));
-        assert_se(streq(last_path_component("///"), "/"));
-        assert_se(streq(last_path_component("."), "."));
-        assert_se(streq(last_path_component("./."), "."));
-        assert_se(streq(last_path_component("././"), "./"));
-        assert_se(streq(last_path_component("././/"), ".//"));
-        assert_se(streq(last_path_component("/foo/a"), "a"));
-        assert_se(streq(last_path_component("/foo/a/"), "a/"));
-        assert_se(streq(last_path_component(""), ""));
-        assert_se(streq(last_path_component("a"), "a"));
-        assert_se(streq(last_path_component("a/"), "a/"));
-        assert_se(streq(last_path_component("/a"), "a"));
-        assert_se(streq(last_path_component("/a/"), "a/"));
+        ASSERT_NULL(last_path_component(NULL));
+        ASSERT_STREQ(last_path_component("a/b/c"), "c");
+        ASSERT_STREQ(last_path_component("a/b/c/"), "c/");
+        ASSERT_STREQ(last_path_component("/"), "/");
+        ASSERT_STREQ(last_path_component("//"), "/");
+        ASSERT_STREQ(last_path_component("///"), "/");
+        ASSERT_STREQ(last_path_component("."), ".");
+        ASSERT_STREQ(last_path_component("./."), ".");
+        ASSERT_STREQ(last_path_component("././"), "./");
+        ASSERT_STREQ(last_path_component("././/"), ".//");
+        ASSERT_STREQ(last_path_component("/foo/a"), "a");
+        ASSERT_STREQ(last_path_component("/foo/a/"), "a/");
+        ASSERT_STREQ(last_path_component(""), "");
+        ASSERT_STREQ(last_path_component("a"), "a");
+        ASSERT_STREQ(last_path_component("a/"), "a/");
+        ASSERT_STREQ(last_path_component("/a"), "a");
+        ASSERT_STREQ(last_path_component("/a/"), "a/");
 }
 
 static void test_path_extract_filename_one(const char *input, const char *output, int ret) {
@@ -1014,7 +1014,7 @@ static void test_path_extract_filename_one(const char *input, const char *output
                  strnull(input),
                  strnull(k), r < 0 ? STRERROR(r) : "-",
                  strnull(output), ret < 0 ? STRERROR(ret) : "-");
-        assert_se(streq_ptr(k, output));
+        ASSERT_STREQ(k, output);
         assert_se(r == ret);
 }
 
@@ -1058,7 +1058,7 @@ static void test_path_extract_directory_one(const char *input, const char *outpu
                  strnull(input),
                  strnull(k), r < 0 ? STRERROR(r) : "-",
                  strnull(output), STRERROR(ret));
-        assert_se(streq_ptr(k, output));
+        ASSERT_STREQ(k, output);
         assert_se(r == ret);
 
         /* Extra safety check: let's make sure that if we split out the filename too (and it works) the
@@ -1192,17 +1192,17 @@ TEST(hidden_or_backup_file) {
 }
 
 TEST(skip_dev_prefix) {
-        assert_se(streq(skip_dev_prefix("/"), "/"));
-        assert_se(streq(skip_dev_prefix("/dev"), ""));
-        assert_se(streq(skip_dev_prefix("/dev/"), ""));
-        assert_se(streq(skip_dev_prefix("/dev/foo"), "foo"));
-        assert_se(streq(skip_dev_prefix("/dev/foo/bar"), "foo/bar"));
-        assert_se(streq(skip_dev_prefix("//dev"), ""));
-        assert_se(streq(skip_dev_prefix("//dev//"), ""));
-        assert_se(streq(skip_dev_prefix("/dev///foo"), "foo"));
-        assert_se(streq(skip_dev_prefix("///dev///foo///bar"), "foo///bar"));
-        assert_se(streq(skip_dev_prefix("//foo"), "//foo"));
-        assert_se(streq(skip_dev_prefix("foo"), "foo"));
+        ASSERT_STREQ(skip_dev_prefix("/"), "/");
+        ASSERT_STREQ(skip_dev_prefix("/dev"), "");
+        ASSERT_STREQ(skip_dev_prefix("/dev/"), "");
+        ASSERT_STREQ(skip_dev_prefix("/dev/foo"), "foo");
+        ASSERT_STREQ(skip_dev_prefix("/dev/foo/bar"), "foo/bar");
+        ASSERT_STREQ(skip_dev_prefix("//dev"), "");
+        ASSERT_STREQ(skip_dev_prefix("//dev//"), "");
+        ASSERT_STREQ(skip_dev_prefix("/dev///foo"), "foo");
+        ASSERT_STREQ(skip_dev_prefix("///dev///foo///bar"), "foo///bar");
+        ASSERT_STREQ(skip_dev_prefix("//foo"), "//foo");
+        ASSERT_STREQ(skip_dev_prefix("foo"), "foo");
 }
 
 TEST(empty_or_root) {
@@ -1219,43 +1219,43 @@ TEST(empty_or_root) {
 }
 
 TEST(path_startswith_set) {
-        assert_se(streq_ptr(PATH_STARTSWITH_SET("/foo/bar", "/foo/quux", "/foo/bar", "/zzz"), ""));
-        assert_se(streq_ptr(PATH_STARTSWITH_SET("/foo/bar", "/foo/quux", "/foo/", "/zzz"), "bar"));
-        assert_se(streq_ptr(PATH_STARTSWITH_SET("/foo/bar", "/foo/quux", "/foo", "/zzz"), "bar"));
-        assert_se(streq_ptr(PATH_STARTSWITH_SET("/foo/bar", "/foo/quux", "/", "/zzz"), "foo/bar"));
-        assert_se(streq_ptr(PATH_STARTSWITH_SET("/foo/bar", "/foo/quux", "", "/zzz"), NULL));
-
-        assert_se(streq_ptr(PATH_STARTSWITH_SET("/foo/bar2", "/foo/quux", "/foo/bar", "/zzz"), NULL));
-        assert_se(streq_ptr(PATH_STARTSWITH_SET("/foo/bar2", "/foo/quux", "/foo/", "/zzz"), "bar2"));
-        assert_se(streq_ptr(PATH_STARTSWITH_SET("/foo/bar2", "/foo/quux", "/foo", "/zzz"), "bar2"));
-        assert_se(streq_ptr(PATH_STARTSWITH_SET("/foo/bar2", "/foo/quux", "/", "/zzz"), "foo/bar2"));
-        assert_se(streq_ptr(PATH_STARTSWITH_SET("/foo/bar2", "/foo/quux", "", "/zzz"), NULL));
-
-        assert_se(streq_ptr(PATH_STARTSWITH_SET("/foo2/bar", "/foo/quux", "/foo/bar", "/zzz"), NULL));
-        assert_se(streq_ptr(PATH_STARTSWITH_SET("/foo2/bar", "/foo/quux", "/foo/", "/zzz"), NULL));
-        assert_se(streq_ptr(PATH_STARTSWITH_SET("/foo2/bar", "/foo/quux", "/foo", "/zzz"), NULL));
-        assert_se(streq_ptr(PATH_STARTSWITH_SET("/foo2/bar", "/foo/quux", "/", "/zzz"), "foo2/bar"));
-        assert_se(streq_ptr(PATH_STARTSWITH_SET("/foo2/bar", "/foo/quux", "", "/zzz"), NULL));
+        ASSERT_STREQ(PATH_STARTSWITH_SET("/foo/bar", "/foo/quux", "/foo/bar", "/zzz"), "");
+        ASSERT_STREQ(PATH_STARTSWITH_SET("/foo/bar", "/foo/quux", "/foo/", "/zzz"), "bar");
+        ASSERT_STREQ(PATH_STARTSWITH_SET("/foo/bar", "/foo/quux", "/foo", "/zzz"), "bar");
+        ASSERT_STREQ(PATH_STARTSWITH_SET("/foo/bar", "/foo/quux", "/", "/zzz"), "foo/bar");
+        ASSERT_STREQ(PATH_STARTSWITH_SET("/foo/bar", "/foo/quux", "", "/zzz"), NULL);
+
+        ASSERT_STREQ(PATH_STARTSWITH_SET("/foo/bar2", "/foo/quux", "/foo/bar", "/zzz"), NULL);
+        ASSERT_STREQ(PATH_STARTSWITH_SET("/foo/bar2", "/foo/quux", "/foo/", "/zzz"), "bar2");
+        ASSERT_STREQ(PATH_STARTSWITH_SET("/foo/bar2", "/foo/quux", "/foo", "/zzz"), "bar2");
+        ASSERT_STREQ(PATH_STARTSWITH_SET("/foo/bar2", "/foo/quux", "/", "/zzz"), "foo/bar2");
+        ASSERT_STREQ(PATH_STARTSWITH_SET("/foo/bar2", "/foo/quux", "", "/zzz"), NULL);
+
+        ASSERT_STREQ(PATH_STARTSWITH_SET("/foo2/bar", "/foo/quux", "/foo/bar", "/zzz"), NULL);
+        ASSERT_STREQ(PATH_STARTSWITH_SET("/foo2/bar", "/foo/quux", "/foo/", "/zzz"), NULL);
+        ASSERT_STREQ(PATH_STARTSWITH_SET("/foo2/bar", "/foo/quux", "/foo", "/zzz"), NULL);
+        ASSERT_STREQ(PATH_STARTSWITH_SET("/foo2/bar", "/foo/quux", "/", "/zzz"), "foo2/bar");
+        ASSERT_STREQ(PATH_STARTSWITH_SET("/foo2/bar", "/foo/quux", "", "/zzz"), NULL);
 }
 
 TEST(path_startswith_strv) {
-        assert_se(streq_ptr(path_startswith_strv("/foo/bar", STRV_MAKE("/foo/quux", "/foo/bar", "/zzz")), ""));
-        assert_se(streq_ptr(path_startswith_strv("/foo/bar", STRV_MAKE("/foo/quux", "/foo/", "/zzz")), "bar"));
-        assert_se(streq_ptr(path_startswith_strv("/foo/bar", STRV_MAKE("/foo/quux", "/foo", "/zzz")), "bar"));
-        assert_se(streq_ptr(path_startswith_strv("/foo/bar", STRV_MAKE("/foo/quux", "/", "/zzz")), "foo/bar"));
-        assert_se(streq_ptr(path_startswith_strv("/foo/bar", STRV_MAKE("/foo/quux", "", "/zzz")), NULL));
-
-        assert_se(streq_ptr(path_startswith_strv("/foo/bar2", STRV_MAKE("/foo/quux", "/foo/bar", "/zzz")), NULL));
-        assert_se(streq_ptr(path_startswith_strv("/foo/bar2", STRV_MAKE("/foo/quux", "/foo/", "/zzz")), "bar2"));
-        assert_se(streq_ptr(path_startswith_strv("/foo/bar2", STRV_MAKE("/foo/quux", "/foo", "/zzz")), "bar2"));
-        assert_se(streq_ptr(path_startswith_strv("/foo/bar2", STRV_MAKE("/foo/quux", "/", "/zzz")), "foo/bar2"));
-        assert_se(streq_ptr(path_startswith_strv("/foo/bar2", STRV_MAKE("/foo/quux", "", "/zzz")), NULL));
-
-        assert_se(streq_ptr(path_startswith_strv("/foo2/bar", STRV_MAKE("/foo/quux", "/foo/bar", "/zzz")), NULL));
-        assert_se(streq_ptr(path_startswith_strv("/foo2/bar", STRV_MAKE("/foo/quux", "/foo/", "/zzz")), NULL));
-        assert_se(streq_ptr(path_startswith_strv("/foo2/bar", STRV_MAKE("/foo/quux", "/foo", "/zzz")), NULL));
-        assert_se(streq_ptr(path_startswith_strv("/foo2/bar", STRV_MAKE("/foo/quux", "/", "/zzz")), "foo2/bar"));
-        assert_se(streq_ptr(path_startswith_strv("/foo2/bar", STRV_MAKE("/foo/quux", "", "/zzz")), NULL));
+        ASSERT_STREQ(path_startswith_strv("/foo/bar", STRV_MAKE("/foo/quux", "/foo/bar", "/zzz")), "");
+        ASSERT_STREQ(path_startswith_strv("/foo/bar", STRV_MAKE("/foo/quux", "/foo/", "/zzz")), "bar");
+        ASSERT_STREQ(path_startswith_strv("/foo/bar", STRV_MAKE("/foo/quux", "/foo", "/zzz")), "bar");
+        ASSERT_STREQ(path_startswith_strv("/foo/bar", STRV_MAKE("/foo/quux", "/", "/zzz")), "foo/bar");
+        ASSERT_STREQ(path_startswith_strv("/foo/bar", STRV_MAKE("/foo/quux", "", "/zzz")), NULL);
+
+        ASSERT_STREQ(path_startswith_strv("/foo/bar2", STRV_MAKE("/foo/quux", "/foo/bar", "/zzz")), NULL);
+        ASSERT_STREQ(path_startswith_strv("/foo/bar2", STRV_MAKE("/foo/quux", "/foo/", "/zzz")), "bar2");
+        ASSERT_STREQ(path_startswith_strv("/foo/bar2", STRV_MAKE("/foo/quux", "/foo", "/zzz")), "bar2");
+        ASSERT_STREQ(path_startswith_strv("/foo/bar2", STRV_MAKE("/foo/quux", "/", "/zzz")), "foo/bar2");
+        ASSERT_STREQ(path_startswith_strv("/foo/bar2", STRV_MAKE("/foo/quux", "", "/zzz")), NULL);
+
+        ASSERT_STREQ(path_startswith_strv("/foo2/bar", STRV_MAKE("/foo/quux", "/foo/bar", "/zzz")), NULL);
+        ASSERT_STREQ(path_startswith_strv("/foo2/bar", STRV_MAKE("/foo/quux", "/foo/", "/zzz")), NULL);
+        ASSERT_STREQ(path_startswith_strv("/foo2/bar", STRV_MAKE("/foo/quux", "/foo", "/zzz")), NULL);
+        ASSERT_STREQ(path_startswith_strv("/foo2/bar", STRV_MAKE("/foo/quux", "/", "/zzz")), "foo2/bar");
+        ASSERT_STREQ(path_startswith_strv("/foo2/bar", STRV_MAKE("/foo/quux", "", "/zzz")), NULL);
 }
 
 static void test_path_glob_can_match_one(const char *pattern, const char *prefix, const char *expected) {
@@ -1264,7 +1264,7 @@ static void test_path_glob_can_match_one(const char *pattern, const char *prefix
         log_debug("%s(%s, %s, %s)", __func__, pattern, prefix, strnull(expected));
 
         assert_se(path_glob_can_match(pattern, prefix, &result) == !!expected);
-        assert_se(streq_ptr(result, expected));
+        ASSERT_STREQ(result, expected);
 }
 
 TEST(path_glob_can_match) {
index d1b1ce9170a39c30445e990265c535c818f2e3da..92c22d3b20d78d346f82131ccfbecbee1253f61d 100644 (file)
@@ -70,10 +70,10 @@ TEST(struct) {
         assert_se(q = prioq_new((compare_func_t) test_compare));
         assert_se(s = set_new(&test_hash_ops));
 
-        assert_se(prioq_peek(q) == NULL);
-        assert_se(prioq_peek_by_index(q, 0) == NULL);
-        assert_se(prioq_peek_by_index(q, 1) == NULL);
-        assert_se(prioq_peek_by_index(q, UINT_MAX) == NULL);
+        ASSERT_NULL(prioq_peek(q));
+        ASSERT_NULL(prioq_peek_by_index(q, 0));
+        ASSERT_NULL(prioq_peek_by_index(q, 1));
+        ASSERT_NULL(prioq_peek_by_index(q, UINT_MAX));
 
         for (i = 0; i < SET_SIZE; i++) {
                 assert_se(t = new0(struct test, 1));
@@ -87,7 +87,7 @@ TEST(struct) {
 
         for (i = 0; i < SET_SIZE; i++)
                 assert_se(prioq_peek_by_index(q, i));
-        assert_se(prioq_peek_by_index(q, SET_SIZE) == NULL);
+        ASSERT_NULL(prioq_peek_by_index(q, SET_SIZE));
 
         unsigned count = 0;
         PRIOQ_FOREACH_ITEM(q, t) {
index 8b5bbb079ea00524d890ed1de0df2394304f409a..420f3126363c725069d85a626b225070400845ed 100644 (file)
@@ -37,7 +37,7 @@ TEST(proc_cmdline_override) {
 
         /* First test if the overrides for /proc/cmdline still work */
         assert_se(proc_cmdline(&line) >= 0);
-        assert_se(streq(line, "foo_bar=quux wuff-piep=tuet zumm some_arg_with_space='foo bar' and_one_more=\"zzz aaa\""));
+        ASSERT_STREQ(line, "foo_bar=quux wuff-piep=tuet zumm some_arg_with_space='foo bar' and_one_more=\"zzz aaa\"");
         line = mfree(line);
         assert_se(proc_cmdline_strv(&args) >= 0);
         assert_se(strv_equal(args, STRV_MAKE("foo_bar=quux", "wuff-piep=tuet", "zumm", "some_arg_with_space=foo bar", "and_one_more=zzz aaa")));
@@ -57,7 +57,7 @@ TEST(proc_cmdline_override) {
         assert_se(putenv((char*) "SYSTEMD_EFI_OPTIONS=foo_bar=quux wuff-piep=tuet zumm some_arg_with_space='foo bar' and_one_more=\"zzz aaa\"") == 0);
 
         assert_se(proc_cmdline(&line) >= 0);
-        assert_se(streq(line, "hoge"));
+        ASSERT_STREQ(line, "hoge");
         line = mfree(line);
         assert_se(proc_cmdline_strv(&args) >= 0);
         assert_se(strv_equal(args, STRV_MAKE("hoge")));
@@ -83,13 +83,13 @@ static int parse_item_given(const char *key, const char *value, void *data) {
 
         log_info("%s: option <%s> = <%s>", __func__, key, strna(value));
         if (proc_cmdline_key_streq(key, "foo_bar"))
-                assert_se(streq(value, "quux"));
+                ASSERT_STREQ(value, "quux");
         else if (proc_cmdline_key_streq(key, "wuff-piep"))
-                assert_se(streq(value, "tuet "));
+                ASSERT_STREQ(value, "tuet ");
         else if (proc_cmdline_key_streq(key, "space"))
-                assert_se(streq(value, "x y z"));
+                ASSERT_STREQ(value, "x y z");
         else if (proc_cmdline_key_streq(key, "miepf"))
-                assert_se(streq(value, "uuu"));
+                ASSERT_STREQ(value, "uuu");
         else if (in_initrd() && *strip && proc_cmdline_key_streq(key, "zumm"))
                 assert_se(!value);
         else if (in_initrd() && !*strip && proc_cmdline_key_streq(key, "rd.zumm"))
@@ -231,13 +231,13 @@ TEST(proc_cmdline_get_key_many) {
                                             "doubleticks", &value6,
                                             "zummm", &value7) == 5);
 
-        assert_se(streq_ptr(value1, "quux"));
+        ASSERT_STREQ(value1, "quux");
         assert_se(!value2);
-        assert_se(streq_ptr(value3, "tuet"));
+        ASSERT_STREQ(value3, "tuet");
         assert_se(!value4);
-        assert_se(streq_ptr(value5, "one two"));
-        assert_se(streq_ptr(value6, " aaa aaa "));
-        assert_se(streq_ptr(value7, "\n"));
+        ASSERT_STREQ(value5, "one two");
+        ASSERT_STREQ(value6, " aaa aaa ");
+        ASSERT_STREQ(value7, "\n");
 }
 
 TEST(proc_cmdline_key_streq) {
index 027b2a401eb1d2a3dda7662938818effbfb29d09..c96bd4341bc226c879c5cd22d9b3e9bddf17a0f0 100644 (file)
@@ -176,7 +176,7 @@ static void test_pid_get_comm_escape_one(const char *input, const char *output)
 
         log_debug("got: <%s>", n);
 
-        assert_se(streq_ptr(n, output));
+        ASSERT_STREQ(n, output);
 }
 
 TEST(pid_get_comm_escape) {
@@ -236,14 +236,14 @@ TEST(personality) {
         assert_se(personality_to_string(PER_LINUX));
         assert_se(!personality_to_string(PERSONALITY_INVALID));
 
-        assert_se(streq(personality_to_string(PER_LINUX), architecture_to_string(native_architecture())));
+        ASSERT_STREQ(personality_to_string(PER_LINUX), architecture_to_string(native_architecture()));
 
         assert_se(personality_from_string(personality_to_string(PER_LINUX)) == PER_LINUX);
         assert_se(personality_from_string(architecture_to_string(native_architecture())) == PER_LINUX);
 
 #ifdef __x86_64__
-        assert_se(streq_ptr(personality_to_string(PER_LINUX), "x86-64"));
-        assert_se(streq_ptr(personality_to_string(PER_LINUX32), "x86"));
+        ASSERT_STREQ(personality_to_string(PER_LINUX), "x86-64");
+        ASSERT_STREQ(personality_to_string(PER_LINUX32), "x86");
 
         assert_se(personality_from_string("x86-64") == PER_LINUX);
         assert_se(personality_from_string("x86") == PER_LINUX32);
@@ -328,49 +328,49 @@ TEST(pid_get_cmdline_harder) {
 
         assert_se(pid_get_cmdline(0, SIZE_MAX, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
         log_debug("'%s'", line);
-        assert_se(streq(line, "[testa]"));
+        ASSERT_STREQ(line, "[testa]");
         line = mfree(line);
 
         assert_se(pid_get_cmdline(0, SIZE_MAX, PROCESS_CMDLINE_COMM_FALLBACK | PROCESS_CMDLINE_QUOTE, &line) >= 0);
         log_debug("'%s'", line);
-        assert_se(streq(line, "\"[testa]\"")); /* quoting is enabled here */
+        ASSERT_STREQ(line, "\"[testa]\""); /* quoting is enabled here */
         line = mfree(line);
 
         assert_se(pid_get_cmdline(0, 0, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
         log_debug("'%s'", line);
-        assert_se(streq(line, ""));
+        ASSERT_STREQ(line, "");
         line = mfree(line);
 
         assert_se(pid_get_cmdline(0, 1, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
-        assert_se(streq(line, "…"));
+        ASSERT_STREQ(line, "…");
         line = mfree(line);
 
         assert_se(pid_get_cmdline(0, 2, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
-        assert_se(streq(line, "[…"));
+        ASSERT_STREQ(line, "[…");
         line = mfree(line);
 
         assert_se(pid_get_cmdline(0, 3, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
-        assert_se(streq(line, "[t…"));
+        ASSERT_STREQ(line, "[t…");
         line = mfree(line);
 
         assert_se(pid_get_cmdline(0, 4, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
-        assert_se(streq(line, "[te…"));
+        ASSERT_STREQ(line, "[te…");
         line = mfree(line);
 
         assert_se(pid_get_cmdline(0, 5, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
-        assert_se(streq(line, "[tes…"));
+        ASSERT_STREQ(line, "[tes…");
         line = mfree(line);
 
         assert_se(pid_get_cmdline(0, 6, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
-        assert_se(streq(line, "[test…"));
+        ASSERT_STREQ(line, "[test…");
         line = mfree(line);
 
         assert_se(pid_get_cmdline(0, 7, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
-        assert_se(streq(line, "[testa]"));
+        ASSERT_STREQ(line, "[testa]");
         line = mfree(line);
 
         assert_se(pid_get_cmdline(0, 8, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
-        assert_se(streq(line, "[testa]"));
+        ASSERT_STREQ(line, "[testa]");
         line = mfree(line);
 
         assert_se(pid_get_cmdline_strv(0, PROCESS_CMDLINE_COMM_FALLBACK, &args) >= 0);
@@ -383,11 +383,11 @@ TEST(pid_get_cmdline_harder) {
 
         assert_se(pid_get_cmdline(0, SIZE_MAX, 0, &line) >= 0);
         log_debug("'%s'", line);
-        assert_se(streq(line, "foo bar"));
+        ASSERT_STREQ(line, "foo bar");
         line = mfree(line);
 
         assert_se(pid_get_cmdline(0, SIZE_MAX, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
-        assert_se(streq(line, "foo bar"));
+        ASSERT_STREQ(line, "foo bar");
         line = mfree(line);
 
         assert_se(pid_get_cmdline_strv(0, PROCESS_CMDLINE_COMM_FALLBACK, &args) >= 0);
@@ -397,87 +397,87 @@ TEST(pid_get_cmdline_harder) {
         assert_se(write(fd, "quux", 4) == 4);
         assert_se(pid_get_cmdline(0, SIZE_MAX, 0, &line) >= 0);
         log_debug("'%s'", line);
-        assert_se(streq(line, "foo bar quux"));
+        ASSERT_STREQ(line, "foo bar quux");
         line = mfree(line);
 
         assert_se(pid_get_cmdline(0, SIZE_MAX, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
         log_debug("'%s'", line);
-        assert_se(streq(line, "foo bar quux"));
+        ASSERT_STREQ(line, "foo bar quux");
         line = mfree(line);
 
         assert_se(pid_get_cmdline(0, 1, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
         log_debug("'%s'", line);
-        assert_se(streq(line, "…"));
+        ASSERT_STREQ(line, "…");
         line = mfree(line);
 
         assert_se(pid_get_cmdline(0, 2, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
         log_debug("'%s'", line);
-        assert_se(streq(line, "f…"));
+        ASSERT_STREQ(line, "f…");
         line = mfree(line);
 
         assert_se(pid_get_cmdline(0, 3, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
         log_debug("'%s'", line);
-        assert_se(streq(line, "fo…"));
+        ASSERT_STREQ(line, "fo…");
         line = mfree(line);
 
         assert_se(pid_get_cmdline(0, 4, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
         log_debug("'%s'", line);
-        assert_se(streq(line, "foo…"));
+        ASSERT_STREQ(line, "foo…");
         line = mfree(line);
 
         assert_se(pid_get_cmdline(0, 5, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
         log_debug("'%s'", line);
-        assert_se(streq(line, "foo …"));
+        ASSERT_STREQ(line, "foo …");
         line = mfree(line);
 
         assert_se(pid_get_cmdline(0, 6, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
         log_debug("'%s'", line);
-        assert_se(streq(line, "foo b…"));
+        ASSERT_STREQ(line, "foo b…");
         line = mfree(line);
 
         assert_se(pid_get_cmdline(0, 7, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
         log_debug("'%s'", line);
-        assert_se(streq(line, "foo ba…"));
+        ASSERT_STREQ(line, "foo ba…");
         line = mfree(line);
 
         assert_se(pid_get_cmdline(0, 8, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
         log_debug("'%s'", line);
-        assert_se(streq(line, "foo bar…"));
+        ASSERT_STREQ(line, "foo bar…");
         line = mfree(line);
 
         assert_se(pid_get_cmdline(0, 9, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
         log_debug("'%s'", line);
-        assert_se(streq(line, "foo bar …"));
+        ASSERT_STREQ(line, "foo bar …");
         line = mfree(line);
 
         assert_se(pid_get_cmdline(0, 10, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
         log_debug("'%s'", line);
-        assert_se(streq(line, "foo bar q…"));
+        ASSERT_STREQ(line, "foo bar q…");
         line = mfree(line);
 
         assert_se(pid_get_cmdline(0, 11, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
         log_debug("'%s'", line);
-        assert_se(streq(line, "foo bar qu…"));
+        ASSERT_STREQ(line, "foo bar qu…");
         line = mfree(line);
 
         assert_se(pid_get_cmdline(0, 12, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
         log_debug("'%s'", line);
-        assert_se(streq(line, "foo bar quux"));
+        ASSERT_STREQ(line, "foo bar quux");
         line = mfree(line);
 
         assert_se(pid_get_cmdline(0, 13, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
         log_debug("'%s'", line);
-        assert_se(streq(line, "foo bar quux"));
+        ASSERT_STREQ(line, "foo bar quux");
         line = mfree(line);
 
         assert_se(pid_get_cmdline(0, 14, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
         log_debug("'%s'", line);
-        assert_se(streq(line, "foo bar quux"));
+        ASSERT_STREQ(line, "foo bar quux");
         line = mfree(line);
 
         assert_se(pid_get_cmdline(0, 1000, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
         log_debug("'%s'", line);
-        assert_se(streq(line, "foo bar quux"));
+        ASSERT_STREQ(line, "foo bar quux");
         line = mfree(line);
 
         assert_se(pid_get_cmdline_strv(0, PROCESS_CMDLINE_COMM_FALLBACK, &args) >= 0);
@@ -491,22 +491,22 @@ TEST(pid_get_cmdline_harder) {
 
         assert_se(pid_get_cmdline(0, SIZE_MAX, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
         log_debug("'%s'", line);
-        assert_se(streq(line, "[aaaa bbbb cccc]"));
+        ASSERT_STREQ(line, "[aaaa bbbb cccc]");
         line = mfree(line);
 
         assert_se(pid_get_cmdline(0, 10, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
         log_debug("'%s'", line);
-        assert_se(streq(line, "[aaaa bbb…"));
+        ASSERT_STREQ(line, "[aaaa bbb…");
         line = mfree(line);
 
         assert_se(pid_get_cmdline(0, 11, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
         log_debug("'%s'", line);
-        assert_se(streq(line, "[aaaa bbbb…"));
+        ASSERT_STREQ(line, "[aaaa bbbb…");
         line = mfree(line);
 
         assert_se(pid_get_cmdline(0, 12, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
         log_debug("'%s'", line);
-        assert_se(streq(line, "[aaaa bbbb …"));
+        ASSERT_STREQ(line, "[aaaa bbbb …");
         line = mfree(line);
 
         assert_se(pid_get_cmdline_strv(0, PROCESS_CMDLINE_COMM_FALLBACK, &args) >= 0);
@@ -527,13 +527,13 @@ TEST(pid_get_cmdline_harder) {
         assert_se(pid_get_cmdline(0, SIZE_MAX, PROCESS_CMDLINE_QUOTE, &line) >= 0);
         log_debug("got: ==%s==", line);
         log_debug("exp: ==%s==", EXPECT1);
-        assert_se(streq(line, EXPECT1));
+        ASSERT_STREQ(line, EXPECT1);
         line = mfree(line);
 
         assert_se(pid_get_cmdline(0, SIZE_MAX, PROCESS_CMDLINE_QUOTE_POSIX, &line) >= 0);
         log_debug("got: ==%s==", line);
         log_debug("exp: ==%s==", EXPECT1p);
-        assert_se(streq(line, EXPECT1p));
+        ASSERT_STREQ(line, EXPECT1p);
         line = mfree(line);
 
         assert_se(pid_get_cmdline_strv(0, 0, &args) >= 0);
@@ -552,13 +552,13 @@ TEST(pid_get_cmdline_harder) {
         assert_se(pid_get_cmdline(0, SIZE_MAX, PROCESS_CMDLINE_QUOTE, &line) >= 0);
         log_debug("got: ==%s==", line);
         log_debug("exp: ==%s==", EXPECT2);
-        assert_se(streq(line, EXPECT2));
+        ASSERT_STREQ(line, EXPECT2);
         line = mfree(line);
 
         assert_se(pid_get_cmdline(0, SIZE_MAX, PROCESS_CMDLINE_QUOTE_POSIX, &line) >= 0);
         log_debug("got: ==%s==", line);
         log_debug("exp: ==%s==", EXPECT2p);
-        assert_se(streq(line, EXPECT2p));
+        ASSERT_STREQ(line, EXPECT2p);
         line = mfree(line);
 
         assert_se(pid_get_cmdline_strv(0, 0, &args) >= 0);
@@ -651,7 +651,7 @@ TEST(safe_fork) {
 
 TEST(pid_to_ptr) {
         assert_se(PTR_TO_PID(NULL) == 0);
-        assert_se(PID_TO_PTR(0) == NULL);
+        ASSERT_NULL(PID_TO_PTR(0));
 
         assert_se(PTR_TO_PID(PID_TO_PTR(1)) == 1);
         assert_se(PTR_TO_PID(PID_TO_PTR(2)) == 2);
index 2e901dc9bcfff46132f5288f3dd9a7715da789df..8d5ed717d978d3ec1567ac2b411674757a46b0a6 100644 (file)
@@ -107,7 +107,7 @@ TEST(normalize_recovery_key) {
         r = normalize_recovery_key("cdefVhij-cDefghij-cdefkhij-cdufghij-cdefgdij-cidefIhj-cdefNijR-cdVfguij",
                         &normalized_key2);
         assert_se(r == 0);
-        assert_se(streq(normalized_key2, "cdefvhij-cdefghij-cdefkhij-cdufghij-cdefgdij-cidefihj-cdefnijr-cdvfguij"));
+        ASSERT_STREQ(normalized_key2, "cdefvhij-cdefghij-cdefkhij-cdufghij-cdefgdij-cidefihj-cdefnijr-cdvfguij");
 
         /* Case 3: Invalid password length */
         r = normalize_recovery_key("1234-5678-90AB-CDEF-1234-5678-90AB-CDEF", &normalized_key1);
@@ -122,7 +122,7 @@ TEST(normalize_recovery_key) {
         r = normalize_recovery_key("BFGHICEHHIUVLKJIHFHEDlntruvcdefjiTUVKLNIJVTUTKJIHDFBCBGHIJHHFDBC",
                         &normalized_key3);
         assert(r == 0);
-        assert_se(streq(normalized_key3, "bfghiceh-hiuvlkji-hfhedlnt-ruvcdefj-ituvklni-jvtutkji-hdfbcbgh-ijhhfdbc"));
+        ASSERT_STREQ(normalized_key3, "bfghiceh-hiuvlkji-hfhedlnt-ruvcdefj-ituvklni-jvtutkji-hdfbcbgh-ijhhfdbc");
 
         /* Case 6: Minimum password length */
         r = normalize_recovery_key("", &normalized_key1);
index f861b27c12a7b3411bb6cebaaf788cd37ea1fc00..56634a854a7329f16b6edbacb2c79ca2843748bb 100644 (file)
@@ -16,7 +16,7 @@ TEST(replace_var) {
 
         assert_se(r = replace_var("@@@foobar@xyz@HALLO@foobar@test@@testtest@TEST@...@@@", lookup, NULL));
         puts(r);
-        assert_se(streq(r, "@@@foobar@xyz<<<HALLO>>>foobar@test@@testtest<<<TEST>>>...@@@"));
+        ASSERT_STREQ(r, "@@@foobar@xyz<<<HALLO>>>foobar@test@@testtest<<<TEST>>>...@@@");
         free(r);
 }
 
@@ -25,7 +25,7 @@ TEST(strreplace) {
 
         assert_se(r = strreplace("XYZFFFFXYZFFFFXYZ", "XYZ", "ABC"));
         puts(r);
-        assert_se(streq(r, "ABCFFFFABCFFFFABC"));
+        ASSERT_STREQ(r, "ABCFFFFABCFFFFABC");
         free(r);
 }
 
index e14f27df7935c75312274972bfd118b679fdd901..fc9e89c46670112cff4655cffd95446e1c3f43c7 100644 (file)
@@ -34,7 +34,7 @@ static void test_rlimit_parse_format_one(int resource, const char *string, rlim_
         assert_se(rl.rlim_max == hard);
 
         assert_se(rlimit_format(&rl, &f) >= 0);
-        assert_se(streq(formatted, f));
+        ASSERT_STREQ(formatted, f);
 
         assert_se(rlimit_parse(resource, formatted, &rl2) >= 0);
         assert_se(memcmp(&rl, &rl2, sizeof(struct rlimit)) == 0);
@@ -118,8 +118,8 @@ TEST(setrlimit) {
         new.rlim_max = old.rlim_max;
         assert_se(setrlimit(RLIMIT_NOFILE, &new) >= 0);
 
-        assert_se(streq_ptr(rlimit_to_string(RLIMIT_NOFILE), "NOFILE"));
-        assert_se(rlimit_to_string(-1) == NULL);
+        ASSERT_STREQ(rlimit_to_string(RLIMIT_NOFILE), "NOFILE");
+        ASSERT_NULL(rlimit_to_string(-1));
 
         assert_se(getrlimit(RLIMIT_NOFILE, &old) == 0);
         assert_se(setrlimit_closest(RLIMIT_NOFILE, &old) == 0);
index 4d3021cf9bd8b73ad02d83069231747b5fd7890c..74d950ad1c0a0a34af5346c7ea704254c8b35f39 100644 (file)
@@ -52,40 +52,40 @@ TEST(parse_syscall_and_errno) {
         int e;
 
         assert_se(parse_syscall_and_errno("uname:EILSEQ", &n, &e) >= 0);
-        assert_se(streq(n, "uname"));
+        ASSERT_STREQ(n, "uname");
         assert_se(e == errno_from_name("EILSEQ") && e >= 0);
         n = mfree(n);
 
         assert_se(parse_syscall_and_errno("uname:EINVAL", &n, &e) >= 0);
-        assert_se(streq(n, "uname"));
+        ASSERT_STREQ(n, "uname");
         assert_se(e == errno_from_name("EINVAL") && e >= 0);
         n = mfree(n);
 
         assert_se(parse_syscall_and_errno("@sync:4095", &n, &e) >= 0);
-        assert_se(streq(n, "@sync"));
+        ASSERT_STREQ(n, "@sync");
         assert_se(e == 4095);
         n = mfree(n);
 
         /* If errno is omitted, then e is set to -1 */
         assert_se(parse_syscall_and_errno("mount", &n, &e) >= 0);
-        assert_se(streq(n, "mount"));
+        ASSERT_STREQ(n, "mount");
         assert_se(e == -1);
         n = mfree(n);
 
         /* parse_syscall_and_errno() does not check the syscall name is valid or not. */
         assert_se(parse_syscall_and_errno("hoge:255", &n, &e) >= 0);
-        assert_se(streq(n, "hoge"));
+        ASSERT_STREQ(n, "hoge");
         assert_se(e == 255);
         n = mfree(n);
 
         /* 0 is also a valid errno. */
         assert_se(parse_syscall_and_errno("hoge:0", &n, &e) >= 0);
-        assert_se(streq(n, "hoge"));
+        ASSERT_STREQ(n, "hoge");
         assert_se(e == 0);
         n = mfree(n);
 
         assert_se(parse_syscall_and_errno("hoge:kill", &n, &e) >= 0);
-        assert_se(streq(n, "hoge"));
+        ASSERT_STREQ(n, "hoge");
         assert_se(e == SECCOMP_ERROR_NUMBER_KILL);
         n = mfree(n);
 
@@ -151,7 +151,7 @@ TEST(architecture_table) {
                 assert_se(seccomp_arch_from_string(n, &c) >= 0);
                 n2 = seccomp_arch_to_string(c);
                 log_info("seccomp-arch: %s → 0x%"PRIx32" → %s", n, c, n2);
-                assert_se(streq_ptr(n, n2));
+                ASSERT_STREQ(n, n2);
         }
 }
 
@@ -231,11 +231,11 @@ TEST(filter_sets) {
 TEST(filter_sets_ordered) {
         /* Ensure "@default" always remains at the beginning of the list */
         assert_se(SYSCALL_FILTER_SET_DEFAULT == 0);
-        assert_se(streq(syscall_filter_sets[0].name, "@default"));
+        ASSERT_STREQ(syscall_filter_sets[0].name, "@default");
 
         /* Ensure "@known" always remains at the end of the list */
         assert_se(SYSCALL_FILTER_SET_KNOWN == _SYSCALL_FILTER_SET_MAX - 1);
-        assert_se(streq(syscall_filter_sets[SYSCALL_FILTER_SET_KNOWN].name, "@known"));
+        ASSERT_STREQ(syscall_filter_sets[SYSCALL_FILTER_SET_KNOWN].name, "@known");
 
         for (size_t i = 0; i < _SYSCALL_FILTER_SET_MAX; i++) {
                 const char *p = NULL;
@@ -294,7 +294,7 @@ TEST(restrict_namespace) {
         s = mfree(s);
 
         assert_se(namespace_flags_to_string(NAMESPACE_FLAGS_ALL, &s) == 0);
-        assert_se(streq(s, "cgroup ipc net mnt pid user uts"));
+        ASSERT_STREQ(s, "cgroup ipc net mnt pid user uts");
         assert_se(namespace_flags_from_string(s, &ul) == 0 && ul == NAMESPACE_FLAGS_ALL);
         s = mfree(s);
 
@@ -624,17 +624,17 @@ TEST(memory_deny_write_execute_mmap) {
         if (pid == 0) {
                 void *p;
 
-                p = mmap(NULL, page_size(), PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_ANONYMOUS, -1,0);
+                p = mmap(NULL, page_size(), PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
                 assert_se(p != MAP_FAILED);
                 assert_se(munmap(p, page_size()) >= 0);
 
-                p = mmap(NULL, page_size(), PROT_WRITE|PROT_READ, MAP_PRIVATE|MAP_ANONYMOUS, -1,0);
+                p = mmap(NULL, page_size(), PROT_WRITE|PROT_READ, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
                 assert_se(p != MAP_FAILED);
                 assert_se(munmap(p, page_size()) >= 0);
 
                 assert_se(seccomp_memory_deny_write_execute() >= 0);
 
-                p = mmap(NULL, page_size(), PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_ANONYMOUS, -1,0);
+                p = mmap(NULL, page_size(), PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
 #if defined(__x86_64__) || defined(__i386__) || defined(__powerpc64__) || defined(__arm__) || defined(__aarch64__) || defined(__loongarch_lp64)
                 assert_se(p == MAP_FAILED);
                 assert_se(errno == EPERM);
@@ -644,7 +644,7 @@ TEST(memory_deny_write_execute_mmap) {
                 if (p != MAP_FAILED)
                         assert_se(munmap(p, page_size()) == 0);
 
-                p = mmap(NULL, page_size(), PROT_WRITE|PROT_READ, MAP_PRIVATE|MAP_ANONYMOUS, -1,0);
+                p = mmap(NULL, page_size(), PROT_WRITE|PROT_READ, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
                 assert_se(p != MAP_FAILED);
                 assert_se(munmap(p, page_size()) >= 0);
 
index 27e6a20e09d40652a0b622bad72a4038c657e592..3d353aecda051196682bd89581b307b74f88510f 100644 (file)
@@ -31,7 +31,7 @@ TEST(secure_bits_basic) {
                 assert_se(secure_bits_is_valid(r));
                 assert_se(secure_bits_to_string_alloc(r, &s) >= 0);
                 printf("%s = 0x%x = %s\n", *bit, (unsigned)r, s);
-                assert_se(streq(*bit, s));
+                ASSERT_STREQ(*bit, s);
         }
 
         /* Ditto, but with all bits at once */
@@ -42,7 +42,7 @@ TEST(secure_bits_basic) {
         assert_se(secure_bits_is_valid(r));
         assert_se(secure_bits_to_string_alloc(r, &str) >= 0);
         printf("%s = 0x%x = %s\n", joined, (unsigned)r, str);
-        assert_se(streq(joined, str));
+        ASSERT_STREQ(joined, str);
 
         str = mfree(str);
 
@@ -61,11 +61,11 @@ TEST(secure_bits_basic) {
 
         /* Bits to string with check */
         assert_se(secure_bits_to_string_alloc_with_check(INT_MAX, &str) == -EINVAL);
-        assert_se(str == NULL);
+        ASSERT_NULL(str);
         assert_se(secure_bits_to_string_alloc_with_check(
                                 (1 << SECURE_KEEP_CAPS) | (1 << SECURE_KEEP_CAPS_LOCKED),
                                 &str) >= 0);
-        assert_se(streq(str, "keep-caps keep-caps-locked"));
+        ASSERT_STREQ(str, "keep-caps keep-caps-locked");
 }
 
 TEST(secure_bits_mix) {
@@ -90,7 +90,7 @@ TEST(secure_bits_mix) {
                 assert_se(secure_bits_is_valid(r));
                 assert_se(secure_bits_to_string_alloc(r, &str) >= 0);
                 printf("%s = 0x%x = %s\n", s->input, (unsigned)r, str);
-                assert_se(streq(s->expected, str));
+                ASSERT_STREQ(s->expected, str);
         }
 }
 
index 8f744727c673b7c8bc40cad18cc246c243869325..40abf6cdcf6acc4496d7c5af74e3b5c762c0074b 100644 (file)
@@ -32,13 +32,13 @@ TEST(serialize_item) {
 
         _cleanup_free_ char *line1 = NULL, *line2 = NULL, *line3 = NULL, *line4 = NULL;
         assert_se(read_line(f, LONG_LINE_MAX, &line1) > 0);
-        assert_se(streq(line1, "a=bbb"));
+        ASSERT_STREQ(line1, "a=bbb");
         assert_se(read_line(f, LONG_LINE_MAX, &line2) > 0);
-        assert_se(streq(line2, "a=bbb"));
+        ASSERT_STREQ(line2, "a=bbb");
         assert_se(read_line(f, LONG_LINE_MAX, &line3) > 0);
-        assert_se(streq(line3, "c=yes"));
+        ASSERT_STREQ(line3, "c=yes");
         assert_se(read_line(f, LONG_LINE_MAX, &line4) == 0);
-        assert_se(streq(line4, ""));
+        ASSERT_STREQ(line4, "");
 }
 
 TEST(serialize_item_escaped) {
@@ -59,11 +59,11 @@ TEST(serialize_item_escaped) {
 
         _cleanup_free_ char *line1 = NULL, *line2 = NULL, *line3 = NULL;
         assert_se(read_line(f, LONG_LINE_MAX, &line1) > 0);
-        assert_se(streq(line1, "a=bbb"));
+        ASSERT_STREQ(line1, "a=bbb");
         assert_se(read_line(f, LONG_LINE_MAX, &line2) > 0);
-        assert_se(streq(line2, "a=bbb"));
+        ASSERT_STREQ(line2, "a=bbb");
         assert_se(read_line(f, LONG_LINE_MAX, &line3) == 0);
-        assert_se(streq(line3, ""));
+        ASSERT_STREQ(line3, "");
 }
 
 TEST(serialize_usec) {
@@ -83,7 +83,7 @@ TEST(serialize_usec) {
         usec_t x;
 
         assert_se(read_line(f, LONG_LINE_MAX, &line1) > 0);
-        assert_se(streq(line1, "usec2=0"));
+        ASSERT_STREQ(line1, "usec2=0");
         assert_se(deserialize_usec(line1 + 6, &x) == 0);
         assert_se(x == 0);
 
@@ -204,7 +204,7 @@ TEST(serialize_item_hexmem) {
 
         _cleanup_free_ char *line = NULL;
         assert_se(read_line(f, LONG_LINE_MAX, &line) > 0);
-        assert_se(streq(line, "a=ffffff"));
+        ASSERT_STREQ(line, "a=ffffff");
 
 }
 
@@ -222,7 +222,7 @@ TEST(serialize_item_base64mem) {
 
         _cleanup_free_ char *line = NULL;
         assert_se(read_line(f, LONG_LINE_MAX, &line) > 0);
-        assert_se(streq(line, "a=////"));
+        ASSERT_STREQ(line, "a=////");
 }
 
 TEST(serialize_string_set) {
index af5b5553e069b4b4ce73c00a55304ca65e1be191..23bac0255a84ebd996579ca72ad805b49426a1af 100644 (file)
@@ -231,28 +231,28 @@ TEST(set_strjoin) {
         /* Single entry */
         assert_se(set_put_strdup(&m, "aaa") == 1);
         assert_se(set_strjoin(m, NULL, false, &joined) >= 0);
-        assert_se(streq(joined, "aaa"));
+        ASSERT_STREQ(joined, "aaa");
         joined = mfree(joined);
         assert_se(set_strjoin(m, "", false, &joined) >= 0);
-        assert_se(streq(joined, "aaa"));
+        ASSERT_STREQ(joined, "aaa");
         joined = mfree(joined);
         assert_se(set_strjoin(m, " ", false, &joined) >= 0);
-        assert_se(streq(joined, "aaa"));
+        ASSERT_STREQ(joined, "aaa");
         joined = mfree(joined);
         assert_se(set_strjoin(m, "xxx", false, &joined) >= 0);
-        assert_se(streq(joined, "aaa"));
+        ASSERT_STREQ(joined, "aaa");
         joined = mfree(joined);
         assert_se(set_strjoin(m, NULL, true, &joined) >= 0);
-        assert_se(streq(joined, "aaa"));
+        ASSERT_STREQ(joined, "aaa");
         joined = mfree(joined);
         assert_se(set_strjoin(m, "", true, &joined) >= 0);
-        assert_se(streq(joined, "aaa"));
+        ASSERT_STREQ(joined, "aaa");
         joined = mfree(joined);
         assert_se(set_strjoin(m, " ", true, &joined) >= 0);
-        assert_se(streq(joined, " aaa "));
+        ASSERT_STREQ(joined, " aaa ");
         joined = mfree(joined);
         assert_se(set_strjoin(m, "xxx", true, &joined) >= 0);
-        assert_se(streq(joined, "xxxaaaxxx"));
+        ASSERT_STREQ(joined, "xxxaaaxxx");
 
         /* Two entries */
         assert_se(set_put_strdup(&m, "bbb") == 1);
index f168e4c355be50e01fd3c570932174a3160d7604..1acc893c744d357ce13f49f02b38ab16958855e4 100644 (file)
@@ -31,7 +31,7 @@ static void test_sha256_one(const char *key, const char *expect) {
                         sha256_finish_ctx(&ctx, result + j);
 
                         hex_result = hexmem(result + j, SHA256_DIGEST_SIZE);
-                        assert_se(streq_ptr(hex_result, expect));
+                        ASSERT_STREQ(hex_result, expect);
                 }
         }
 }
index 112fec63461e457fca25df33479f26dacb4d4640..dc1a8fb71cc5dfed30663e0ff3c1f3c990fc0ab3 100644 (file)
@@ -52,10 +52,10 @@ TEST(sleep_supported) {
         log_info("Standby configured: %s", yes_no(sleep_state_supported(standby) > 0));
         log_info("Suspend configured: %s", yes_no(sleep_state_supported(mem) > 0));
         log_info("Hibernate configured: %s", yes_no(sleep_state_supported(disk) > 0));
-        log_info("Hibernate+Suspend (Hybrid-Sleep) configured: %s", yes_no(sleep_mode_supported(suspend) > 0));
-        log_info("Hibernate+Reboot configured: %s", yes_no(sleep_mode_supported(reboot) > 0));
-        log_info("Hibernate+Platform configured: %s", yes_no(sleep_mode_supported(platform) > 0));
-        log_info("Hibernate+Shutdown configured: %s", yes_no(sleep_mode_supported(shutdown) > 0));
+        log_info("Hibernate+Suspend (Hybrid-Sleep) configured: %s", yes_no(sleep_mode_supported("/sys/power/disk", suspend) > 0));
+        log_info("Hibernate+Reboot configured: %s", yes_no(sleep_mode_supported("/sys/power/disk", reboot) > 0));
+        log_info("Hibernate+Platform configured: %s", yes_no(sleep_mode_supported("/sys/power/disk", platform) > 0));
+        log_info("Hibernate+Shutdown configured: %s", yes_no(sleep_mode_supported("/sys/power/disk", shutdown) > 0));
         log_info("Freeze configured: %s", yes_no(sleep_state_supported(freeze) > 0));
 
         log_info("/= high-level sleep verbs =/");
index 84a897801a5595546d0665d3878b865e4fb9a837..fb65573e7a7908e7c1c2de60b23cee9943359fb3 100644 (file)
@@ -66,7 +66,7 @@ static int test_socket_bind(
         fputc('\n', stderr);
 
         exec_start = strjoin("-timeout --preserve-status -sSIGTERM 1s ", netcat_path, " -l ", port, " -vv");
-        assert_se(exec_start != NULL);
+        ASSERT_NOT_NULL(exec_start);
 
         r = config_parse_exec(u->id, "filename", 1, "Service", 1, "ExecStart",
                         SERVICE_EXEC_START, exec_start, SERVICE(u)->exec_command, u);
index 1cbfe6d66bd4a01f79d3a681dd17c57146598a01..1849585cc823e8487a1235c777c62ad189919ac0 100644 (file)
@@ -29,7 +29,7 @@ static void test_socket_address_parse_one(const char *in, int ret, int family, c
         assert_se(r == ret);
         if (r >= 0) {
                 assert_se(a.sockaddr.sa.sa_family == family);
-                assert_se(streq(out, expected ?: in));
+                ASSERT_STREQ(out, expected ?: in);
         }
 }
 
@@ -199,7 +199,7 @@ TEST(socket_address_get_path) {
         assert_se(!socket_address_get_path(&a));
 
         assert_se(socket_address_parse(&a, "/foo/bar") >= 0);
-        assert_se(streq(socket_address_get_path(&a), "/foo/bar"));
+        ASSERT_STREQ(socket_address_get_path(&a), "/foo/bar");
 
         assert_se(socket_address_parse(&a, "vsock:2:1234") >= 0);
         assert_se(!socket_address_get_path(&a));
@@ -235,7 +235,7 @@ static void test_in_addr_ifindex_to_string_one(int f, const char *a, int ifindex
         assert_se(in_addr_from_string(f, a, &ua) >= 0);
         assert_se(in_addr_ifindex_to_string(f, &ua, ifindex, &r) >= 0);
         printf("test_in_addr_ifindex_to_string_one: %s == %s\n", b, r);
-        assert_se(streq(b, r));
+        ASSERT_STREQ(b, r);
 
         assert_se(in_addr_ifindex_from_string_auto(b, &ff, &uuaa, &ifindex2) >= 0);
         assert_se(ff == f);
@@ -282,7 +282,7 @@ static void test_in_addr_ifindex_name_from_string_auto_one(const char *a, const
         _cleanup_free_ char *server_name = NULL;
 
         assert_se(in_addr_ifindex_name_from_string_auto(a, &family, &ua, &ifindex, &server_name) >= 0);
-        assert_se(streq_ptr(server_name, expected));
+        ASSERT_STREQ(server_name, expected);
 }
 
 TEST(in_addr_ifindex_name_from_string_auto) {
@@ -307,9 +307,9 @@ static void test_in_addr_port_ifindex_name_from_string_auto_one(const char *str,
                 assert_se(family == f);
                 assert_se(port == p);
                 assert_se(ifindex == i);
-                assert_se(streq_ptr(server_name, name));
+                ASSERT_STREQ(server_name, name);
                 assert_se(in_addr_port_ifindex_name_to_string(f, &a, p, i, name, &x) >= 0);
-                assert_se(streq(str_repr ?: str, x));
+                ASSERT_STREQ(str_repr ?: str, x);
         }
 
         if (port > 0)
@@ -319,9 +319,9 @@ static void test_in_addr_port_ifindex_name_from_string_auto_one(const char *str,
                 assert_se(in_addr_port_ifindex_name_from_string_auto(str, &f, &a, NULL, &i, &name) == 0);
                 assert_se(family == f);
                 assert_se(ifindex == i);
-                assert_se(streq_ptr(server_name, name));
+                ASSERT_STREQ(server_name, name);
                 assert_se(in_addr_port_ifindex_name_to_string(f, &a, 0, i, name, &x) >= 0);
-                assert_se(streq(str_repr ?: str, x));
+                ASSERT_STREQ(str_repr ?: str, x);
         }
 
         if (ifindex > 0)
@@ -331,9 +331,9 @@ static void test_in_addr_port_ifindex_name_from_string_auto_one(const char *str,
                 assert_se(in_addr_port_ifindex_name_from_string_auto(str, &f, &a, &p, NULL, &name) == 0);
                 assert_se(family == f);
                 assert_se(port == p);
-                assert_se(streq_ptr(server_name, name));
+                ASSERT_STREQ(server_name, name);
                 assert_se(in_addr_port_ifindex_name_to_string(f, &a, p, 0, name, &x) >= 0);
-                assert_se(streq(str_repr ?: str, x));
+                ASSERT_STREQ(str_repr ?: str, x);
         }
 
         if (server_name)
@@ -345,7 +345,7 @@ static void test_in_addr_port_ifindex_name_from_string_auto_one(const char *str,
                 assert_se(port == p);
                 assert_se(ifindex == i);
                 assert_se(in_addr_port_ifindex_name_to_string(f, &a, p, i, NULL, &x) >= 0);
-                assert_se(streq(str_repr ?: str, x));
+                ASSERT_STREQ(str_repr ?: str, x);
         }
 }
 
index 4233ca652797775aa4e57262539dedc4be564334..bcb3dfd05586afcc48ad7e526baac0f142bf43e1 100644 (file)
@@ -69,7 +69,7 @@ static void test_socket_print_unix_one(const char *in, size_t len_in, const char
         assert_se(socket_address_print(&a, &out) >= 0);
         assert_se(c = cescape(in));
         log_info("\"%s\" → \"%s\" (expect \"%s\")", in, out, expected);
-        assert_se(streq(out, expected));
+        ASSERT_STREQ(out, expected);
 }
 
 TEST(socket_print_unix) {
@@ -168,7 +168,7 @@ TEST(getpeercred_getpeergroups) {
                 uid_t test_uid;
                 gid_t test_gid;
                 struct ucred ucred;
-                int pair[2];
+                int pair[2] = EBADF_PAIR;
 
                 if (geteuid() == 0) {
                         test_uid = 1;
@@ -220,7 +220,7 @@ TEST(getpeercred_getpeergroups) {
 
 TEST(passfd_read) {
         static const char file_contents[] = "test contents for passfd";
-        _cleanup_close_pair_ int pair[2];
+        _cleanup_close_pair_ int pair[2] = EBADF_PAIR;
         int r;
 
         assert_se(socketpair(AF_UNIX, SOCK_DGRAM, 0, pair) >= 0);
@@ -246,7 +246,7 @@ TEST(passfd_read) {
         /* Parent */
         char buf[64];
         struct iovec iov = IOVEC_MAKE(buf, sizeof(buf)-1);
-        _cleanup_close_ int fd;
+        _cleanup_close_ int fd = -EBADF;
 
         pair[1] = safe_close(pair[1]);
 
@@ -256,11 +256,11 @@ TEST(passfd_read) {
         r = read(fd, buf, sizeof(buf)-1);
         assert_se(r >= 0);
         buf[r] = 0;
-        assert_se(streq(buf, file_contents));
+        ASSERT_STREQ(buf, file_contents);
 }
 
 TEST(passfd_contents_read) {
-        _cleanup_close_pair_ int pair[2];
+        _cleanup_close_pair_ int pair[2] = EBADF_PAIR;
         static const char file_contents[] = "test contents in the file";
         static const char wire_contents[] = "test contents on the wire";
         int r;
@@ -298,17 +298,17 @@ TEST(passfd_contents_read) {
         k = receive_one_fd_iov(pair[0], &iov, 1, MSG_DONTWAIT, &fd);
         assert_se(k > 0);
         buf[k] = 0;
-        assert_se(streq(buf, wire_contents));
+        ASSERT_STREQ(buf, wire_contents);
 
         assert_se(fd >= 0);
         r = read(fd, buf, sizeof(buf)-1);
         assert_se(r >= 0);
         buf[r] = 0;
-        assert_se(streq(buf, file_contents));
+        ASSERT_STREQ(buf, file_contents);
 }
 
 TEST(pass_many_fds_contents_read) {
-        _cleanup_close_pair_ int pair[2];
+        _cleanup_close_pair_ int pair[2] = EBADF_PAIR;
         static const char file_contents[][STRLEN("test contents in the fileX") + 1] = {
                 "test contents in the file0",
                 "test contents in the file1",
@@ -358,7 +358,7 @@ TEST(pass_many_fds_contents_read) {
         k = receive_many_fds_iov(pair[0], &iov, 1, &fds, &n_fds, MSG_DONTWAIT);
         assert_se(k > 0);
         buf[k] = 0;
-        assert_se(streq(buf, wire_contents));
+        ASSERT_STREQ(buf, wire_contents);
 
         assert_se(n_fds == 3);
 
@@ -367,13 +367,13 @@ TEST(pass_many_fds_contents_read) {
                 r = read(fds[i], buf, sizeof(buf)-1);
                 assert_se(r >= 0);
                 buf[r] = 0;
-                assert_se(streq(buf, file_contents[i]));
+                ASSERT_STREQ(buf, file_contents[i]);
                 safe_close(fds[i]);
         }
 }
 
 TEST(receive_nopassfd) {
-        _cleanup_close_pair_ int pair[2];
+        _cleanup_close_pair_ int pair[2] = EBADF_PAIR;
         static const char wire_contents[] = "no fd passed here";
         int r;
 
@@ -403,14 +403,14 @@ TEST(receive_nopassfd) {
         k = receive_one_fd_iov(pair[0], &iov, 1, MSG_DONTWAIT, &fd);
         assert_se(k > 0);
         buf[k] = 0;
-        assert_se(streq(buf, wire_contents));
+        ASSERT_STREQ(buf, wire_contents);
 
         /* no fd passed here, confirm it was reset */
         assert_se(fd == -EBADF);
 }
 
 TEST(send_nodata_nofd) {
-        _cleanup_close_pair_ int pair[2];
+        _cleanup_close_pair_ int pair[2] = EBADF_PAIR;
         int r;
 
         assert_se(socketpair(AF_UNIX, SOCK_DGRAM, 0, pair) >= 0);
@@ -443,7 +443,7 @@ TEST(send_nodata_nofd) {
 }
 
 TEST(send_emptydata) {
-        _cleanup_close_pair_ int pair[2];
+        _cleanup_close_pair_ int pair[2] = EBADF_PAIR;
         int r;
 
         assert_se(socketpair(AF_UNIX, SOCK_DGRAM, 0, pair) >= 0);
index d6a8b79aa4ac6693f4c08b0d5ba0bf40731fa576..7d25969bb07a7cda663f1e0062feb80d4c70ed76 100644 (file)
@@ -16,7 +16,7 @@ static void test_specifier_escape_one(const char *a, const char *b) {
         _cleanup_free_ char *x = NULL;
 
         x = specifier_escape(a);
-        assert_se(streq_ptr(x, b));
+        ASSERT_STREQ(x, b);
 }
 
 TEST(specifier_escape) {
@@ -73,7 +73,7 @@ TEST(specifier_printf) {
         assert_se(w);
 
         puts(w);
-        assert_se(streq(w, "xxx a=AAAA b=BBBB e= yyy"));
+        ASSERT_STREQ(w, "xxx a=AAAA b=BBBB e= yyy");
 
         free(w);
         r = specifier_printf("boot=%b, host=%H, pretty=%q, version=%v, arch=%a, empty=%e", SIZE_MAX, table, NULL, NULL, &w);
@@ -107,7 +107,7 @@ TEST(specifier_real_path) {
 
         /* /dev/initctl should normally be a symlink to /run/initctl */
         if (inode_same("/dev/initctl", "/run/initctl", 0) > 0)
-                assert_se(streq(w, "p=/dev/initctl y=/run/initctl Y=/run w=/dev/tty W=/dev"));
+                ASSERT_STREQ(w, "p=/dev/initctl y=/run/initctl Y=/run w=/dev/tty W=/dev");
 }
 
 TEST(specifier_real_path_missing_file) {
@@ -138,6 +138,8 @@ TEST(specifiers) {
                 xsprintf(spec, "%%%c", s->specifier);
 
                 r = specifier_printf(spec, SIZE_MAX, specifier_table, NULL, NULL, &resolved);
+                if (s->specifier == 'A' && r == -EUNATCH) /* os-release might be missing in build chroots */
+                        continue;
                 if (s->specifier == 'm' && IN_SET(r, -EUNATCH, -ENOMEDIUM, -ENOPKG)) /* machine-id might be missing in build chroots */
                         continue;
                 assert_se(r >= 0);
@@ -176,11 +178,11 @@ TEST(specifiers_missing_data_ok) {
 
         assert_se(setenv("SYSTEMD_OS_RELEASE", "/dev/null", 1) == 0);
         assert_se(specifier_printf("%A-%B-%M-%o-%w-%W", SIZE_MAX, specifier_table, NULL, NULL, &resolved) >= 0);
-        assert_se(streq(resolved, "-----"));
+        ASSERT_STREQ(resolved, "-----");
 
         assert_se(setenv("SYSTEMD_OS_RELEASE", "/nosuchfileordirectory", 1) == 0);
         assert_se(specifier_printf("%A-%B-%M-%o-%w-%W", SIZE_MAX, specifier_table, NULL, NULL, &resolved) == -EUNATCH);
-        assert_se(streq(resolved, "-----"));
+        ASSERT_STREQ(resolved, "-----");
 
         assert_se(unsetenv("SYSTEMD_OS_RELEASE") == 0);
 }
index df37dcb528d50e6c16c19aefc169b75c65db4473..a69f6f06e0a7fda0dea6308c7dbc8974159d42fc 100644 (file)
@@ -205,7 +205,7 @@ TEST(anonymous_inode) {
         /* Verify that we handle anonymous inodes correctly, i.e. those which have no file type */
 
         struct stat st;
-        assert_se(fstat(fd, &st) >= 0);
+        ASSERT_OK_ERRNO(fstat(fd, &st));
         assert_se((st.st_mode & S_IFMT) == 0);
 
         assert_se(!inode_type_to_string(st.st_mode));
index 39a7142b5bb645a7af65cdd649c14a6c2ea6bf80..c0a4d1167edaeb51de834295805db0c23b752a3c 100644 (file)
@@ -32,12 +32,12 @@ TEST(strbuf) {
         l = strv_parse_nulstr(sb->buf, sb->len);
         assert_se(l);
 
-        assert_se(streq(l[0], "")); /* root */
-        assert_se(streq(l[1], "waldo"));
-        assert_se(streq(l[2], "foo"));
-        assert_se(streq(l[3], "bar"));
-        assert_se(streq(l[4], "waldorf"));
-        assert_se(l[5] == NULL);
+        ASSERT_STREQ(l[0], ""); /* root */
+        ASSERT_STREQ(l[1], "waldo");
+        ASSERT_STREQ(l[2], "foo");
+        ASSERT_STREQ(l[3], "bar");
+        ASSERT_STREQ(l[4], "waldorf");
+        ASSERT_NULL(l[5]);
 
         assert_se(sb->nodes_count == 5); /* root + 4 non-duplicates */
         assert_se(sb->dedup_count == 4);
@@ -57,17 +57,17 @@ TEST(strbuf) {
         assert_se(g == 15);
         assert_se(h == 0);
 
-        assert_se(streq(sb->buf + a, "waldo"));
-        assert_se(streq(sb->buf + b, "foo"));
-        assert_se(streq(sb->buf + c, "bar"));
-        assert_se(streq(sb->buf + d, "waldo"));
-        assert_se(streq(sb->buf + e, "aldo"));
-        assert_se(streq(sb->buf + f, "do"));
-        assert_se(streq(sb->buf + g, "waldorf"));
-        assert_se(streq(sb->buf + h, ""));
+        ASSERT_STREQ(sb->buf + a, "waldo");
+        ASSERT_STREQ(sb->buf + b, "foo");
+        ASSERT_STREQ(sb->buf + c, "bar");
+        ASSERT_STREQ(sb->buf + d, "waldo");
+        ASSERT_STREQ(sb->buf + e, "aldo");
+        ASSERT_STREQ(sb->buf + f, "do");
+        ASSERT_STREQ(sb->buf + g, "waldorf");
+        ASSERT_STREQ(sb->buf + h, "");
 
         strbuf_complete(sb);
-        assert_se(sb->root == NULL);
+        ASSERT_NULL(sb->root);
 }
 
 DEFINE_TEST_MAIN(LOG_INFO);
index 3b003e885f0d3af249d40b0014c3a1674f068cc4..4bf7548ac1584453f8736fab5cc411cc6c9a18d1 100644 (file)
 TEST(string_erase) {
         char *x;
         x = strdupa_safe("");
-        assert_se(streq(string_erase(x), ""));
+        ASSERT_STREQ(string_erase(x), "");
 
         x = strdupa_safe("1");
-        assert_se(streq(string_erase(x), ""));
+        ASSERT_STREQ(string_erase(x), "");
 
         x = strdupa_safe("123456789");
-        assert_se(streq(string_erase(x), ""));
+        ASSERT_STREQ(string_erase(x), "");
 
         assert_se(x[1] == '\0');
         assert_se(x[2] == '\0');
@@ -37,7 +37,7 @@ static void test_free_and_strndup_one(char **t, const char *src, size_t l, const
                   __func__, strnull(*t), strnull(src), l, strnull(expected), yes_no(change));
 
         int r = free_and_strndup(t, src, l);
-        assert_se(streq_ptr(*t, expected));
+        ASSERT_STREQ(*t, expected);
         assert_se(r == change); /* check that change occurs only when necessary */
 }
 
@@ -96,12 +96,12 @@ TEST(strdup_to_full) {
 
         assert_se(strdup_to_full(NULL, "") == 1);
         assert_se(strdup_to_full(&dst, "") == 1);
-        assert_se(streq_ptr(dst, ""));
+        ASSERT_STREQ(dst, "");
         dst = mfree(dst);
 
         assert_se(strdup_to_full(NULL, "x") == 1);
         assert_se(strdup_to_full(&dst, "x") == 1);
-        assert_se(streq_ptr(dst, "x"));
+        ASSERT_STREQ(dst, "x");
 }
 
 TEST(strdup_to) {
@@ -110,11 +110,11 @@ TEST(strdup_to) {
         assert_se(strdup_to(&dst, NULL) == 0);
 
         assert_se(strdup_to(&dst, "") == 0);
-        assert_se(streq_ptr(dst, ""));
+        ASSERT_STREQ(dst, "");
         dst = mfree(dst);
 
         assert_se(strdup_to(&dst, "x") == 0);
-        assert_se(streq_ptr(dst, "x"));
+        ASSERT_STREQ(dst, "x");
 }
 
 TEST(ascii_strcasecmp_n) {
@@ -163,82 +163,82 @@ TEST(ascii_strcasecmp_nn) {
 TEST(cellescape) {
         char buf[40];
 
-        assert_se(streq(cellescape(buf, 1, ""), ""));
-        assert_se(streq(cellescape(buf, 1, "1"), ""));
-        assert_se(streq(cellescape(buf, 1, "12"), ""));
-
-        assert_se(streq(cellescape(buf, 2, ""), ""));
-        assert_se(streq(cellescape(buf, 2, "1"), "1"));
-        assert_se(streq(cellescape(buf, 2, "12"), "."));
-        assert_se(streq(cellescape(buf, 2, "123"), "."));
-
-        assert_se(streq(cellescape(buf, 3, ""), ""));
-        assert_se(streq(cellescape(buf, 3, "1"), "1"));
-        assert_se(streq(cellescape(buf, 3, "12"), "12"));
-        assert_se(streq(cellescape(buf, 3, "123"), ".."));
-        assert_se(streq(cellescape(buf, 3, "1234"), ".."));
-
-        assert_se(streq(cellescape(buf, 4, ""), ""));
-        assert_se(streq(cellescape(buf, 4, "1"), "1"));
-        assert_se(streq(cellescape(buf, 4, "12"), "12"));
-        assert_se(streq(cellescape(buf, 4, "123"), "123"));
-        assert_se(streq(cellescape(buf, 4, "1234"), is_locale_utf8() ? "…" : "..."));
-        assert_se(streq(cellescape(buf, 4, "12345"), is_locale_utf8() ? "…" : "..."));
-
-        assert_se(streq(cellescape(buf, 5, ""), ""));
-        assert_se(streq(cellescape(buf, 5, "1"), "1"));
-        assert_se(streq(cellescape(buf, 5, "12"), "12"));
-        assert_se(streq(cellescape(buf, 5, "123"), "123"));
-        assert_se(streq(cellescape(buf, 5, "1234"), "1234"));
-        assert_se(streq(cellescape(buf, 5, "12345"), is_locale_utf8() ? "1…" : "1..."));
-        assert_se(streq(cellescape(buf, 5, "123456"), is_locale_utf8() ? "1…" : "1..."));
-
-        assert_se(streq(cellescape(buf, 1, "\020"), ""));
-        assert_se(streq(cellescape(buf, 2, "\020"), "."));
-        assert_se(streq(cellescape(buf, 3, "\020"), ".."));
-        assert_se(streq(cellescape(buf, 4, "\020"), is_locale_utf8() ? "…" : "..."));
-        assert_se(streq(cellescape(buf, 5, "\020"), "\\020"));
-
-        assert_se(streq(cellescape(buf, 5, "1234\020"), is_locale_utf8() ? "1…" : "1..."));
-        assert_se(streq(cellescape(buf, 6, "1234\020"), is_locale_utf8() ? "12…" : "12..."));
-        assert_se(streq(cellescape(buf, 7, "1234\020"), is_locale_utf8() ? "123…" : "123..."));
-        assert_se(streq(cellescape(buf, 8, "1234\020"), is_locale_utf8() ? "1234…" : "1234..."));
-        assert_se(streq(cellescape(buf, 9, "1234\020"), "1234\\020"));
-
-        assert_se(streq(cellescape(buf, 1, "\t\n"), ""));
-        assert_se(streq(cellescape(buf, 2, "\t\n"), "."));
-        assert_se(streq(cellescape(buf, 3, "\t\n"), ".."));
-        assert_se(streq(cellescape(buf, 4, "\t\n"), is_locale_utf8() ? "…" : "..."));
-        assert_se(streq(cellescape(buf, 5, "\t\n"), "\\t\\n"));
-
-        assert_se(streq(cellescape(buf, 5, "1234\t\n"), is_locale_utf8() ? "1…" : "1..."));
-        assert_se(streq(cellescape(buf, 6, "1234\t\n"), is_locale_utf8() ? "12…" : "12..."));
-        assert_se(streq(cellescape(buf, 7, "1234\t\n"), is_locale_utf8() ? "123…" : "123..."));
-        assert_se(streq(cellescape(buf, 8, "1234\t\n"), is_locale_utf8() ? "1234…" : "1234..."));
-        assert_se(streq(cellescape(buf, 9, "1234\t\n"), "1234\\t\\n"));
-
-        assert_se(streq(cellescape(buf, 4, "x\t\020\n"), is_locale_utf8() ? "…" : "..."));
-        assert_se(streq(cellescape(buf, 5, "x\t\020\n"), is_locale_utf8() ? "x…" : "x..."));
-        assert_se(streq(cellescape(buf, 6, "x\t\020\n"), is_locale_utf8() ? "x…" : "x..."));
-        assert_se(streq(cellescape(buf, 7, "x\t\020\n"), is_locale_utf8() ? "x\\t…" : "x\\t..."));
-        assert_se(streq(cellescape(buf, 8, "x\t\020\n"), is_locale_utf8() ? "x\\t…" : "x\\t..."));
-        assert_se(streq(cellescape(buf, 9, "x\t\020\n"), is_locale_utf8() ? "x\\t…" : "x\\t..."));
-        assert_se(streq(cellescape(buf, 10, "x\t\020\n"), "x\\t\\020\\n"));
-
-        assert_se(streq(cellescape(buf, 6, "1\011"), "1\\t"));
-        assert_se(streq(cellescape(buf, 6, "1\020"), "1\\020"));
-        assert_se(streq(cellescape(buf, 6, "1\020x"), is_locale_utf8() ? "1…" : "1..."));
-
-        assert_se(streq(cellescape(buf, 40, "1\020"), "1\\020"));
-        assert_se(streq(cellescape(buf, 40, "1\020x"), "1\\020x"));
-
-        assert_se(streq(cellescape(buf, 40, "\a\b\f\n\r\t\v\\\"'"), "\\a\\b\\f\\n\\r\\t\\v\\\\\\\"\\'"));
-        assert_se(streq(cellescape(buf, 6, "\a\b\f\n\r\t\v\\\"'"), is_locale_utf8() ? "\\a…" : "\\a..."));
-        assert_se(streq(cellescape(buf, 7, "\a\b\f\n\r\t\v\\\"'"), is_locale_utf8() ? "\\a…" : "\\a..."));
-        assert_se(streq(cellescape(buf, 8, "\a\b\f\n\r\t\v\\\"'"), is_locale_utf8() ? "\\a\\b…" : "\\a\\b..."));
-
-        assert_se(streq(cellescape(buf, sizeof buf, "1\020"), "1\\020"));
-        assert_se(streq(cellescape(buf, sizeof buf, "1\020x"), "1\\020x"));
+        ASSERT_STREQ(cellescape(buf, 1, ""), "");
+        ASSERT_STREQ(cellescape(buf, 1, "1"), "");
+        ASSERT_STREQ(cellescape(buf, 1, "12"), "");
+
+        ASSERT_STREQ(cellescape(buf, 2, ""), "");
+        ASSERT_STREQ(cellescape(buf, 2, "1"), "1");
+        ASSERT_STREQ(cellescape(buf, 2, "12"), ".");
+        ASSERT_STREQ(cellescape(buf, 2, "123"), ".");
+
+        ASSERT_STREQ(cellescape(buf, 3, ""), "");
+        ASSERT_STREQ(cellescape(buf, 3, "1"), "1");
+        ASSERT_STREQ(cellescape(buf, 3, "12"), "12");
+        ASSERT_STREQ(cellescape(buf, 3, "123"), "..");
+        ASSERT_STREQ(cellescape(buf, 3, "1234"), "..");
+
+        ASSERT_STREQ(cellescape(buf, 4, ""), "");
+        ASSERT_STREQ(cellescape(buf, 4, "1"), "1");
+        ASSERT_STREQ(cellescape(buf, 4, "12"), "12");
+        ASSERT_STREQ(cellescape(buf, 4, "123"), "123");
+        ASSERT_STREQ(cellescape(buf, 4, "1234"), is_locale_utf8() ? "…" : "...");
+        ASSERT_STREQ(cellescape(buf, 4, "12345"), is_locale_utf8() ? "…" : "...");
+
+        ASSERT_STREQ(cellescape(buf, 5, ""), "");
+        ASSERT_STREQ(cellescape(buf, 5, "1"), "1");
+        ASSERT_STREQ(cellescape(buf, 5, "12"), "12");
+        ASSERT_STREQ(cellescape(buf, 5, "123"), "123");
+        ASSERT_STREQ(cellescape(buf, 5, "1234"), "1234");
+        ASSERT_STREQ(cellescape(buf, 5, "12345"), is_locale_utf8() ? "1…" : "1...");
+        ASSERT_STREQ(cellescape(buf, 5, "123456"), is_locale_utf8() ? "1…" : "1...");
+
+        ASSERT_STREQ(cellescape(buf, 1, "\020"), "");
+        ASSERT_STREQ(cellescape(buf, 2, "\020"), ".");
+        ASSERT_STREQ(cellescape(buf, 3, "\020"), "..");
+        ASSERT_STREQ(cellescape(buf, 4, "\020"), is_locale_utf8() ? "…" : "...");
+        ASSERT_STREQ(cellescape(buf, 5, "\020"), "\\020");
+
+        ASSERT_STREQ(cellescape(buf, 5, "1234\020"), is_locale_utf8() ? "1…" : "1...");
+        ASSERT_STREQ(cellescape(buf, 6, "1234\020"), is_locale_utf8() ? "12…" : "12...");
+        ASSERT_STREQ(cellescape(buf, 7, "1234\020"), is_locale_utf8() ? "123…" : "123...");
+        ASSERT_STREQ(cellescape(buf, 8, "1234\020"), is_locale_utf8() ? "1234…" : "1234...");
+        ASSERT_STREQ(cellescape(buf, 9, "1234\020"), "1234\\020");
+
+        ASSERT_STREQ(cellescape(buf, 1, "\t\n"), "");
+        ASSERT_STREQ(cellescape(buf, 2, "\t\n"), ".");
+        ASSERT_STREQ(cellescape(buf, 3, "\t\n"), "..");
+        ASSERT_STREQ(cellescape(buf, 4, "\t\n"), is_locale_utf8() ? "…" : "...");
+        ASSERT_STREQ(cellescape(buf, 5, "\t\n"), "\\t\\n");
+
+        ASSERT_STREQ(cellescape(buf, 5, "1234\t\n"), is_locale_utf8() ? "1…" : "1...");
+        ASSERT_STREQ(cellescape(buf, 6, "1234\t\n"), is_locale_utf8() ? "12…" : "12...");
+        ASSERT_STREQ(cellescape(buf, 7, "1234\t\n"), is_locale_utf8() ? "123…" : "123...");
+        ASSERT_STREQ(cellescape(buf, 8, "1234\t\n"), is_locale_utf8() ? "1234…" : "1234...");
+        ASSERT_STREQ(cellescape(buf, 9, "1234\t\n"), "1234\\t\\n");
+
+        ASSERT_STREQ(cellescape(buf, 4, "x\t\020\n"), is_locale_utf8() ? "…" : "...");
+        ASSERT_STREQ(cellescape(buf, 5, "x\t\020\n"), is_locale_utf8() ? "x…" : "x...");
+        ASSERT_STREQ(cellescape(buf, 6, "x\t\020\n"), is_locale_utf8() ? "x…" : "x...");
+        ASSERT_STREQ(cellescape(buf, 7, "x\t\020\n"), is_locale_utf8() ? "x\\t…" : "x\\t...");
+        ASSERT_STREQ(cellescape(buf, 8, "x\t\020\n"), is_locale_utf8() ? "x\\t…" : "x\\t...");
+        ASSERT_STREQ(cellescape(buf, 9, "x\t\020\n"), is_locale_utf8() ? "x\\t…" : "x\\t...");
+        ASSERT_STREQ(cellescape(buf, 10, "x\t\020\n"), "x\\t\\020\\n");
+
+        ASSERT_STREQ(cellescape(buf, 6, "1\011"), "1\\t");
+        ASSERT_STREQ(cellescape(buf, 6, "1\020"), "1\\020");
+        ASSERT_STREQ(cellescape(buf, 6, "1\020x"), is_locale_utf8() ? "1…" : "1...");
+
+        ASSERT_STREQ(cellescape(buf, 40, "1\020"), "1\\020");
+        ASSERT_STREQ(cellescape(buf, 40, "1\020x"), "1\\020x");
+
+        ASSERT_STREQ(cellescape(buf, 40, "\a\b\f\n\r\t\v\\\"'"), "\\a\\b\\f\\n\\r\\t\\v\\\\\\\"\\'");
+        ASSERT_STREQ(cellescape(buf, 6, "\a\b\f\n\r\t\v\\\"'"), is_locale_utf8() ? "\\a…" : "\\a...");
+        ASSERT_STREQ(cellescape(buf, 7, "\a\b\f\n\r\t\v\\\"'"), is_locale_utf8() ? "\\a…" : "\\a...");
+        ASSERT_STREQ(cellescape(buf, 8, "\a\b\f\n\r\t\v\\\"'"), is_locale_utf8() ? "\\a\\b…" : "\\a\\b...");
+
+        ASSERT_STREQ(cellescape(buf, sizeof buf, "1\020"), "1\\020");
+        ASSERT_STREQ(cellescape(buf, sizeof buf, "1\020x"), "1\\020x");
 }
 
 TEST(streq_ptr) {
@@ -250,41 +250,41 @@ TEST(strstrip) {
         char *ret, input[] = "   hello, waldo.   ";
 
         ret = strstrip(input);
-        assert_se(streq(ret, "hello, waldo."));
+        ASSERT_STREQ(ret, "hello, waldo.");
 }
 
 TEST(strextend) {
         _cleanup_free_ char *str = NULL;
 
         assert_se(strextend(&str, NULL));
-        assert_se(streq_ptr(str, ""));
+        ASSERT_STREQ(str, "");
         assert_se(strextend(&str, "", "0", "", "", "123"));
-        assert_se(streq_ptr(str, "0123"));
+        ASSERT_STREQ(str, "0123");
         assert_se(strextend(&str, "456", "78", "9"));
-        assert_se(streq_ptr(str, "0123456789"));
+        ASSERT_STREQ(str, "0123456789");
 }
 
 TEST(strextend_with_separator) {
         _cleanup_free_ char *str = NULL;
 
         assert_se(strextend_with_separator(&str, NULL, NULL));
-        assert_se(streq_ptr(str, ""));
+        ASSERT_STREQ(str, "");
         str = mfree(str);
 
         assert_se(strextend_with_separator(&str, "...", NULL));
-        assert_se(streq_ptr(str, ""));
+        ASSERT_STREQ(str, "");
         assert_se(strextend_with_separator(&str, "...", NULL));
-        assert_se(streq_ptr(str, ""));
+        ASSERT_STREQ(str, "");
         str = mfree(str);
 
         assert_se(strextend_with_separator(&str, "xyz", "a", "bb", "ccc"));
-        assert_se(streq_ptr(str, "axyzbbxyzccc"));
+        ASSERT_STREQ(str, "axyzbbxyzccc");
         str = mfree(str);
 
         assert_se(strextend_with_separator(&str, ",", "start", "", "1", "234"));
-        assert_se(streq_ptr(str, "start,,1,234"));
+        ASSERT_STREQ(str, "start,,1,234");
         assert_se(strextend_with_separator(&str, ";", "more", "5", "678"));
-        assert_se(streq_ptr(str, "start,,1,234;more;5;678"));
+        ASSERT_STREQ(str, "start,,1,234;more;5;678");
 }
 
 TEST(strrep) {
@@ -295,15 +295,15 @@ TEST(strrep) {
         three = strrep("waldo", 3);
         zero = strrep("waldo", 0);
 
-        assert_se(streq(one, "waldo"));
-        assert_se(streq(three, "waldowaldowaldo"));
-        assert_se(streq(zero, ""));
+        ASSERT_STREQ(one, "waldo");
+        ASSERT_STREQ(three, "waldowaldowaldo");
+        ASSERT_STREQ(zero, "");
 
         onea = strrepa("waldo", 1);
         threea = strrepa("waldo", 3);
 
-        assert_se(streq(onea, "waldo"));
-        assert_se(streq(threea, "waldowaldowaldo"));
+        ASSERT_STREQ(onea, "waldo");
+        ASSERT_STREQ(threea, "waldowaldowaldo");
 }
 
 TEST(string_has_cc) {
@@ -322,7 +322,7 @@ TEST(string_has_cc) {
 
 TEST(ascii_strlower) {
         char a[] = "AabBcC Jk Ii Od LKJJJ kkd LK";
-        assert_se(streq(ascii_strlower(a), "aabbcc jk ii od lkjjj kkd lk"));
+        ASSERT_STREQ(ascii_strlower(a), "aabbcc jk ii od lkjjj kkd lk");
 }
 
 TEST(strshorten) {
@@ -343,63 +343,63 @@ TEST(strjoina) {
         char *actual;
 
         actual = strjoina("", "foo", "bar");
-        assert_se(streq(actual, "foobar"));
+        ASSERT_STREQ(actual, "foobar");
 
         actual = strjoina("foo", "bar", "baz");
-        assert_se(streq(actual, "foobarbaz"));
+        ASSERT_STREQ(actual, "foobarbaz");
 
         actual = strjoina("foo", "", "bar", "baz");
-        assert_se(streq(actual, "foobarbaz"));
+        ASSERT_STREQ(actual, "foobarbaz");
 
         actual = strjoina("foo");
-        assert_se(streq(actual, "foo"));
+        ASSERT_STREQ(actual, "foo");
 
         actual = strjoina(NULL);
-        assert_se(streq(actual, ""));
+        ASSERT_STREQ(actual, "");
 
         actual = strjoina(NULL, "foo");
-        assert_se(streq(actual, ""));
+        ASSERT_STREQ(actual, "");
 
         actual = strjoina("foo", NULL, "bar");
-        assert_se(streq(actual, "foo"));
+        ASSERT_STREQ(actual, "foo");
 
         actual = strjoina("/sys/fs/cgroup/", "dn", "/a/b/c", "/cgroup.procs");
-        assert_se(streq(actual, "/sys/fs/cgroup/dn/a/b/c/cgroup.procs"));
+        ASSERT_STREQ(actual, "/sys/fs/cgroup/dn/a/b/c/cgroup.procs");
 
         actual = strjoina("/sys/fs/cgroup/", "dn", NULL, NULL);
-        assert_se(streq(actual, "/sys/fs/cgroup/dn"));
+        ASSERT_STREQ(actual, "/sys/fs/cgroup/dn");
 }
 
 TEST(strjoin) {
         char *actual;
 
         actual = strjoin("", "foo", "bar");
-        assert_se(streq(actual, "foobar"));
-        mfree(actual);
+        ASSERT_STREQ(actual, "foobar");
+        free(actual);
 
         actual = strjoin("foo", "bar", "baz");
-        assert_se(streq(actual, "foobarbaz"));
-        mfree(actual);
+        ASSERT_STREQ(actual, "foobarbaz");
+        free(actual);
 
         actual = strjoin("foo", "", "bar", "baz");
-        assert_se(streq(actual, "foobarbaz"));
-        mfree(actual);
+        ASSERT_STREQ(actual, "foobarbaz");
+        free(actual);
 
         actual = strjoin("foo", NULL);
-        assert_se(streq(actual, "foo"));
-        mfree(actual);
+        ASSERT_STREQ(actual, "foo");
+        free(actual);
 
         actual = strjoin(NULL, NULL);
-        assert_se(streq(actual, ""));
-        mfree(actual);
+        ASSERT_STREQ(actual, "");
+        free(actual);
 
         actual = strjoin(NULL, "foo");
-        assert_se(streq(actual, ""));
-        mfree(actual);
+        ASSERT_STREQ(actual, "");
+        free(actual);
 
         actual = strjoin("foo", NULL, "bar");
-        assert_se(streq(actual, "foo"));
-        mfree(actual);
+        ASSERT_STREQ(actual, "foo");
+        free(actual);
 }
 
 TEST(strcmp_ptr) {
@@ -436,7 +436,7 @@ TEST(foreach_word) {
                 }
                 assert_se(r > 0);
 
-                assert_se(streq(expected[i++], word));
+                ASSERT_STREQ(expected[i++], word);
         }
 }
 
@@ -457,10 +457,10 @@ static void check(const char *test, char** expected, bool trailing) {
                         break;
                 }
 
-                assert_se(streq(word, expected[i++]));
+                ASSERT_STREQ(word, expected[i++]);
                 printf("<%s>\n", word);
         }
-        assert_se(expected[i] == NULL);
+        ASSERT_NULL(expected[i]);
 }
 
 TEST(foreach_word_quoted) {
@@ -512,7 +512,7 @@ TEST(delete_chars) {
         char *s, input[] = "   hello, waldo.   abc";
 
         s = delete_chars(input, WHITESPACE);
-        assert_se(streq(s, "hello,waldo.abc"));
+        ASSERT_STREQ(s, "hello,waldo.abc");
         assert_se(s == input);
 }
 
@@ -523,19 +523,19 @@ TEST(delete_trailing_chars) {
                 input3[] = "abcdef";
 
         s = delete_trailing_chars(input1, WHITESPACE);
-        assert_se(streq(s, " \n \r k"));
+        ASSERT_STREQ(s, " \n \r k");
         assert_se(s == input1);
 
         s = delete_trailing_chars(input2, "kt");
-        assert_se(streq(s, "kkkkthiskkkiskkkaktes"));
+        ASSERT_STREQ(s, "kkkkthiskkkiskkkaktes");
         assert_se(s == input2);
 
         s = delete_trailing_chars(input3, WHITESPACE);
-        assert_se(streq(s, "abcdef"));
+        ASSERT_STREQ(s, "abcdef");
         assert_se(s == input3);
 
         s = delete_trailing_chars(input3, "fe");
-        assert_se(streq(s, "abcd"));
+        ASSERT_STREQ(s, "abcd");
         assert_se(s == input3);
 }
 
@@ -545,11 +545,11 @@ TEST(delete_trailing_slashes) {
              s3[] = "foobar",
              s4[] = "";
 
-        assert_se(streq(delete_trailing_chars(s1, "_"), "foobar//"));
-        assert_se(streq(delete_trailing_chars(s1, "/"), "foobar"));
-        assert_se(streq(delete_trailing_chars(s2, "/"), "foobar"));
-        assert_se(streq(delete_trailing_chars(s3, "/"), "foobar"));
-        assert_se(streq(delete_trailing_chars(s4, "/"), ""));
+        ASSERT_STREQ(delete_trailing_chars(s1, "_"), "foobar//");
+        ASSERT_STREQ(delete_trailing_chars(s1, "/"), "foobar");
+        ASSERT_STREQ(delete_trailing_chars(s2, "/"), "foobar");
+        ASSERT_STREQ(delete_trailing_chars(s3, "/"), "foobar");
+        ASSERT_STREQ(delete_trailing_chars(s4, "/"), "");
 }
 
 TEST(skip_leading_chars) {
@@ -557,11 +557,11 @@ TEST(skip_leading_chars) {
                 input2[] = "kkkkthiskkkiskkkaktestkkk",
                 input3[] = "abcdef";
 
-        assert_se(streq(skip_leading_chars(input1, WHITESPACE), "k \n \r "));
-        assert_se(streq(skip_leading_chars(input2, "k"), "thiskkkiskkkaktestkkk"));
-        assert_se(streq(skip_leading_chars(input2, "tk"), "hiskkkiskkkaktestkkk"));
-        assert_se(streq(skip_leading_chars(input3, WHITESPACE), "abcdef"));
-        assert_se(streq(skip_leading_chars(input3, "bcaef"), "def"));
+        ASSERT_STREQ(skip_leading_chars(input1, WHITESPACE), "k \n \r ");
+        ASSERT_STREQ(skip_leading_chars(input2, "k"), "thiskkkiskkkaktestkkk");
+        ASSERT_STREQ(skip_leading_chars(input2, "tk"), "hiskkkiskkkaktestkkk");
+        ASSERT_STREQ(skip_leading_chars(input3, WHITESPACE), "abcdef");
+        ASSERT_STREQ(skip_leading_chars(input3, "bcaef"), "def");
 }
 
 TEST(in_charset) {
@@ -576,19 +576,19 @@ TEST(split_pair) {
         assert_se(split_pair("foo=bar", "", &a, &b) == -EINVAL);
         assert_se(split_pair("", "=", &a, &b) == -EINVAL);
         assert_se(split_pair("foo=bar", "=", &a, &b) >= 0);
-        assert_se(streq(a, "foo"));
-        assert_se(streq(b, "bar"));
+        ASSERT_STREQ(a, "foo");
+        ASSERT_STREQ(b, "bar");
         free(a);
         free(b);
         assert_se(split_pair("==", "==", &a, &b) >= 0);
-        assert_se(streq(a, ""));
-        assert_se(streq(b, ""));
+        ASSERT_STREQ(a, "");
+        ASSERT_STREQ(b, "");
         free(a);
         free(b);
 
         assert_se(split_pair("===", "==", &a, &b) >= 0);
-        assert_se(streq(a, ""));
-        assert_se(streq(b, "="));
+        ASSERT_STREQ(a, "");
+        ASSERT_STREQ(b, "=");
 }
 
 TEST(first_word) {
@@ -612,33 +612,33 @@ TEST(strlen_ptr) {
 }
 
 TEST(memory_startswith) {
-        assert_se(streq(memory_startswith("", 0, ""), ""));
-        assert_se(streq(memory_startswith("", 1, ""), ""));
-        assert_se(streq(memory_startswith("x", 2, ""), "x"));
+        ASSERT_STREQ(memory_startswith("", 0, ""), "");
+        ASSERT_STREQ(memory_startswith("", 1, ""), "");
+        ASSERT_STREQ(memory_startswith("x", 2, ""), "x");
         assert_se(!memory_startswith("", 1, "x"));
         assert_se(!memory_startswith("", 1, "xxxxxxxx"));
-        assert_se(streq(memory_startswith("xxx", 4, "x"), "xx"));
-        assert_se(streq(memory_startswith("xxx", 4, "xx"), "x"));
-        assert_se(streq(memory_startswith("xxx", 4, "xxx"), ""));
+        ASSERT_STREQ(memory_startswith("xxx", 4, "x"), "xx");
+        ASSERT_STREQ(memory_startswith("xxx", 4, "xx"), "x");
+        ASSERT_STREQ(memory_startswith("xxx", 4, "xxx"), "");
         assert_se(!memory_startswith("xxx", 4, "xxxx"));
 }
 
 TEST(memory_startswith_no_case) {
-        assert_se(streq(memory_startswith_no_case("", 0, ""), ""));
-        assert_se(streq(memory_startswith_no_case("", 1, ""), ""));
-        assert_se(streq(memory_startswith_no_case("x", 2, ""), "x"));
-        assert_se(streq(memory_startswith_no_case("X", 2, ""), "X"));
+        ASSERT_STREQ(memory_startswith_no_case("", 0, ""), "");
+        ASSERT_STREQ(memory_startswith_no_case("", 1, ""), "");
+        ASSERT_STREQ(memory_startswith_no_case("x", 2, ""), "x");
+        ASSERT_STREQ(memory_startswith_no_case("X", 2, ""), "X");
         assert_se(!memory_startswith_no_case("", 1, "X"));
         assert_se(!memory_startswith_no_case("", 1, "xxxxXXXX"));
-        assert_se(streq(memory_startswith_no_case("xxx", 4, "X"), "xx"));
-        assert_se(streq(memory_startswith_no_case("XXX", 4, "x"), "XX"));
-        assert_se(streq(memory_startswith_no_case("XXX", 4, "X"), "XX"));
-        assert_se(streq(memory_startswith_no_case("xxx", 4, "XX"), "x"));
-        assert_se(streq(memory_startswith_no_case("XXX", 4, "xx"), "X"));
-        assert_se(streq(memory_startswith_no_case("XXX", 4, "XX"), "X"));
-        assert_se(streq(memory_startswith_no_case("xxx", 4, "XXX"), ""));
-        assert_se(streq(memory_startswith_no_case("XXX", 4, "xxx"), ""));
-        assert_se(streq(memory_startswith_no_case("XXX", 4, "XXX"), ""));
+        ASSERT_STREQ(memory_startswith_no_case("xxx", 4, "X"), "xx");
+        ASSERT_STREQ(memory_startswith_no_case("XXX", 4, "x"), "XX");
+        ASSERT_STREQ(memory_startswith_no_case("XXX", 4, "X"), "XX");
+        ASSERT_STREQ(memory_startswith_no_case("xxx", 4, "XX"), "x");
+        ASSERT_STREQ(memory_startswith_no_case("XXX", 4, "xx"), "X");
+        ASSERT_STREQ(memory_startswith_no_case("XXX", 4, "XX"), "X");
+        ASSERT_STREQ(memory_startswith_no_case("xxx", 4, "XXX"), "");
+        ASSERT_STREQ(memory_startswith_no_case("XXX", 4, "xxx"), "");
+        ASSERT_STREQ(memory_startswith_no_case("XXX", 4, "XXX"), "");
 
         assert_se(memory_startswith_no_case((char[2]){'x', 'x'}, 2, "xx"));
         assert_se(memory_startswith_no_case((char[2]){'x', 'X'}, 2, "xX"));
@@ -651,7 +651,7 @@ static void test_string_truncate_lines_one(const char *input, size_t n_lines, co
         int k;
 
         assert_se((k = string_truncate_lines(input, n_lines, &b)) >= 0);
-        assert_se(streq(b, output));
+        ASSERT_STREQ(b, output);
         assert_se(!!k == truncation);
 }
 
@@ -722,7 +722,7 @@ static void test_string_extract_lines_one(const char *input, size_t i, const cha
         int k;
 
         assert_se((k = string_extract_line(input, i, &b)) >= 0);
-        assert_se(streq(b ?: input, output));
+        ASSERT_STREQ(b ?: input, output);
         assert_se(!!k == more);
 }
 
@@ -799,25 +799,25 @@ TEST(string_contains_word_strv) {
         assert_se(string_contains_word_strv("a b cc", NULL, STRV_MAKE("a", "b"), NULL));
 
         assert_se(string_contains_word_strv("a b cc", NULL, STRV_MAKE("a", "b"), &w));
-        assert_se(streq(w, "a"));
+        ASSERT_STREQ(w, "a");
 
         assert_se(!string_contains_word_strv("a b cc", NULL, STRV_MAKE("d"), &w));
-        assert_se(w == NULL);
+        ASSERT_NULL(w);
 
         assert_se(string_contains_word_strv("a b cc", NULL, STRV_MAKE("b", "a"), &w));
-        assert_se(streq(w, "a"));
+        ASSERT_STREQ(w, "a");
 
         assert_se(string_contains_word_strv("b a b cc", NULL, STRV_MAKE("b", "a", "b"), &w));
-        assert_se(streq(w, "b"));
+        ASSERT_STREQ(w, "b");
 
         assert_se(string_contains_word_strv("a b cc", NULL, STRV_MAKE("b", ""), &w));
-        assert_se(streq(w, "b"));
+        ASSERT_STREQ(w, "b");
 
         assert_se(!string_contains_word_strv("a b cc", NULL, STRV_MAKE(""), &w));
-        assert_se(w == NULL);
+        ASSERT_NULL(w);
 
         assert_se(string_contains_word_strv("a b  cc", " ", STRV_MAKE(""), &w));
-        assert_se(streq(w, ""));
+        ASSERT_STREQ(w, "");
 }
 
 TEST(string_contains_word) {
@@ -1139,38 +1139,38 @@ TEST(strextendf) {
         _cleanup_free_ char *p = NULL;
 
         assert_se(strextendf(&p, "<%i>", 77) >= 0);
-        assert_se(streq(p, "<77>"));
+        ASSERT_STREQ(p, "<77>");
 
         assert_se(strextendf(&p, "<%i>", 99) >= 0);
-        assert_se(streq(p, "<77><99>"));
+        ASSERT_STREQ(p, "<77><99>");
 
         assert_se(strextendf(&p, "<%80i>", 88) >= 0);
-        assert_se(streq(p, "<77><99><                                                                              88>"));
+        ASSERT_STREQ(p, "<77><99><                                                                              88>");
 
         assert_se(strextendf(&p, "<%08x>", 0x1234u) >= 0);
-        assert_se(streq(p, "<77><99><                                                                              88><00001234>"));
+        ASSERT_STREQ(p, "<77><99><                                                                              88><00001234>");
 
         p = mfree(p);
 
         assert_se(strextendf_with_separator(&p, ",", "<%i>", 77) >= 0);
-        assert_se(streq(p, "<77>"));
+        ASSERT_STREQ(p, "<77>");
 
         assert_se(strextendf_with_separator(&p, ",", "<%i>", 99) >= 0);
-        assert_se(streq(p, "<77>,<99>"));
+        ASSERT_STREQ(p, "<77>,<99>");
 
         assert_se(strextendf_with_separator(&p, ",", "<%80i>", 88) >= 0);
-        assert_se(streq(p, "<77>,<99>,<                                                                              88>"));
+        ASSERT_STREQ(p, "<77>,<99>,<                                                                              88>");
 
         assert_se(strextendf_with_separator(&p, ",", "<%08x>", 0x1234u) >= 0);
-        assert_se(streq(p, "<77>,<99>,<                                                                              88>,<00001234>"));
+        ASSERT_STREQ(p, "<77>,<99>,<                                                                              88>,<00001234>");
 }
 
 TEST(string_replace_char) {
-        assert_se(streq(string_replace_char(strdupa_safe(""), 'a', 'b'), ""));
-        assert_se(streq(string_replace_char(strdupa_safe("abc"), 'a', 'b'), "bbc"));
-        assert_se(streq(string_replace_char(strdupa_safe("hoge"), 'a', 'b'), "hoge"));
-        assert_se(streq(string_replace_char(strdupa_safe("aaaa"), 'a', 'b'), "bbbb"));
-        assert_se(streq(string_replace_char(strdupa_safe("aaaa"), 'a', '\t'), "\t\t\t\t"));
+        ASSERT_STREQ(string_replace_char(strdupa_safe(""), 'a', 'b'), "");
+        ASSERT_STREQ(string_replace_char(strdupa_safe("abc"), 'a', 'b'), "bbc");
+        ASSERT_STREQ(string_replace_char(strdupa_safe("hoge"), 'a', 'b'), "hoge");
+        ASSERT_STREQ(string_replace_char(strdupa_safe("aaaa"), 'a', 'b'), "bbbb");
+        ASSERT_STREQ(string_replace_char(strdupa_safe("aaaa"), 'a', '\t'), "\t\t\t\t");
 }
 
 TEST(strspn_from_end) {
@@ -1214,7 +1214,7 @@ TEST(streq_skip_trailing_chars) {
         do {                                                            \
                 _cleanup_free_ char *b = NULL;                          \
                 assert_se(make_cstring((x), ELEMENTSOF(x), (mode), &b) == (ret)); \
-                assert_se(streq_ptr(b, (expect)));                      \
+                ASSERT_STREQ(b, (expect));                      \
         } while(false)
 
 TEST(make_cstring) {
@@ -1295,7 +1295,7 @@ TEST(strstrafter) {
         assert_se(!strstrafter(NULL, NULL));
         assert_se(!strstrafter("", NULL));
         assert_se(!strstrafter(NULL, ""));
-        assert_se(streq_ptr(strstrafter("", ""), ""));
+        ASSERT_STREQ(strstrafter("", ""), "");
 
         assert_se(strstrafter(buffer, "a") == buffer + 1);
         assert_se(strstrafter(buffer, "") == buffer);
@@ -1319,18 +1319,18 @@ TEST(version_is_valid) {
 TEST(strextendn) {
         _cleanup_free_ char *x = NULL;
 
-        assert_se(streq_ptr(strextendn(&x, NULL, 0), ""));
+        ASSERT_STREQ(strextendn(&x, NULL, 0), "");
         x = mfree(x);
 
-        assert_se(streq_ptr(strextendn(&x, "", 0), ""));
+        ASSERT_STREQ(strextendn(&x, "", 0), "");
         x = mfree(x);
 
-        assert_se(streq_ptr(strextendn(&x, "xxx", 3), "xxx"));
-        assert_se(streq_ptr(strextendn(&x, "xxx", 3), "xxxxxx"));
-        assert_se(streq_ptr(strextendn(&x, "...", 1), "xxxxxx."));
-        assert_se(streq_ptr(strextendn(&x, "...", 2), "xxxxxx..."));
-        assert_se(streq_ptr(strextendn(&x, "...", 3), "xxxxxx......"));
-        assert_se(streq_ptr(strextendn(&x, "...", 4), "xxxxxx........."));
+        ASSERT_STREQ(strextendn(&x, "xxx", 3), "xxx");
+        ASSERT_STREQ(strextendn(&x, "xxx", 3), "xxxxxx");
+        ASSERT_STREQ(strextendn(&x, "...", 1), "xxxxxx.");
+        ASSERT_STREQ(strextendn(&x, "...", 2), "xxxxxx...");
+        ASSERT_STREQ(strextendn(&x, "...", 3), "xxxxxx......");
+        ASSERT_STREQ(strextendn(&x, "...", 4), "xxxxxx.........");
         x = mfree(x);
 }
 
index 6f73d2686de58354b828a7b6042dedacca431477..3ad0fcd90e6b7d2d8f9050b8d6b079bfa51420cc 100644 (file)
@@ -15,38 +15,38 @@ TEST(strip_tab_ansi) {
         assert_se(p = strdup("\tFoobar\tbar\twaldo\t"));
         assert_se(strip_tab_ansi(&p, NULL, NULL));
         fprintf(stdout, "<%s>\n", p);
-        assert_se(streq(p, "        Foobar        bar        waldo        "));
+        ASSERT_STREQ(p, "        Foobar        bar        waldo        ");
         free(p);
 
         assert_se(p = strdup(ANSI_HIGHLIGHT "Hello" ANSI_NORMAL ANSI_HIGHLIGHT_RED " world!" ANSI_NORMAL));
         assert_se(strip_tab_ansi(&p, NULL, NULL));
         fprintf(stdout, "<%s>\n", p);
-        assert_se(streq(p, "Hello world!"));
+        ASSERT_STREQ(p, "Hello world!");
         free(p);
 
         assert_se(p = strdup("\x1B[\x1B[\t\x1B[" ANSI_HIGHLIGHT "\x1B[" "Hello" ANSI_NORMAL ANSI_HIGHLIGHT_RED " world!" ANSI_NORMAL));
         assert_se(strip_tab_ansi(&p, NULL, NULL));
-        assert_se(streq(p, "\x1B[\x1B[        \x1B[\x1B[Hello world!"));
+        ASSERT_STREQ(p, "\x1B[\x1B[        \x1B[\x1B[Hello world!");
         free(p);
 
         assert_se(p = strdup("\x1B[waldo"));
         assert_se(strip_tab_ansi(&p, NULL, NULL));
-        assert_se(streq(p, "\x1B[waldo"));
+        ASSERT_STREQ(p, "\x1B[waldo");
         free(p);
 
         assert_se(p = strdup("\r\rwaldo"));
         assert_se(strip_tab_ansi(&p, NULL, NULL));
-        assert_se(streq(p, "\r\rwaldo"));
+        ASSERT_STREQ(p, "\r\rwaldo");
         free(p);
 
         assert_se(p = strdup("waldo\r\r"));
         assert_se(strip_tab_ansi(&p, NULL, NULL));
-        assert_se(streq(p, "waldo"));
+        ASSERT_STREQ(p, "waldo");
         free(p);
 
         assert_se(p = strdup("waldo\r\r\n\r\n"));
         assert_se(strip_tab_ansi(&p, NULL, NULL));
-        assert_se(streq(p, "waldo\n\n"));
+        ASSERT_STREQ(p, "waldo\n\n");
         free(p);
 
         assert_se(terminal_urlify_path("/etc/fstab", "i am a fabulous link", &urlified) >= 0);
@@ -55,7 +55,7 @@ TEST(strip_tab_ansi) {
         printf("<%s>\n", p);
         assert_se(strip_tab_ansi(&p, NULL, NULL));
         printf("<%s>\n", p);
-        assert_se(streq(p, "something i am a fabulous link something-else"));
+        ASSERT_STREQ(p, "something i am a fabulous link something-else");
         p = mfree(p);
 
         /* Truncate the formatted string in the middle of an ANSI sequence (in which case we shouldn't touch the
@@ -65,7 +65,7 @@ TEST(strip_tab_ansi) {
                 *z = 0;
                 assert_se(qq = strdup(q));
                 assert_se(strip_tab_ansi(&q, NULL, NULL));
-                assert_se(streq(q, qq));
+                ASSERT_STREQ(q, qq);
         }
 }
 
index 47ad0eb639c30407b91b2ec06350afccc8953176..28b8b2270c099bfafda3b77203fe9f2fc0f566a9 100644 (file)
@@ -34,9 +34,9 @@ TEST(startswith_set) {
         assert_se(STARTSWITH_SET("abc", "ax", "abx", "abc"));
         assert_se(!STARTSWITH_SET("abc", "ax", "abx", "abcx"));
 
-        assert_se(streq_ptr(STARTSWITH_SET("foobar", "hhh", "kkk", "foo", "zzz"), "bar"));
-        assert_se(streq_ptr(STARTSWITH_SET("foobar", "hhh", "kkk", "", "zzz"), "foobar"));
-        assert_se(streq_ptr(STARTSWITH_SET("", "hhh", "kkk", "zzz", ""), ""));
+        ASSERT_STREQ(STARTSWITH_SET("foobar", "hhh", "kkk", "foo", "zzz"), "bar");
+        ASSERT_STREQ(STARTSWITH_SET("foobar", "hhh", "kkk", "", "zzz"), "foobar");
+        ASSERT_STREQ(STARTSWITH_SET("", "hhh", "kkk", "zzz", ""), "");
 }
 
 static const char* const input_table_multiple[] = {
@@ -125,78 +125,78 @@ TEST(strv_find_startswith) {
 TEST(strv_join) {
         _cleanup_free_ char *p = strv_join((char **)input_table_multiple, ", ");
         assert_se(p);
-        assert_se(streq(p, "one, two, three"));
+        ASSERT_STREQ(p, "one, two, three");
 
         _cleanup_free_ char *q = strv_join((char **)input_table_multiple, ";");
         assert_se(q);
-        assert_se(streq(q, "one;two;three"));
+        ASSERT_STREQ(q, "one;two;three");
 
         _cleanup_free_ char *r = strv_join((char **)input_table_multiple, NULL);
         assert_se(r);
-        assert_se(streq(r, "one two three"));
+        ASSERT_STREQ(r, "one two three");
 
         _cleanup_free_ char *s = strv_join(STRV_MAKE("1", "2", "3,3"), ",");
         assert_se(s);
-        assert_se(streq(s, "1,2,3,3"));
+        ASSERT_STREQ(s, "1,2,3,3");
 
         _cleanup_free_ char *t = strv_join((char **)input_table_one, ", ");
         assert_se(t);
-        assert_se(streq(t, "one"));
+        ASSERT_STREQ(t, "one");
 
         _cleanup_free_ char *u = strv_join((char **)input_table_none, ", ");
         assert_se(u);
-        assert_se(streq(u, ""));
+        ASSERT_STREQ(u, "");
 
         _cleanup_free_ char *v = strv_join((char **)input_table_two_empties, ", ");
         assert_se(v);
-        assert_se(streq(v, ", "));
+        ASSERT_STREQ(v, ", ");
 
         _cleanup_free_ char *w = strv_join((char **)input_table_one_empty, ", ");
         assert_se(w);
-        assert_se(streq(w, ""));
+        ASSERT_STREQ(w, "");
 }
 
 TEST(strv_join_full) {
         _cleanup_free_ char *p = strv_join_full((char **)input_table_multiple, ", ", "foo", false);
         assert_se(p);
-        assert_se(streq(p, "fooone, footwo, foothree"));
+        ASSERT_STREQ(p, "fooone, footwo, foothree");
 
         _cleanup_free_ char *q = strv_join_full((char **)input_table_multiple, ";", "foo", false);
         assert_se(q);
-        assert_se(streq(q, "fooone;footwo;foothree"));
+        ASSERT_STREQ(q, "fooone;footwo;foothree");
 
         _cleanup_free_ char *r = strv_join_full(STRV_MAKE("a", "a;b", "a:c"), ";", NULL, true);
         assert_se(r);
-        assert_se(streq(r, "a;a\\;b;a:c"));
+        ASSERT_STREQ(r, "a;a\\;b;a:c");
 
         _cleanup_free_ char *s = strv_join_full(STRV_MAKE("a", "a;b", "a;;c", ";", ";x"), ";", NULL, true);
         assert_se(s);
-        assert_se(streq(s, "a;a\\;b;a\\;\\;c;\\;;\\;x"));
+        ASSERT_STREQ(s, "a;a\\;b;a\\;\\;c;\\;;\\;x");
 
         _cleanup_free_ char *t = strv_join_full(STRV_MAKE("a", "a;b", "a:c", ";"), ";", "=", true);
         assert_se(t);
-        assert_se(streq(t, "=a;=a\\;b;=a:c;=\\;"));
+        ASSERT_STREQ(t, "=a;=a\\;b;=a:c;=\\;");
         t = mfree(t);
 
         _cleanup_free_ char *u = strv_join_full((char **)input_table_multiple, NULL, "foo", false);
         assert_se(u);
-        assert_se(streq(u, "fooone footwo foothree"));
+        ASSERT_STREQ(u, "fooone footwo foothree");
 
         _cleanup_free_ char *v = strv_join_full((char **)input_table_one, ", ", "foo", false);
         assert_se(v);
-        assert_se(streq(v, "fooone"));
+        ASSERT_STREQ(v, "fooone");
 
         _cleanup_free_ char *w = strv_join_full((char **)input_table_none, ", ", "foo", false);
         assert_se(w);
-        assert_se(streq(w, ""));
+        ASSERT_STREQ(w, "");
 
         _cleanup_free_ char *x = strv_join_full((char **)input_table_two_empties, ", ", "foo", false);
         assert_se(x);
-        assert_se(streq(x, "foo, foo"));
+        ASSERT_STREQ(x, "foo, foo");
 
         _cleanup_free_ char *y = strv_join_full((char **)input_table_one_empty, ", ", "foo", false);
         assert_se(y);
-        assert_se(streq(y, "foo"));
+        ASSERT_STREQ(y, "foo");
 }
 
 static void test_strv_unquote_one(const char *quoted, char **list) {
@@ -215,9 +215,9 @@ static void test_strv_unquote_one(const char *quoted, char **list) {
         puts(j);
 
         STRV_FOREACH(t, s)
-                assert_se(streq(list[i++], *t));
+                ASSERT_STREQ(list[i++], *t);
 
-        assert_se(list[i] == NULL);
+        ASSERT_NULL(list[i]);
 }
 
 TEST(strv_unquote) {
@@ -245,7 +245,7 @@ static void test_invalid_unquote_one(const char *quoted) {
         log_info("/* %s */", __func__);
 
         r = strv_split_full(&s, quoted, WHITESPACE, EXTRACT_UNQUOTE);
-        assert_se(s == NULL);
+        ASSERT_NULL(s);
         assert_se(r == -EINVAL);
 }
 
@@ -402,12 +402,12 @@ TEST(strv_split_full) {
 
         r = strv_split_full(&l, str, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
         assert_se(r == (int) strv_length(l));
-        assert_se(streq_ptr(l[0], ""));
-        assert_se(streq_ptr(l[1], "foo:bar"));
-        assert_se(streq_ptr(l[2], ""));
-        assert_se(streq_ptr(l[3], "waldo"));
-        assert_se(streq_ptr(l[4], ""));
-        assert_se(streq_ptr(l[5], NULL));
+        ASSERT_STREQ(l[0], "");
+        ASSERT_STREQ(l[1], "foo:bar");
+        ASSERT_STREQ(l[2], "");
+        ASSERT_STREQ(l[3], "waldo");
+        ASSERT_STREQ(l[4], "");
+        ASSERT_STREQ(l[5], NULL);
 }
 
 TEST(strv_split_and_extend_full) {
@@ -420,14 +420,14 @@ TEST(strv_split_and_extend_full) {
         assert_se(r == (int) strv_length(l));
         r = strv_split_and_extend_full(&l, str1, ":", false, EXTRACT_DONT_COALESCE_SEPARATORS);
         assert_se(r == (int) strv_length(l));
-        assert_se(streq_ptr(l[0], ""));
-        assert_se(streq_ptr(l[1], "foo:bar"));
-        assert_se(streq_ptr(l[2], ""));
+        ASSERT_STREQ(l[0], "");
+        ASSERT_STREQ(l[1], "foo:bar");
+        ASSERT_STREQ(l[2], "");
         r = strv_split_and_extend_full(&l, str2, ":", false, 0);
         assert_se(r == (int) strv_length(l));
-        assert_se(streq_ptr(l[3], "waldo"));
-        assert_se(streq_ptr(l[4], "baz"));
-        assert_se(streq_ptr(l[5], NULL));
+        ASSERT_STREQ(l[3], "waldo");
+        ASSERT_STREQ(l[4], "baz");
+        ASSERT_STREQ(l[5], NULL);
 }
 
 TEST(strv_split_colon_pairs) {
@@ -439,19 +439,19 @@ TEST(strv_split_colon_pairs) {
         r = strv_split_colon_pairs(&l, str);
         assert_se(r == (int) strv_length(l));
         assert_se(r == 12);
-        assert_se(streq_ptr(l[0], "one"));
-        assert_se(streq_ptr(l[1], "two"));
-        assert_se(streq_ptr(l[2], "three"));
-        assert_se(streq_ptr(l[3], ""));
-        assert_se(streq_ptr(l[4], "four"));
-        assert_se(streq_ptr(l[5], "five"));
-        assert_se(streq_ptr(l[6], "six"));
-        assert_se(streq_ptr(l[7], ""));
-        assert_se(streq_ptr(l[8], "seven"));
-        assert_se(streq_ptr(l[9], "eight:nine"));
-        assert_se(streq_ptr(l[10], "ten:eleven\\"));
-        assert_se(streq_ptr(l[11], ""));
-        assert_se(streq_ptr(l[12], NULL));
+        ASSERT_STREQ(l[0], "one");
+        ASSERT_STREQ(l[1], "two");
+        ASSERT_STREQ(l[2], "three");
+        ASSERT_STREQ(l[3], "");
+        ASSERT_STREQ(l[4], "four");
+        ASSERT_STREQ(l[5], "five");
+        ASSERT_STREQ(l[6], "six");
+        ASSERT_STREQ(l[7], "");
+        ASSERT_STREQ(l[8], "seven");
+        ASSERT_STREQ(l[9], "eight:nine");
+        ASSERT_STREQ(l[10], "ten:eleven\\");
+        ASSERT_STREQ(l[11], "");
+        ASSERT_STREQ(l[12], NULL);
 
         r = strv_split_colon_pairs(&l, str_inval);
         assert_se(r == -EINVAL);
@@ -466,7 +466,7 @@ TEST(strv_split_newlines) {
         assert_se(l);
 
         STRV_FOREACH(s, l)
-                assert_se(streq(*s, input_table_multiple[i++]));
+                ASSERT_STREQ(*s, input_table_multiple[i++]);
 }
 
 TEST(strv_split_newlines_full) {
@@ -520,11 +520,11 @@ TEST(strv_sort) {
 
         strv_sort((char **)input_table);
 
-        assert_se(streq(input_table[0], "CAPITAL LETTERS FIRST"));
-        assert_se(streq(input_table[1], "apple"));
-        assert_se(streq(input_table[2], "banana"));
-        assert_se(streq(input_table[3], "citrus"));
-        assert_se(streq(input_table[4], "durian"));
+        ASSERT_STREQ(input_table[0], "CAPITAL LETTERS FIRST");
+        ASSERT_STREQ(input_table[1], "apple");
+        ASSERT_STREQ(input_table[2], "banana");
+        ASSERT_STREQ(input_table[3], "citrus");
+        ASSERT_STREQ(input_table[4], "durian");
 }
 
 TEST(strv_extend_strv_biconcat) {
@@ -537,10 +537,10 @@ TEST(strv_extend_strv_biconcat) {
 
         assert_se(strv_extend_strv_biconcat(&a, "prefix_", (const char* const*) b, "_suffix") >= 0);
 
-        assert_se(streq(a[0], "without"));
-        assert_se(streq(a[1], "suffix"));
-        assert_se(streq(a[2], "prefix_with_suffix"));
-        assert_se(streq(a[3], "prefix_suffix_suffix"));
+        ASSERT_STREQ(a[0], "without");
+        ASSERT_STREQ(a[1], "suffix");
+        ASSERT_STREQ(a[2], "prefix_with_suffix");
+        ASSERT_STREQ(a[3], "prefix_suffix_suffix");
 }
 
 TEST(strv_extend_strv_concat) {
@@ -553,10 +553,10 @@ TEST(strv_extend_strv_concat) {
 
         assert_se(strv_extend_strv_concat(&a, (const char* const*) b, "_suffix") >= 0);
 
-        assert_se(streq(a[0], "without"));
-        assert_se(streq(a[1], "suffix"));
-        assert_se(streq(a[2], "with_suffix"));
-        assert_se(streq(a[3], "suffix_suffix"));
+        ASSERT_STREQ(a[0], "without");
+        ASSERT_STREQ(a[1], "suffix");
+        ASSERT_STREQ(a[2], "with_suffix");
+        ASSERT_STREQ(a[3], "suffix_suffix");
 }
 
 TEST(strv_extend_strv) {
@@ -569,19 +569,19 @@ TEST(strv_extend_strv) {
 
         assert_se(strv_extend_strv(&a, b, true) == 3);
 
-        assert_se(streq(a[0], "abc"));
-        assert_se(streq(a[1], "def"));
-        assert_se(streq(a[2], "ghi"));
-        assert_se(streq(a[3], "jkl"));
-        assert_se(streq(a[4], "mno"));
-        assert_se(streq(a[5], "pqr"));
+        ASSERT_STREQ(a[0], "abc");
+        ASSERT_STREQ(a[1], "def");
+        ASSERT_STREQ(a[2], "ghi");
+        ASSERT_STREQ(a[3], "jkl");
+        ASSERT_STREQ(a[4], "mno");
+        ASSERT_STREQ(a[5], "pqr");
         assert_se(strv_length(a) == 6);
 
         assert_se(strv_extend_strv(&n, b, false) >= 0);
-        assert_se(streq(n[0], "jkl"));
-        assert_se(streq(n[1], "mno"));
-        assert_se(streq(n[2], "abc"));
-        assert_se(streq(n[3], "pqr"));
+        ASSERT_STREQ(n[0], "jkl");
+        ASSERT_STREQ(n[1], "mno");
+        ASSERT_STREQ(n[2], "abc");
+        ASSERT_STREQ(n[3], "pqr");
         assert_se(strv_length(n) == 4);
 }
 
@@ -597,11 +597,11 @@ TEST(strv_extend_with_size) {
         assert_se(strv_extend_with_size(&a, &n, "test3") >= 0);
         assert_se(n == 4);
 
-        assert_se(streq(a[0], "test"));
-        assert_se(streq(a[1], "test1"));
-        assert_se(streq(a[2], "test2"));
-        assert_se(streq(a[3], "test3"));
-        assert_se(a[4] == NULL);
+        ASSERT_STREQ(a[0], "test");
+        ASSERT_STREQ(a[1], "test1");
+        ASSERT_STREQ(a[2], "test2");
+        ASSERT_STREQ(a[3], "test3");
+        ASSERT_NULL(a[4]);
 }
 
 TEST(strv_extend) {
@@ -612,10 +612,10 @@ TEST(strv_extend) {
         assert_se(strv_extend(&a, "test2") >= 0);
         assert_se(strv_extend(&b, "test3") >= 0);
 
-        assert_se(streq(a[0], "test"));
-        assert_se(streq(a[1], "test1"));
-        assert_se(streq(a[2], "test2"));
-        assert_se(streq(b[0], "test3"));
+        ASSERT_STREQ(a[0], "test");
+        ASSERT_STREQ(a[1], "test1");
+        ASSERT_STREQ(a[2], "test2");
+        ASSERT_STREQ(b[0], "test3");
 }
 
 TEST(strv_extendf) {
@@ -626,10 +626,10 @@ TEST(strv_extendf) {
         assert_se(strv_extendf(&a, "test2 %s %d %s", "foo", 128, "bar") >= 0);
         assert_se(strv_extendf(&b, "test3 %s %s %d", "bar", "foo", 128) >= 0);
 
-        assert_se(streq(a[0], "test"));
-        assert_se(streq(a[1], "test1"));
-        assert_se(streq(a[2], "test2 foo 128 bar"));
-        assert_se(streq(b[0], "test3 bar foo 128"));
+        ASSERT_STREQ(a[0], "test");
+        ASSERT_STREQ(a[1], "test1");
+        ASSERT_STREQ(a[2], "test2 foo 128 bar");
+        ASSERT_STREQ(b[0], "test3 bar foo 128");
 }
 
 TEST(strv_foreach) {
@@ -640,7 +640,7 @@ TEST(strv_foreach) {
         assert_se(a);
 
         STRV_FOREACH(check, a)
-                assert_se(streq(*check, input_table_multiple[i++]));
+                ASSERT_STREQ(*check, input_table_multiple[i++]);
 }
 
 TEST(strv_foreach_backwards) {
@@ -652,7 +652,7 @@ TEST(strv_foreach_backwards) {
         assert_se(a);
 
         STRV_FOREACH_BACKWARDS(check, a)
-                assert_se(streq_ptr(*check, input_table_multiple[i--]));
+                ASSERT_STREQ(*check, input_table_multiple[i--]);
 
         STRV_FOREACH_BACKWARDS(check, (char**) NULL)
                 assert_not_reached();
@@ -673,7 +673,7 @@ TEST(strv_foreach_pair) {
                      "pair_two",   "pair_two",
                      "pair_three", "pair_three");
         STRV_FOREACH_PAIR(x, y, a)
-                assert_se(streq(*x, *y));
+                ASSERT_STREQ(*x, *y);
 }
 
 static void test_strv_from_stdarg_alloca_one(char **l, const char *first, ...) {
@@ -685,7 +685,7 @@ static void test_strv_from_stdarg_alloca_one(char **l, const char *first, ...) {
         j = strv_from_stdarg_alloca(first);
 
         for (i = 0;; i++) {
-                assert_se(streq_ptr(l[i], j[i]));
+                ASSERT_STREQ(l[i], j[i]);
 
                 if (!l[i])
                         break;
@@ -702,29 +702,29 @@ TEST(strv_insert) {
         _cleanup_strv_free_ char **a = NULL;
 
         assert_se(strv_insert(&a, 0, strdup("first")) == 0);
-        assert_se(streq(a[0], "first"));
+        ASSERT_STREQ(a[0], "first");
         assert_se(!a[1]);
 
         assert_se(strv_insert(&a, 0, NULL) == 0);
-        assert_se(streq(a[0], "first"));
+        ASSERT_STREQ(a[0], "first");
         assert_se(!a[1]);
 
         assert_se(strv_insert(&a, 1, strdup("two")) == 0);
-        assert_se(streq(a[0], "first"));
-        assert_se(streq(a[1], "two"));
+        ASSERT_STREQ(a[0], "first");
+        ASSERT_STREQ(a[1], "two");
         assert_se(!a[2]);
 
         assert_se(strv_insert(&a, 4, strdup("tri")) == 0);
-        assert_se(streq(a[0], "first"));
-        assert_se(streq(a[1], "two"));
-        assert_se(streq(a[2], "tri"));
+        ASSERT_STREQ(a[0], "first");
+        ASSERT_STREQ(a[1], "two");
+        ASSERT_STREQ(a[2], "tri");
         assert_se(!a[3]);
 
         assert_se(strv_insert(&a, 1, strdup("duo")) == 0);
-        assert_se(streq(a[0], "first"));
-        assert_se(streq(a[1], "duo"));
-        assert_se(streq(a[2], "two"));
-        assert_se(streq(a[3], "tri"));
+        ASSERT_STREQ(a[0], "first");
+        ASSERT_STREQ(a[1], "duo");
+        ASSERT_STREQ(a[2], "two");
+        ASSERT_STREQ(a[3], "tri");
         assert_se(!a[4]);
 }
 
@@ -734,18 +734,18 @@ TEST(strv_push_prepend) {
         assert_se(a = strv_new("foo", "bar", "three"));
 
         assert_se(strv_push_prepend(&a, strdup("first")) >= 0);
-        assert_se(streq(a[0], "first"));
-        assert_se(streq(a[1], "foo"));
-        assert_se(streq(a[2], "bar"));
-        assert_se(streq(a[3], "three"));
+        ASSERT_STREQ(a[0], "first");
+        ASSERT_STREQ(a[1], "foo");
+        ASSERT_STREQ(a[2], "bar");
+        ASSERT_STREQ(a[3], "three");
         assert_se(!a[4]);
 
         assert_se(strv_consume_prepend(&a, strdup("first2")) >= 0);
-        assert_se(streq(a[0], "first2"));
-        assert_se(streq(a[1], "first"));
-        assert_se(streq(a[2], "foo"));
-        assert_se(streq(a[3], "bar"));
-        assert_se(streq(a[4], "three"));
+        ASSERT_STREQ(a[0], "first2");
+        ASSERT_STREQ(a[1], "first");
+        ASSERT_STREQ(a[2], "foo");
+        ASSERT_STREQ(a[3], "bar");
+        ASSERT_STREQ(a[4], "three");
         assert_se(!a[5]);
 }
 
@@ -765,10 +765,10 @@ TEST(strv_push_with_size) {
         assert_se(strv_push_with_size(&a, &n, j) >= 0);
         assert_se(n == 3);
 
-        assert_se(streq_ptr(a[0], "foo"));
-        assert_se(streq_ptr(a[1], "a"));
-        assert_se(streq_ptr(a[2], "b"));
-        assert_se(streq_ptr(a[3], NULL));
+        ASSERT_STREQ(a[0], "foo");
+        ASSERT_STREQ(a[1], "a");
+        ASSERT_STREQ(a[2], "b");
+        ASSERT_STREQ(a[3], NULL);
 
         assert_se(n = strv_length(a));
 }
@@ -784,10 +784,10 @@ TEST(strv_push) {
         assert_se(j = strdup("b"));
         assert_se(strv_push_pair(&a, i, j) >= 0);
 
-        assert_se(streq_ptr(a[0], "foo"));
-        assert_se(streq_ptr(a[1], "a"));
-        assert_se(streq_ptr(a[2], "b"));
-        assert_se(streq_ptr(a[3], NULL));
+        ASSERT_STREQ(a[0], "foo");
+        ASSERT_STREQ(a[1], "a");
+        ASSERT_STREQ(a[2], "b");
+        ASSERT_STREQ(a[3], NULL);
 }
 
 TEST(strv_compare) {
@@ -849,23 +849,23 @@ TEST(strv_reverse) {
         b = strv_new("foo");
         assert_se(b);
         strv_reverse(b);
-        assert_se(streq_ptr(b[0], "foo"));
-        assert_se(streq_ptr(b[1], NULL));
+        ASSERT_STREQ(b[0], "foo");
+        ASSERT_STREQ(b[1], NULL);
 
         c = strv_new("foo", "bar");
         assert_se(c);
         strv_reverse(c);
-        assert_se(streq_ptr(c[0], "bar"));
-        assert_se(streq_ptr(c[1], "foo"));
-        assert_se(streq_ptr(c[2], NULL));
+        ASSERT_STREQ(c[0], "bar");
+        ASSERT_STREQ(c[1], "foo");
+        ASSERT_STREQ(c[2], NULL);
 
         d = strv_new("foo", "bar", "waldo");
         assert_se(d);
         strv_reverse(d);
-        assert_se(streq_ptr(d[0], "waldo"));
-        assert_se(streq_ptr(d[1], "bar"));
-        assert_se(streq_ptr(d[2], "foo"));
-        assert_se(streq_ptr(d[3], NULL));
+        ASSERT_STREQ(d[0], "waldo");
+        ASSERT_STREQ(d[1], "bar");
+        ASSERT_STREQ(d[2], "foo");
+        ASSERT_STREQ(d[3], NULL);
 }
 
 TEST(strv_shell_escape) {
@@ -874,10 +874,10 @@ TEST(strv_shell_escape) {
         v = strv_new("foo:bar", "bar,baz", "wal\\do");
         assert_se(v);
         assert_se(strv_shell_escape(v, ",:"));
-        assert_se(streq_ptr(v[0], "foo\\:bar"));
-        assert_se(streq_ptr(v[1], "bar\\,baz"));
-        assert_se(streq_ptr(v[2], "wal\\\\do"));
-        assert_se(streq_ptr(v[3], NULL));
+        ASSERT_STREQ(v[0], "foo\\:bar");
+        ASSERT_STREQ(v[1], "bar\\,baz");
+        ASSERT_STREQ(v[2], "wal\\\\do");
+        ASSERT_STREQ(v[3], NULL);
 }
 
 static void test_strv_skip_one(char **a, size_t n, char **b) {
@@ -911,22 +911,22 @@ TEST(strv_extend_n) {
         assert_se(strv_extend_n(&v, "waldo", 3) >= 0);
         assert_se(strv_extend_n(&v, "piep", 2) >= 0);
 
-        assert_se(streq(v[0], "foo"));
-        assert_se(streq(v[1], "bar"));
-        assert_se(streq(v[2], "waldo"));
-        assert_se(streq(v[3], "waldo"));
-        assert_se(streq(v[4], "waldo"));
-        assert_se(streq(v[5], "piep"));
-        assert_se(streq(v[6], "piep"));
-        assert_se(v[7] == NULL);
+        ASSERT_STREQ(v[0], "foo");
+        ASSERT_STREQ(v[1], "bar");
+        ASSERT_STREQ(v[2], "waldo");
+        ASSERT_STREQ(v[3], "waldo");
+        ASSERT_STREQ(v[4], "waldo");
+        ASSERT_STREQ(v[5], "piep");
+        ASSERT_STREQ(v[6], "piep");
+        ASSERT_NULL(v[7]);
 
         v = strv_free(v);
 
         assert_se(strv_extend_n(&v, "foo", 1) >= 0);
         assert_se(strv_extend_n(&v, "bar", 0) >= 0);
 
-        assert_se(streq(v[0], "foo"));
-        assert_se(v[1] == NULL);
+        ASSERT_STREQ(v[0], "foo");
+        ASSERT_NULL(v[1]);
 }
 
 TEST(foreach_string) {
@@ -939,11 +939,11 @@ TEST(foreach_string) {
 
         unsigned i = 0;
         FOREACH_STRING(x, "foo", "bar", "waldo")
-                assert_se(streq_ptr(t[i++], x));
+                ASSERT_STREQ(t[i++], x);
         assert_se(i == 3);
 
         FOREACH_STRING(x, "zzz")
-                assert_se(streq(x, "zzz"));
+                ASSERT_STREQ(x, "zzz");
 }
 
 TEST(strv_fnmatch) {
@@ -966,8 +966,8 @@ TEST(strv_extend_join) {
         assert_se(strv_extend_assignment(&v, "MISSING", NULL) >= 0);
 
         assert_se(strv_length(v) == 2);
-        assert_se(streq(v[0], "MESSAGE=ABC"));
-        assert_se(streq(v[1], "ABC=QER"));
+        ASSERT_STREQ(v[0], "MESSAGE=ABC");
+        ASSERT_STREQ(v[1], "ABC=QER");
 }
 
 TEST(strv_copy_n) {
@@ -1013,21 +1013,21 @@ TEST(strv_copy_n) {
 TEST(strv_find_first_field) {
         char **haystack = STRV_MAKE("a", "b", "c", "d", "e", "f", "g", "h", "i", "j");
 
-        assert_se(strv_find_first_field(NULL, NULL) == NULL);
-        assert_se(strv_find_first_field(NULL, haystack) == NULL);
-        assert_se(strv_find_first_field(STRV_MAKE("k", "l", "m", "d", "b"), NULL) == NULL);
-        assert_se(strv_find_first_field(STRV_MAKE("k", "l", "m", "d", "b"), haystack) == NULL);
-        assert_se(streq_ptr(strv_find_first_field(STRV_MAKE("k", "l", "m", "d", "a", "c"), haystack), "b"));
-        assert_se(streq_ptr(strv_find_first_field(STRV_MAKE("k", "l", "m", "d", "c", "a"), haystack), "d"));
-        assert_se(streq_ptr(strv_find_first_field(STRV_MAKE("i", "k", "l", "m", "d", "c", "a", "b"), haystack), "j"));
+        ASSERT_NULL(strv_find_first_field(NULL, NULL));
+        ASSERT_NULL(strv_find_first_field(NULL, haystack));
+        ASSERT_NULL(strv_find_first_field(STRV_MAKE("k", "l", "m", "d", "b"), NULL));
+        ASSERT_NULL(strv_find_first_field(STRV_MAKE("k", "l", "m", "d", "b"), haystack));
+        ASSERT_STREQ(strv_find_first_field(STRV_MAKE("k", "l", "m", "d", "a", "c"), haystack), "b");
+        ASSERT_STREQ(strv_find_first_field(STRV_MAKE("k", "l", "m", "d", "c", "a"), haystack), "d");
+        ASSERT_STREQ(strv_find_first_field(STRV_MAKE("i", "k", "l", "m", "d", "c", "a", "b"), haystack), "j");
 }
 
 TEST(endswith_strv) {
-        assert_se(streq_ptr(endswith_strv("waldo", STRV_MAKE("xxx", "yyy", "ldo", "zzz")), "ldo"));
-        assert_se(streq_ptr(endswith_strv("waldo", STRV_MAKE("xxx", "yyy", "zzz")), NULL));
-        assert_se(streq_ptr(endswith_strv("waldo", STRV_MAKE("waldo")), "waldo"));
-        assert_se(streq_ptr(endswith_strv("waldo", STRV_MAKE("w", "o", "ldo")), "o"));
-        assert_se(streq_ptr(endswith_strv("waldo", STRV_MAKE("knurz", "", "waldo")), ""));
+        ASSERT_STREQ(endswith_strv("waldo", STRV_MAKE("xxx", "yyy", "ldo", "zzz")), "ldo");
+        ASSERT_STREQ(endswith_strv("waldo", STRV_MAKE("xxx", "yyy", "zzz")), NULL);
+        ASSERT_STREQ(endswith_strv("waldo", STRV_MAKE("waldo")), "waldo");
+        ASSERT_STREQ(endswith_strv("waldo", STRV_MAKE("w", "o", "ldo")), "o");
+        ASSERT_STREQ(endswith_strv("waldo", STRV_MAKE("knurz", "", "waldo")), "");
 }
 
 TEST(strv_extend_many) {
index b679522fc620c6293d9ee85fa573a23c11024d7f..00f595c4bb9a61d52a1977120f2d27d77862cba1 100644 (file)
@@ -24,27 +24,27 @@ TEST(strpcpy) {
         space_left = strpcpy_full(&s, space_left, "r", &truncated);
         assert_se(!truncated);
         assert_se(space_left == 1);
-        assert_se(streq(target, "12345hey hey heywaldobar"));
+        ASSERT_STREQ(target, "12345hey hey heywaldobar");
 
         space_left = strpcpy_full(&s, space_left, "", &truncated);
         assert_se(!truncated);
         assert_se(space_left == 1);
-        assert_se(streq(target, "12345hey hey heywaldobar"));
+        ASSERT_STREQ(target, "12345hey hey heywaldobar");
 
         space_left = strpcpy_full(&s, space_left, "f", &truncated);
         assert_se(truncated);
         assert_se(space_left == 0);
-        assert_se(streq(target, "12345hey hey heywaldobar"));
+        ASSERT_STREQ(target, "12345hey hey heywaldobar");
 
         space_left = strpcpy_full(&s, space_left, "", &truncated);
         assert_se(!truncated);
         assert_se(space_left == 0);
-        assert_se(streq(target, "12345hey hey heywaldobar"));
+        ASSERT_STREQ(target, "12345hey hey heywaldobar");
 
         space_left = strpcpy_full(&s, space_left, "foo", &truncated);
         assert_se(truncated);
         assert_se(space_left == 0);
-        assert_se(streq(target, "12345hey hey heywaldobar"));
+        ASSERT_STREQ(target, "12345hey hey heywaldobar");
 }
 
 TEST(strpcpyf) {
@@ -59,38 +59,38 @@ TEST(strpcpyf) {
         space_left = strpcpyf_full(&s, space_left, &truncated, "foo%s", "bar");
         assert_se(!truncated);
         assert_se(space_left == 3);
-        assert_se(streq(target, "space left: 25. foobar"));
+        ASSERT_STREQ(target, "space left: 25. foobar");
 
         space_left = strpcpyf_full(&s, space_left, &truncated, "%i", 42);
         assert_se(!truncated);
         assert_se(space_left == 1);
-        assert_se(streq(target, "space left: 25. foobar42"));
+        ASSERT_STREQ(target, "space left: 25. foobar42");
 
         space_left = strpcpyf_full(&s, space_left, &truncated, "%s", "");
         assert_se(!truncated);
         assert_se(space_left == 1);
-        assert_se(streq(target, "space left: 25. foobar42"));
+        ASSERT_STREQ(target, "space left: 25. foobar42");
 
         space_left = strpcpyf_full(&s, space_left, &truncated, "%c", 'x');
         assert_se(truncated);
         assert_se(space_left == 0);
-        assert_se(streq(target, "space left: 25. foobar42"));
+        ASSERT_STREQ(target, "space left: 25. foobar42");
 
         space_left = strpcpyf_full(&s, space_left, &truncated, "%s", "");
         assert_se(!truncated);
         assert_se(space_left == 0);
-        assert_se(streq(target, "space left: 25. foobar42"));
+        ASSERT_STREQ(target, "space left: 25. foobar42");
 
         space_left = strpcpyf_full(&s, space_left, &truncated, "abc%s", "hoge");
         assert_se(truncated);
         assert_se(space_left == 0);
-        assert_se(streq(target, "space left: 25. foobar42"));
+        ASSERT_STREQ(target, "space left: 25. foobar42");
 
         /* test overflow */
         s = target;
         space_left = strpcpyf_full(&s, 12, &truncated, "00 left: %i. ", 999);
         assert_se(truncated);
-        assert_se(streq(target, "00 left: 99"));
+        ASSERT_STREQ(target, "00 left: 99");
         assert_se(space_left == 0);
         assert_se(target[12] == '2');
 }
@@ -107,22 +107,22 @@ TEST(strpcpyl) {
         space_left = strpcpyl_full(&s, space_left, &truncated, "Banana", NULL);
         assert_se(!truncated);
         assert_se(space_left == 1);
-        assert_se(streq(target, "waldo test waldo. Banana"));
+        ASSERT_STREQ(target, "waldo test waldo. Banana");
 
         space_left = strpcpyl_full(&s, space_left, &truncated, "", "", "", NULL);
         assert_se(!truncated);
         assert_se(space_left == 1);
-        assert_se(streq(target, "waldo test waldo. Banana"));
+        ASSERT_STREQ(target, "waldo test waldo. Banana");
 
         space_left = strpcpyl_full(&s, space_left, &truncated, "", "x", "", NULL);
         assert_se(truncated);
         assert_se(space_left == 0);
-        assert_se(streq(target, "waldo test waldo. Banana"));
+        ASSERT_STREQ(target, "waldo test waldo. Banana");
 
         space_left = strpcpyl_full(&s, space_left, &truncated, "hoge", NULL);
         assert_se(truncated);
         assert_se(space_left == 0);
-        assert_se(streq(target, "waldo test waldo. Banana"));
+        ASSERT_STREQ(target, "waldo test waldo. Banana");
 }
 
 TEST(strscpy) {
@@ -134,7 +134,7 @@ TEST(strscpy) {
         space_left = strscpy_full(target, space_left, "12345", &truncated);
         assert_se(!truncated);
 
-        assert_se(streq(target, "12345"));
+        ASSERT_STREQ(target, "12345");
         assert_se(space_left == 20);
 }
 
@@ -147,7 +147,7 @@ TEST(strscpyl) {
         space_left = strscpyl_full(target, space_left, &truncated, "12345", "waldo", "waldo", NULL);
         assert_se(!truncated);
 
-        assert_se(streq(target, "12345waldowaldo"));
+        ASSERT_STREQ(target, "12345waldowaldo");
         assert_se(space_left == 10);
 }
 
@@ -169,7 +169,7 @@ TEST(sd_event_code_migration) {
         for (i = 0; i < 100; i++)
                 l = strpcpyf(&p, l, "%u ", i);
 
-        assert_se(streq(b, c));
+        ASSERT_STREQ(b, c);
 }
 
 DEFINE_TEST_MAIN(LOG_INFO);
index 81207f5cfd3c5c15027ee3a168eaab2d0e770977..e94099605ca49ae0e75d57a0d197d80e41d64009 100644 (file)
@@ -34,7 +34,7 @@ TEST(sysctl_normalize) {
                 assert_se(sysctl_normalize(t) == t);
 
                 log_info("\"%s\" → \"%s\", expected \"%s\"", *s, t, *expected);
-                assert_se(streq(t, *expected));
+                ASSERT_STREQ(t, *expected);
         }
 }
 
@@ -66,7 +66,7 @@ TEST(sysctl_read) {
 
         assert_se(sysctl_read("kernel/hostname", &s) >= 0);
         assert_se(uname(&u) >= 0);
-        assert_se(streq_ptr(s, u.nodename));
+        ASSERT_STREQ(s, u.nodename);
 
         r = sysctl_write("kernel/hostname", s);
         assert_se(r >= 0 || ERRNO_IS_PRIVILEGE(r) || r == -EROFS);
index 0b46cad9827a40b81adf46964c01b8d719d1d6e0..dbd76549919f2663793debf0eb0e4c55279f92ff 100644 (file)
@@ -155,7 +155,7 @@ TEST(get_ctty) {
         }
 
         /* In almost all cases STDIN will match our controlling TTY. Let's verify that and then compare paths */
-        assert_se(fstat(STDIN_FILENO, &st) >= 0);
+        ASSERT_OK_ERRNO(fstat(STDIN_FILENO, &st));
         if (S_ISCHR(st.st_mode) && st.st_rdev == devnr) {
                 _cleanup_free_ char *stdin_name = NULL;
 
index 9ce014dc9e3cbe432daf5ae6695d92e55ad80cce..9943923be30ac2bbbaf15f62d396806661eb118b 100644 (file)
@@ -413,7 +413,7 @@ static void test_format_timestamp_impl(usec_t x) {
                  override ? ", ignoring." : "");
         if (!override) {
                 assert_se(x / USEC_PER_SEC == y / USEC_PER_SEC);
-                assert_se(streq(xx, yy));
+                ASSERT_STREQ(xx, yy);
         }
 }
 
@@ -482,69 +482,69 @@ TEST(format_timestamp_relative_full) {
         x = now(CLOCK_REALTIME) - (1*USEC_PER_YEAR + 1*USEC_PER_MONTH);
         assert_se(format_timestamp_relative_full(buf, sizeof(buf), x, CLOCK_REALTIME, true));
         log_debug("%s", buf);
-        assert_se(streq(buf, "1 year 1 month ago"));
+        ASSERT_STREQ(buf, "1 year 1 month ago");
 
         x = now(CLOCK_MONOTONIC) + (1*USEC_PER_YEAR + 1.5*USEC_PER_MONTH);
         assert_se(format_timestamp_relative_full(buf, sizeof(buf), x, CLOCK_MONOTONIC, false));
         log_debug("%s", buf);
-        assert_se(streq(buf, "1 year 1 month left"));
+        ASSERT_STREQ(buf, "1 year 1 month left");
 
         x = now(CLOCK_REALTIME) - (1*USEC_PER_YEAR + 2*USEC_PER_MONTH);
         assert_se(format_timestamp_relative_full(buf, sizeof(buf), x, CLOCK_REALTIME, true));
         log_debug("%s", buf);
-        assert_se(streq(buf, "1 year 2 months ago"));
+        ASSERT_STREQ(buf, "1 year 2 months ago");
 
         x = now(CLOCK_REALTIME) - (2*USEC_PER_YEAR + 1*USEC_PER_MONTH);
         assert_se(format_timestamp_relative_full(buf, sizeof(buf), x, CLOCK_REALTIME, true));
         log_debug("%s", buf);
-        assert_se(streq(buf, "2 years 1 month ago"));
+        ASSERT_STREQ(buf, "2 years 1 month ago");
 
         x = now(CLOCK_REALTIME) - (2*USEC_PER_YEAR + 2*USEC_PER_MONTH);
         assert_se(format_timestamp_relative_full(buf, sizeof(buf), x, CLOCK_REALTIME, true));
         log_debug("%s", buf);
-        assert_se(streq(buf, "2 years 2 months ago"));
+        ASSERT_STREQ(buf, "2 years 2 months ago");
 
         /* Months and days */
         x = now(CLOCK_REALTIME) - (1*USEC_PER_MONTH + 1*USEC_PER_DAY);
         assert_se(format_timestamp_relative_full(buf, sizeof(buf), x, CLOCK_REALTIME, true));
         log_debug("%s", buf);
-        assert_se(streq(buf, "1 month 1 day ago"));
+        ASSERT_STREQ(buf, "1 month 1 day ago");
 
         x = now(CLOCK_REALTIME) - (1*USEC_PER_MONTH + 2*USEC_PER_DAY);
         assert_se(format_timestamp_relative_full(buf, sizeof(buf), x, CLOCK_REALTIME, true));
         log_debug("%s", buf);
-        assert_se(streq(buf, "1 month 2 days ago"));
+        ASSERT_STREQ(buf, "1 month 2 days ago");
 
         x = now(CLOCK_REALTIME) - (2*USEC_PER_MONTH + 1*USEC_PER_DAY);
         assert_se(format_timestamp_relative_full(buf, sizeof(buf), x, CLOCK_REALTIME, true));
         log_debug("%s", buf);
-        assert_se(streq(buf, "2 months 1 day ago"));
+        ASSERT_STREQ(buf, "2 months 1 day ago");
 
         x = now(CLOCK_REALTIME) - (2*USEC_PER_MONTH + 2*USEC_PER_DAY);
         assert_se(format_timestamp_relative_full(buf, sizeof(buf), x, CLOCK_REALTIME, true));
         log_debug("%s", buf);
-        assert_se(streq(buf, "2 months 2 days ago"));
+        ASSERT_STREQ(buf, "2 months 2 days ago");
 
         /* Weeks and days */
         x = now(CLOCK_REALTIME) - (1*USEC_PER_WEEK + 1*USEC_PER_DAY);
         assert_se(format_timestamp_relative_full(buf, sizeof(buf), x, CLOCK_REALTIME, true));
         log_debug("%s", buf);
-        assert_se(streq(buf, "1 week 1 day ago"));
+        ASSERT_STREQ(buf, "1 week 1 day ago");
 
         x = now(CLOCK_REALTIME) - (1*USEC_PER_WEEK + 2*USEC_PER_DAY);
         assert_se(format_timestamp_relative_full(buf, sizeof(buf), x, CLOCK_REALTIME, true));
         log_debug("%s", buf);
-        assert_se(streq(buf, "1 week 2 days ago"));
+        ASSERT_STREQ(buf, "1 week 2 days ago");
 
         x = now(CLOCK_REALTIME) - (2*USEC_PER_WEEK + 1*USEC_PER_DAY);
         assert_se(format_timestamp_relative_full(buf, sizeof(buf), x, CLOCK_REALTIME, true));
         log_debug("%s", buf);
-        assert_se(streq(buf, "2 weeks 1 day ago"));
+        ASSERT_STREQ(buf, "2 weeks 1 day ago");
 
         x = now(CLOCK_REALTIME) - (2*USEC_PER_WEEK + 2*USEC_PER_DAY);
         assert_se(format_timestamp_relative_full(buf, sizeof(buf), x, CLOCK_REALTIME, true));
         log_debug("%s", buf);
-        assert_se(streq(buf, "2 weeks 2 days ago"));
+        ASSERT_STREQ(buf, "2 weeks 2 days ago");
 }
 
 TEST(format_timestamp_relative) {
@@ -559,64 +559,64 @@ TEST(format_timestamp_relative) {
         x = now(CLOCK_REALTIME) - (1*USEC_PER_YEAR + 1*USEC_PER_MONTH);
         assert_se(format_timestamp_relative(buf, sizeof(buf), x));
         log_debug("%s", buf);
-        assert_se(streq(buf, "1 year 1 month ago"));
+        ASSERT_STREQ(buf, "1 year 1 month ago");
 
         x = now(CLOCK_REALTIME) - (1*USEC_PER_YEAR + 2*USEC_PER_MONTH);
         assert_se(format_timestamp_relative(buf, sizeof(buf), x));
         log_debug("%s", buf);
-        assert_se(streq(buf, "1 year 2 months ago"));
+        ASSERT_STREQ(buf, "1 year 2 months ago");
 
         x = now(CLOCK_REALTIME) - (2*USEC_PER_YEAR + 1*USEC_PER_MONTH);
         assert_se(format_timestamp_relative(buf, sizeof(buf), x));
         log_debug("%s", buf);
-        assert_se(streq(buf, "2 years 1 month ago"));
+        ASSERT_STREQ(buf, "2 years 1 month ago");
 
         x = now(CLOCK_REALTIME) - (2*USEC_PER_YEAR + 2*USEC_PER_MONTH);
         assert_se(format_timestamp_relative(buf, sizeof(buf), x));
         log_debug("%s", buf);
-        assert_se(streq(buf, "2 years 2 months ago"));
+        ASSERT_STREQ(buf, "2 years 2 months ago");
 
         /* Months and days */
         x = now(CLOCK_REALTIME) - (1*USEC_PER_MONTH + 1*USEC_PER_DAY);
         assert_se(format_timestamp_relative(buf, sizeof(buf), x));
         log_debug("%s", buf);
-        assert_se(streq(buf, "1 month 1 day ago"));
+        ASSERT_STREQ(buf, "1 month 1 day ago");
 
         x = now(CLOCK_REALTIME) - (1*USEC_PER_MONTH + 2*USEC_PER_DAY);
         assert_se(format_timestamp_relative(buf, sizeof(buf), x));
         log_debug("%s", buf);
-        assert_se(streq(buf, "1 month 2 days ago"));
+        ASSERT_STREQ(buf, "1 month 2 days ago");
 
         x = now(CLOCK_REALTIME) - (2*USEC_PER_MONTH + 1*USEC_PER_DAY);
         assert_se(format_timestamp_relative(buf, sizeof(buf), x));
         log_debug("%s", buf);
-        assert_se(streq(buf, "2 months 1 day ago"));
+        ASSERT_STREQ(buf, "2 months 1 day ago");
 
         x = now(CLOCK_REALTIME) - (2*USEC_PER_MONTH + 2*USEC_PER_DAY);
         assert_se(format_timestamp_relative(buf, sizeof(buf), x));
         log_debug("%s", buf);
-        assert_se(streq(buf, "2 months 2 days ago"));
+        ASSERT_STREQ(buf, "2 months 2 days ago");
 
         /* Weeks and days */
         x = now(CLOCK_REALTIME) - (1*USEC_PER_WEEK + 1*USEC_PER_DAY);
         assert_se(format_timestamp_relative(buf, sizeof(buf), x));
         log_debug("%s", buf);
-        assert_se(streq(buf, "1 week 1 day ago"));
+        ASSERT_STREQ(buf, "1 week 1 day ago");
 
         x = now(CLOCK_REALTIME) - (1*USEC_PER_WEEK + 2*USEC_PER_DAY);
         assert_se(format_timestamp_relative(buf, sizeof(buf), x));
         log_debug("%s", buf);
-        assert_se(streq(buf, "1 week 2 days ago"));
+        ASSERT_STREQ(buf, "1 week 2 days ago");
 
         x = now(CLOCK_REALTIME) - (2*USEC_PER_WEEK + 1*USEC_PER_DAY);
         assert_se(format_timestamp_relative(buf, sizeof(buf), x));
         log_debug("%s", buf);
-        assert_se(streq(buf, "2 weeks 1 day ago"));
+        ASSERT_STREQ(buf, "2 weeks 1 day ago");
 
         x = now(CLOCK_REALTIME) - (2*USEC_PER_WEEK + 2*USEC_PER_DAY);
         assert_se(format_timestamp_relative(buf, sizeof(buf), x));
         log_debug("%s", buf);
-        assert_se(streq(buf, "2 weeks 2 days ago"));
+        ASSERT_STREQ(buf, "2 weeks 2 days ago");
 }
 
 static void test_format_timestamp_one(usec_t val, TimestampStyle style, const char *result) {
@@ -624,7 +624,7 @@ static void test_format_timestamp_one(usec_t val, TimestampStyle style, const ch
         const char *t;
 
         t = format_timestamp_style(buf, sizeof(buf), val, style);
-        assert_se(streq_ptr(t, result));
+        ASSERT_STREQ(t, result);
 }
 
 TEST(format_timestamp_range) {
@@ -1062,15 +1062,15 @@ TEST(in_utc_timezone) {
 
         assert_se(setenv("TZ", ":UTC", 1) >= 0);
         assert_se(in_utc_timezone());
-        assert_se(streq(tzname[0], "UTC"));
-        assert_se(streq(tzname[1], "UTC"));
+        ASSERT_STREQ(tzname[0], "UTC");
+        ASSERT_STREQ(tzname[1], "UTC");
         assert_se(timezone == 0);
         assert_se(daylight == 0);
 
         assert_se(setenv("TZ", ":Europe/Berlin", 1) >= 0);
         assert_se(!in_utc_timezone());
-        assert_se(streq(tzname[0], "CET"));
-        assert_se(streq(tzname[1], "CEST"));
+        ASSERT_STREQ(tzname[0], "CET");
+        ASSERT_STREQ(tzname[1], "CEST");
 
         assert_se(set_unset_env("TZ", tz, true) == 0);
         tzset();
@@ -1114,14 +1114,14 @@ static void test_timezone_offset_change_one(const char *utc, const char *pretty)
         s = FORMAT_TIMESTAMP_STYLE(x, TIMESTAMP_UTC);
         assert_se(parse_timestamp(s, &y) >= 0);
         log_debug("%s -> " USEC_FMT " -> %s -> " USEC_FMT, utc, x, s, y);
-        assert_se(streq(s, utc));
+        ASSERT_STREQ(s, utc);
         assert_se(x == y);
 
         assert_se(parse_timestamp(pretty, &y) >= 0);
         s = FORMAT_TIMESTAMP_STYLE(y, TIMESTAMP_PRETTY);
         assert_se(parse_timestamp(s, &z) >= 0);
         log_debug("%s -> " USEC_FMT " -> %s -> " USEC_FMT, pretty, y, s, z);
-        assert_se(streq(s, pretty));
+        ASSERT_STREQ(s, pretty);
         assert_se(x == y);
         assert_se(x == z);
 }
index 4859f62da8431cde9aba86a93e6d7a1ec16b7f24..bef503bf3ef1e1fd86a6a97186ab5be7ed09d26c 100644 (file)
@@ -105,7 +105,7 @@ static void test_tempfn_xxxxxx_one(const char *p, const char *extra, const char
                 const char *suffix;
 
                 assert_se(suffix = startswith(s, expect));
-                assert_se(streq(suffix, "XXXXXX"));
+                ASSERT_STREQ(suffix, "XXXXXX");
         }
         assert_se(ret == r);
 }
@@ -283,7 +283,7 @@ TEST(link_tmpfile) {
         assert_se(link_tmpfile(fd, tmp, d, /* flags= */ 0) >= 0);
 
         assert_se(read_one_line_file(d, &line) >= 0);
-        assert_se(streq(line, "foobar"));
+        ASSERT_STREQ(line, "foobar");
 
         fd = safe_close(fd);
         tmp = mfree(tmp);
@@ -298,7 +298,7 @@ TEST(link_tmpfile) {
 
         line = mfree(line);
         assert_se(read_one_line_file(d, &line) >= 0);
-        assert_se(streq(line, "waumiau"));
+        ASSERT_STREQ(line, "waumiau");
 
         assert_se(unlink(d) >= 0);
 }
index 254c4c5e8bc2218a1a1baf4e44a3f7bc84d569db..1a9c998818828c6eaefa6ef432e579b7112fcec0 100644 (file)
@@ -199,7 +199,7 @@ static void _test_tpms_sw(
         tpm2_tpms_pcr_selection_from_mask(mask, hash, &s);
 
         _cleanup_free_ char *tpms_str = tpm2_tpms_pcr_selection_to_string(&s);
-        assert_se(streq(tpms_str, expected_str));
+        ASSERT_STREQ(tpms_str, expected_str);
 
         assert_se(tpm2_tpms_pcr_selection_weight(&s) == expected_weight);
         assert_se(tpm2_tpms_pcr_selection_is_empty(&s) == (expected_weight == 0));
@@ -242,7 +242,7 @@ static void _test_tpml_sw(
         assert_se(l.count == expected_count);
 
         _cleanup_free_ char *tpml_str = tpm2_tpml_pcr_selection_to_string(&l);
-        assert_se(streq(tpml_str, expected_str));
+        ASSERT_STREQ(tpml_str, expected_str);
 
         assert_se(tpm2_tpml_pcr_selection_weight(&l) == expected_weight);
         assert_se(tpm2_tpml_pcr_selection_is_empty(&l) == (expected_weight == 0));
@@ -678,7 +678,7 @@ TEST(parse_pcr_argument) {
         assert_se(tpm2_parse_pcr_argument("1,2=123456abc", &v, &n_v) < 0);
         assert_se(tpm2_parse_pcr_argument("1,2:invalid", &v, &n_v) < 0);
         assert_se(tpm2_parse_pcr_argument("1:sha1=invalid", &v, &n_v) < 0);
-        assert_se(v == NULL);
+        ASSERT_NULL(v);
         assert_se(n_v == 0);
 
         check_parse_pcr_argument_to_mask("", 0x0);
index cb80c69c968aeda925378741c5e003b87fa71c57..07f4e018ac4c314447d72585718472901a7cad44 100644 (file)
@@ -16,7 +16,7 @@ static void test_udev_replace_whitespace_one_len(const char *str, size_t len, co
         assert_se(result);
         r = udev_replace_whitespace(str, result, len);
         assert_se((size_t) r == strlen(expected));
-        assert_se(streq(result, expected));
+        ASSERT_STREQ(result, expected);
 }
 
 static void test_udev_replace_whitespace_one(const char *str, const char *expected) {
index e8799e78100d498c8670b6d757b052406df5565f..6041d084906423d73217924dd294c2beb33fcc89 100644 (file)
@@ -23,9 +23,15 @@ TEST(uid_range) {
         assert_se(!uid_range_covers(p, UINT32_MAX, 1));
         assert_se(!uid_range_covers(p, UINT32_MAX - 10, 11));
 
+        assert_se(uid_range_entries(p) == 0);
+        assert_se(uid_range_size(p) == 0);
+        assert_se(uid_range_is_empty(p));
+
         assert_se(uid_range_add_str(&p, "500-999") >= 0);
         assert_se(p);
-        assert_se(p->n_entries == 1);
+        assert_se(uid_range_entries(p) == 1);
+        assert_se(uid_range_size(p) == 500);
+        assert_se(!uid_range_is_empty(p));
         assert_se(p->entries[0].start == 500);
         assert_se(p->entries[0].nr == 500);
 
@@ -56,19 +62,23 @@ TEST(uid_range) {
         assert_se(uid_range_next_lower(p, &search) == -EBUSY);
 
         assert_se(uid_range_add_str(&p, "1000") >= 0);
-        assert_se(p->n_entries == 1);
+        assert_se(uid_range_entries(p) == 1);
         assert_se(p->entries[0].start == 500);
         assert_se(p->entries[0].nr == 501);
 
         assert_se(uid_range_add_str(&p, "30-40") >= 0);
-        assert_se(p->n_entries == 2);
+        assert_se(uid_range_entries(p) == 2);
+        assert_se(uid_range_size(p) == 500 + 1 + 11);
+        assert_se(!uid_range_is_empty(p));
         assert_se(p->entries[0].start == 30);
         assert_se(p->entries[0].nr == 11);
         assert_se(p->entries[1].start == 500);
         assert_se(p->entries[1].nr == 501);
 
         assert_se(uid_range_add_str(&p, "60-70") >= 0);
-        assert_se(p->n_entries == 3);
+        assert_se(uid_range_entries(p) == 3);
+        assert_se(uid_range_size(p) == 500 + 1 + 11 + 11);
+        assert_se(!uid_range_is_empty(p));
         assert_se(p->entries[0].start == 30);
         assert_se(p->entries[0].nr == 11);
         assert_se(p->entries[1].start == 60);
@@ -77,21 +87,34 @@ TEST(uid_range) {
         assert_se(p->entries[2].nr == 501);
 
         assert_se(uid_range_add_str(&p, "20-2000") >= 0);
-        assert_se(p->n_entries == 1);
+        assert_se(uid_range_entries(p) == 1);
+        assert_se(uid_range_size(p) == 1981);
         assert_se(p->entries[0].start == 20);
         assert_se(p->entries[0].nr == 1981);
 
         assert_se(uid_range_add_str(&p, "2002") >= 0);
-        assert_se(p->n_entries == 2);
+        assert_se(uid_range_entries(p) == 2);
+        assert_se(uid_range_size(p) == 1982);
         assert_se(p->entries[0].start == 20);
         assert_se(p->entries[0].nr == 1981);
         assert_se(p->entries[1].start == 2002);
         assert_se(p->entries[1].nr == 1);
 
+        _cleanup_(uid_range_freep) UIDRange *q = NULL;
+        assert_se(!uid_range_equal(p, q));
+        assert_se(uid_range_add_str(&q, "20-2000") >= 0);
+        assert_se(!uid_range_equal(p, q));
+        assert_se(uid_range_add_str(&q, "2002") >= 0);
+        assert_se(uid_range_equal(p, q));
+
         assert_se(uid_range_add_str(&p, "2001") >= 0);
-        assert_se(p->n_entries == 1);
+        assert_se(uid_range_entries(p) == 1);
+        assert_se(uid_range_size(p) == 1983);
         assert_se(p->entries[0].start == 20);
         assert_se(p->entries[0].nr == 1983);
+
+        assert_se(uid_range_add_str(&q, "2001") >= 0);
+        assert_se(uid_range_equal(p, q));
 }
 
 TEST(load_userns) {
@@ -100,7 +123,7 @@ TEST(load_userns) {
         _cleanup_fclose_ FILE *f = NULL;
         int r;
 
-        r = uid_range_load_userns(&p, NULL);
+        r = uid_range_load_userns(NULL, UID_RANGE_USERNS_INSIDE, &p);
         if (ERRNO_IS_NEG_NOT_SUPPORTED(r))
                 return;
 
@@ -123,7 +146,7 @@ TEST(load_userns) {
 
         p = uid_range_free(p);
 
-        assert_se(uid_range_load_userns(&p, fn) >= 0);
+        assert_se(uid_range_load_userns(fn, UID_RANGE_USERNS_INSIDE, &p) >= 0);
 
         assert_se(uid_range_contains(p, 0));
         assert_se(uid_range_contains(p, 19));
index 220802e350ba32faed09bb28530baa924253f15e..63e500003c5303cb7e69a665972f3f69fd075e63 100644 (file)
@@ -88,18 +88,18 @@ TEST(unit_file_build_name_map) {
 
 TEST(runlevel_to_target) {
         in_initrd_force(false);
-        assert_se(streq_ptr(runlevel_to_target(NULL), NULL));
-        assert_se(streq_ptr(runlevel_to_target("unknown-runlevel"), NULL));
-        assert_se(streq_ptr(runlevel_to_target("rd.unknown-runlevel"), NULL));
-        assert_se(streq_ptr(runlevel_to_target("3"), SPECIAL_MULTI_USER_TARGET));
-        assert_se(streq_ptr(runlevel_to_target("rd.rescue"), NULL));
+        ASSERT_STREQ(runlevel_to_target(NULL), NULL);
+        ASSERT_STREQ(runlevel_to_target("unknown-runlevel"), NULL);
+        ASSERT_STREQ(runlevel_to_target("rd.unknown-runlevel"), NULL);
+        ASSERT_STREQ(runlevel_to_target("3"), SPECIAL_MULTI_USER_TARGET);
+        ASSERT_STREQ(runlevel_to_target("rd.rescue"), NULL);
 
         in_initrd_force(true);
-        assert_se(streq_ptr(runlevel_to_target(NULL), NULL));
-        assert_se(streq_ptr(runlevel_to_target("unknown-runlevel"), NULL));
-        assert_se(streq_ptr(runlevel_to_target("rd.unknown-runlevel"), NULL));
-        assert_se(streq_ptr(runlevel_to_target("3"), NULL));
-        assert_se(streq_ptr(runlevel_to_target("rd.rescue"), SPECIAL_RESCUE_TARGET));
+        ASSERT_STREQ(runlevel_to_target(NULL), NULL);
+        ASSERT_STREQ(runlevel_to_target("unknown-runlevel"), NULL);
+        ASSERT_STREQ(runlevel_to_target("rd.unknown-runlevel"), NULL);
+        ASSERT_STREQ(runlevel_to_target("3"), NULL);
+        ASSERT_STREQ(runlevel_to_target("rd.rescue"), SPECIAL_RESCUE_TARGET);
 }
 
 static int intro(void) {
index 8e9332c33e52460681d2021a19d6541edf7a197f..b4821047693d65328210db4c82eb3fcffde90469 100644 (file)
@@ -92,7 +92,7 @@ static void test_unit_name_replace_instance_one(const char *pattern, const char
         _cleanup_free_ char *t = NULL;
         assert_se(unit_name_replace_instance(pattern, repl, &t) == ret);
         puts(strna(t));
-        assert_se(streq_ptr(t, expected));
+        ASSERT_STREQ(t, expected);
 }
 
 TEST(unit_name_replace_instance) {
@@ -112,7 +112,7 @@ static void test_unit_name_from_path_one(const char *path, const char *suffix, c
 
         assert_se(unit_name_from_path(path, suffix, &t) == ret);
         puts(strna(t));
-        assert_se(streq_ptr(t, expected));
+        ASSERT_STREQ(t, expected);
 
         if (t) {
                 _cleanup_free_ char *k = NULL;
@@ -159,7 +159,7 @@ static void test_unit_name_from_path_instance_one(const char *pattern, const cha
 
         assert_se(unit_name_from_path_instance(pattern, path, suffix, &t) == ret);
         puts(strna(t));
-        assert_se(streq_ptr(t, expected));
+        ASSERT_STREQ(t, expected);
 
         if (t) {
                 _cleanup_free_ char *k = NULL, *v = NULL;
@@ -185,7 +185,7 @@ static void test_unit_name_to_path_one(const char *unit, const char *path, int r
         _cleanup_free_ char *p = NULL;
 
         assert_se(unit_name_to_path(unit, &p) == ret);
-        assert_se(streq_ptr(path, p));
+        ASSERT_STREQ(path, p);
 }
 
 TEST(unit_name_to_path) {
@@ -208,7 +208,7 @@ static void test_unit_name_mangle_one(bool allow_globs, const char *pattern, con
 
         assert_se(r == ret);
         puts(strna(t));
-        assert_se(streq_ptr(t, expect));
+        ASSERT_STREQ(t, expect);
 
         if (t) {
                 _cleanup_free_ char *k = NULL;
@@ -217,7 +217,7 @@ static void test_unit_name_mangle_one(bool allow_globs, const char *pattern, con
                           (allow_globs && string_is_glob(t)));
 
                 assert_se(unit_name_mangle(t, (allow_globs * UNIT_NAME_MANGLE_GLOB) | UNIT_NAME_MANGLE_WARN, &k) == 0);
-                assert_se(streq_ptr(t, k));
+                ASSERT_STREQ(t, k);
         }
 }
 
@@ -246,7 +246,7 @@ static void test_unit_name_mangle_with_suffix_one(const char *arg, int expected,
         log_debug("%s: %s -> %d, %s", __func__, arg, r, strnull(s));
 
         assert_se(r == expected);
-        assert_se(streq_ptr(s, expected_name));
+        ASSERT_STREQ(s, expected_name);
 }
 
 TEST(unit_name_mangle_with_suffix) {
@@ -499,11 +499,11 @@ TEST(unit_name_change_suffix) {
         char *t;
 
         assert_se(unit_name_change_suffix("foo.mount", ".service", &t) == 0);
-        assert_se(streq(t, "foo.service"));
+        ASSERT_STREQ(t, "foo.service");
         free(t);
 
         assert_se(unit_name_change_suffix("foo@stuff.service", ".socket", &t) == 0);
-        assert_se(streq(t, "foo@stuff.socket"));
+        ASSERT_STREQ(t, "foo@stuff.socket");
         free(t);
 }
 
@@ -511,15 +511,15 @@ TEST(unit_name_build) {
         char *t;
 
         assert_se(unit_name_build("foo", "bar", ".service", &t) == 0);
-        assert_se(streq(t, "foo@bar.service"));
+        ASSERT_STREQ(t, "foo@bar.service");
         free(t);
 
         assert_se(unit_name_build("fo0-stUff_b", "bar", ".mount", &t) == 0);
-        assert_se(streq(t, "fo0-stUff_b@bar.mount"));
+        ASSERT_STREQ(t, "fo0-stUff_b@bar.mount");
         free(t);
 
         assert_se(unit_name_build("foo", NULL, ".service", &t) == 0);
-        assert_se(streq(t, "foo.service"));
+        ASSERT_STREQ(t, "foo.service");
         free(t);
 }
 
@@ -563,7 +563,7 @@ TEST(build_subslice) {
         free(b);
         assert_se(slice_build_subslice(a, "foobar", &b) >= 0);
         free(a);
-        assert_se(streq(b, "foo-bar-barfoo-foobar.slice"));
+        ASSERT_STREQ(b, "foo-bar-barfoo-foobar.slice");
         free(b);
 
         assert_se(slice_build_subslice("foo.service", "bar", &a) < 0);
@@ -574,7 +574,7 @@ static void test_build_parent_slice_one(const char *name, const char *expect, in
         _cleanup_free_ char *s = NULL;
 
         assert_se(slice_build_parent_slice(name, &s) == ret);
-        assert_se(streq_ptr(s, expect));
+        ASSERT_STREQ(s, expect);
 }
 
 TEST(build_parent_slice) {
@@ -602,17 +602,17 @@ TEST(unit_name_to_instance) {
 
         r = unit_name_to_instance("foo@bar.service", &instance);
         assert_se(r == UNIT_NAME_INSTANCE);
-        assert_se(streq(instance, "bar"));
+        ASSERT_STREQ(instance, "bar");
         free(instance);
 
         r = unit_name_to_instance("foo@.service", &instance);
         assert_se(r == UNIT_NAME_TEMPLATE);
-        assert_se(streq(instance, ""));
+        ASSERT_STREQ(instance, "");
         free(instance);
 
         r = unit_name_to_instance("fo0-stUff_b@b.service", &instance);
         assert_se(r == UNIT_NAME_INSTANCE);
-        assert_se(streq(instance, "b"));
+        ASSERT_STREQ(instance, "b");
         free(instance);
 
         r = unit_name_to_instance("foo.service", &instance);
@@ -633,7 +633,7 @@ TEST(unit_name_escape) {
 
         r = unit_name_escape("ab+-c.a/bc@foo.service");
         assert_se(r);
-        assert_se(streq(r, "ab\\x2b\\x2dc.a-bc\\x40foo.service"));
+        ASSERT_STREQ(r, "ab\\x2b\\x2dc.a-bc\\x40foo.service");
 }
 
 static void test_u_n_t_one(const char *name, const char *expected, int ret) {
@@ -641,7 +641,7 @@ static void test_u_n_t_one(const char *name, const char *expected, int ret) {
 
         assert_se(unit_name_template(name, &f) == ret);
         printf("got: %s, expected: %s\n", strna(f), strna(expected));
-        assert_se(streq_ptr(f, expected));
+        ASSERT_STREQ(f, expected);
 }
 
 TEST(unit_name_template) {
@@ -653,7 +653,7 @@ static void test_unit_name_path_unescape_one(const char *name, const char *path,
         _cleanup_free_ char *p = NULL;
 
         assert_se(unit_name_path_unescape(name, &p) == ret);
-        assert_se(streq_ptr(path, p));
+        ASSERT_STREQ(path, p);
 }
 
 TEST(unit_name_path_unescape) {
@@ -675,7 +675,7 @@ static void test_unit_name_to_prefix_one(const char *input, int ret, const char
         _cleanup_free_ char *k = NULL;
 
         assert_se(unit_name_to_prefix(input, &k) == ret);
-        assert_se(streq_ptr(k, output));
+        ASSERT_STREQ(k, output);
 }
 
 TEST(unit_name_to_prefix) {
@@ -695,7 +695,7 @@ static void test_unit_name_from_dbus_path_one(const char *input, int ret, const
         _cleanup_free_ char *k = NULL;
 
         assert_se(unit_name_from_dbus_path(input, &k) == ret);
-        assert_se(streq_ptr(k, output));
+        ASSERT_STREQ(k, output);
 }
 
 TEST(unit_name_from_dbus_path) {
index f115a477738e67f5b7712cb640be6e272a8b26c0..e21d8da478a66bb414ffcfb63175975841b0208e 100644 (file)
@@ -21,7 +21,7 @@ static void test_uid_to_name_one(uid_t uid, const char *name) {
                 log_info("(skipping detailed tests because nobody is not synthesized)");
                 return;
         }
-        assert_se(streq_ptr(t, name));
+        ASSERT_STREQ(t, name);
 }
 
 TEST(uid_to_name) {
@@ -41,7 +41,7 @@ static void test_gid_to_name_one(gid_t gid, const char *name) {
                 log_info("(skipping detailed tests because nobody is not synthesized)");
                 return;
         }
-        assert_se(streq_ptr(t, name));
+        ASSERT_STREQ(t, name);
 }
 
 TEST(gid_to_name) {
@@ -338,7 +338,7 @@ static void test_get_user_creds_one(const char *id, const char *name, uid_t uid,
                 return;
         }
         assert_se(r == 0);
-        assert_se(streq_ptr(id, name));
+        ASSERT_STREQ(id, name);
         assert_se(ruid == uid);
         assert_se(rgid == gid);
         assert_se(path_equal(rhome, home));
@@ -364,7 +364,7 @@ static void test_get_group_creds_one(const char *id, const char *name, gid_t gid
                 return;
         }
         assert_se(r == 0);
-        assert_se(streq_ptr(id, name));
+        ASSERT_STREQ(id, name);
         assert_se(rgid == gid);
 }
 
@@ -466,7 +466,7 @@ static void test_mangle_gecos_one(const char *input, const char *expected) {
         _cleanup_free_ char *p = NULL;
 
         assert_se(p = mangle_gecos(input));
-        assert_se(streq(p, expected));
+        ASSERT_STREQ(p, expected);
         assert_se(valid_gecos(p));
 }
 
index a0d7dc14ef1d75395305e360a08ac99cd143416b..d60cf00bf325c7c8f444ab6e346ce8d7a6a08165 100644 (file)
@@ -62,7 +62,7 @@ static void test_utf8_to_ascii_one(const char *s, int r_expected, const char *ex
         r = utf8_to_ascii(s, '*', &ans);
         log_debug("\"%s\" → %d/\"%s\" (expected %d/\"%s\")", s, r, strnull(ans), r_expected, strnull(expected));
         assert_se(r == r_expected);
-        assert_se(streq_ptr(ans, expected));
+        ASSERT_STREQ(ans, expected);
 }
 
 TEST(utf8_to_ascii) {
@@ -223,7 +223,7 @@ TEST(utf8_to_utf16) {
 
                 b = utf16_to_utf8(a, SIZE_MAX);
                 assert_se(b);
-                assert_se(streq(p, b));
+                ASSERT_STREQ(p, b);
         }
 }
 
index 3ca36a07a4966e9e06f0246cff5ccb5bcbd1d3ca..86dbcc963cd8ccd83ab6de9b9db9be765ddd74af 100644 (file)
 #include "varlink-io.systemd.Credentials.h"
 #include "varlink-io.systemd.Journal.h"
 #include "varlink-io.systemd.ManagedOOM.h"
+#include "varlink-io.systemd.MountFileSystem.h"
+#include "varlink-io.systemd.NamespaceResource.h"
 #include "varlink-io.systemd.Network.h"
 #include "varlink-io.systemd.PCRExtend.h"
 #include "varlink-io.systemd.PCRLock.h"
-#include "varlink-io.systemd.Resolve.Monitor.h"
 #include "varlink-io.systemd.Resolve.h"
+#include "varlink-io.systemd.Resolve.Monitor.h"
 #include "varlink-io.systemd.UserDatabase.h"
 #include "varlink-io.systemd.oom.h"
 #include "varlink-io.systemd.service.h"
@@ -121,7 +123,7 @@ static void test_parse_format_one(const VarlinkInterface *iface) {
         assert_se(varlink_idl_parse(text, NULL, NULL, &parsed) >= 0);
         assert_se(varlink_idl_consistent(parsed, LOG_ERR) >= 0);
         assert_se(varlink_idl_format(parsed, &text2) >= 0);
-        assert_se(streq(text, text2));
+        ASSERT_STREQ(text, text2);
 }
 
 TEST(parse_format) {
@@ -129,6 +131,8 @@ TEST(parse_format) {
         print_separator();
         test_parse_format_one(&vl_interface_io_systemd_UserDatabase);
         print_separator();
+        test_parse_format_one(&vl_interface_io_systemd_NamespaceResource);
+        print_separator();
         test_parse_format_one(&vl_interface_io_systemd_Journal);
         print_separator();
         test_parse_format_one(&vl_interface_io_systemd_Resolve);
@@ -137,6 +141,8 @@ TEST(parse_format) {
         print_separator();
         test_parse_format_one(&vl_interface_io_systemd_ManagedOOM);
         print_separator();
+        test_parse_format_one(&vl_interface_io_systemd_MountFileSystem);
+        print_separator();
         test_parse_format_one(&vl_interface_io_systemd_Network);
         print_separator();
         test_parse_format_one(&vl_interface_io_systemd_oom);
@@ -381,13 +387,13 @@ TEST(validate_method_call) {
                                                 JSON_BUILD_PAIR_UNSIGNED("foo", 8),
                                                 JSON_BUILD_PAIR_UNSIGNED("bar", 9),
                                                 JSON_BUILD_PAIR_STRING("zzz", "pfft"))) >= 0);
-        assert_se(streq_ptr(error_id, VARLINK_ERROR_INVALID_PARAMETER));
+        ASSERT_STREQ(error_id, VARLINK_ERROR_INVALID_PARAMETER);
 
         assert_se(varlink_callb(v, "xyz.TestMethod", &reply, &error_id,
                                 JSON_BUILD_OBJECT(
                                                 JSON_BUILD_PAIR_BOOLEAN("foo", true),
                                                 JSON_BUILD_PAIR_UNSIGNED("bar", 9))) >= 0);
-        assert_se(streq_ptr(error_id, VARLINK_ERROR_INVALID_PARAMETER));
+        ASSERT_STREQ(error_id, VARLINK_ERROR_INVALID_PARAMETER);
 
         assert_se(varlink_send(v, "xyz.Done", NULL) >= 0);
         assert_se(varlink_flush(v) >= 0);
index cd9c5936130ff76a011427c6e5add8be2e8ce9f2..8ad5757b9d38b993d40cdf49110edf99f4f370ae 100644 (file)
@@ -107,7 +107,7 @@ static int method_passfd(Varlink *link, JsonVariant *parameters, VarlinkMethodFl
         if (!a)
                 return varlink_error(link, "io.test.BadParameters", NULL);
 
-        assert_se(streq_ptr(json_variant_string(a), "whoop"));
+        ASSERT_STREQ(json_variant_string(a), "whoop");
 
         int xx = varlink_peek_fd(link, 0),
                 yy = varlink_peek_fd(link, 1),
@@ -183,7 +183,7 @@ static int overload_reply(Varlink *link, JsonVariant *parameters, const char *er
          * be talking to an overloaded server */
 
         log_debug("Over reply triggered with error: %s", strna(error_id));
-        assert_se(streq(error_id, VARLINK_ERROR_DISCONNECTED));
+        ASSERT_STREQ(error_id, VARLINK_ERROR_DISCONNECTED);
         sd_event_exit(varlink_get_event(link), 0);
 
         return 0;
@@ -299,8 +299,8 @@ static void *thread(void *arg) {
         test_fd(fd5, "wuff", 4);
 
         assert_se(varlink_callb(c, "io.test.IDontExist", &o, &e, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("x", JSON_BUILD_REAL(5.5)))) >= 0);
-        assert_se(streq_ptr(json_variant_string(json_variant_by_key(o, "method")), "io.test.IDontExist"));
-        assert_se(streq(e, VARLINK_ERROR_METHOD_NOT_FOUND));
+        ASSERT_STREQ(json_variant_string(json_variant_by_key(o, "method")), "io.test.IDontExist");
+        ASSERT_STREQ(e, VARLINK_ERROR_METHOD_NOT_FOUND);
 
         flood_test(arg);
 
index 1a288e8ba121e136d3a07ca1379231b81c9da60a..feb16b61a464487aa97150e055340e9d3d9bd3e5 100644 (file)
@@ -53,7 +53,7 @@ TEST(path_pick) {
         if (IN_SET(native_architecture(), ARCHITECTURE_X86, ARCHITECTURE_X86_64)) {
                 assert_se(path_pick(NULL, AT_FDCWD, pp, &filter, PICK_ARCHITECTURE|PICK_TRIES, &result) > 0);
                 assert_se(S_ISREG(result.st.st_mode));
-                assert_se(streq_ptr(result.version, "99"));
+                ASSERT_STREQ(result.version, "99");
                 assert_se(result.architecture == ARCHITECTURE_X86);
                 assert_se(endswith(result.path, "/foo_99_x86.raw"));
 
@@ -63,7 +63,7 @@ TEST(path_pick) {
         filter.architecture = ARCHITECTURE_X86_64;
         assert_se(path_pick(NULL, AT_FDCWD, pp, &filter, PICK_ARCHITECTURE|PICK_TRIES, &result) > 0);
         assert_se(S_ISREG(result.st.st_mode));
-        assert_se(streq_ptr(result.version, "55"));
+        ASSERT_STREQ(result.version, "55");
         assert_se(result.architecture == ARCHITECTURE_X86_64);
         assert_se(endswith(result.path, "/foo_55_x86-64.raw"));
         pick_result_done(&result);
@@ -71,7 +71,7 @@ TEST(path_pick) {
         filter.architecture = ARCHITECTURE_IA64;
         assert_se(path_pick(NULL, AT_FDCWD, pp, &filter, PICK_ARCHITECTURE|PICK_TRIES, &result) > 0);
         assert_se(S_ISREG(result.st.st_mode));
-        assert_se(streq_ptr(result.version, "5"));
+        ASSERT_STREQ(result.version, "5");
         assert_se(result.architecture == ARCHITECTURE_IA64);
         assert_se(endswith(result.path, "/foo_5_ia64.raw"));
         pick_result_done(&result);
@@ -80,7 +80,7 @@ TEST(path_pick) {
         filter.version = "5";
         assert_se(path_pick(NULL, AT_FDCWD, pp, &filter, PICK_ARCHITECTURE|PICK_TRIES, &result) > 0);
         assert_se(S_ISREG(result.st.st_mode));
-        assert_se(streq_ptr(result.version, "5"));
+        ASSERT_STREQ(result.version, "5");
         if (native_architecture() != ARCHITECTURE_IA64) {
                 assert_se(result.architecture == _ARCHITECTURE_INVALID);
                 assert_se(endswith(result.path, "/foo_5.raw"));
@@ -90,7 +90,7 @@ TEST(path_pick) {
         filter.architecture = ARCHITECTURE_IA64;
         assert_se(path_pick(NULL, AT_FDCWD, pp, &filter, PICK_ARCHITECTURE|PICK_TRIES, &result) > 0);
         assert_se(S_ISREG(result.st.st_mode));
-        assert_se(streq_ptr(result.version, "5"));
+        ASSERT_STREQ(result.version, "5");
         assert_se(result.architecture == ARCHITECTURE_IA64);
         assert_se(endswith(result.path, "/foo_5_ia64.raw"));
         pick_result_done(&result);
@@ -109,7 +109,7 @@ TEST(path_pick) {
         if (IN_SET(native_architecture(), ARCHITECTURE_X86_64, ARCHITECTURE_X86)) {
                 assert_se(path_pick(NULL, AT_FDCWD, pp, &filter, PICK_ARCHITECTURE|PICK_TRIES, &result) > 0);
                 assert_se(S_ISREG(result.st.st_mode));
-                assert_se(streq_ptr(result.version, "55"));
+                ASSERT_STREQ(result.version, "55");
 
                 if (native_architecture() == ARCHITECTURE_X86_64) {
                         assert_se(result.architecture == ARCHITECTURE_X86_64);
@@ -129,7 +129,7 @@ TEST(path_pick) {
         if (IN_SET(native_architecture(), ARCHITECTURE_X86, ARCHITECTURE_X86_64)) {
                 assert_se(path_pick(NULL, AT_FDCWD, pp, &filter, PICK_ARCHITECTURE|PICK_TRIES, &result) > 0);
                 assert_se(S_ISREG(result.st.st_mode));
-                assert_se(streq_ptr(result.version, "55"));
+                ASSERT_STREQ(result.version, "55");
                 assert_se(result.architecture == native_architecture());
                 assert_se(endswith(result.path, ".raw"));
                 assert_se(strrstr(result.path, "/foo_55_x86"));
@@ -161,7 +161,7 @@ TEST(path_pick) {
 
         assert_se(path_pick(NULL, AT_FDCWD, pp, &filter, PICK_ARCHITECTURE|PICK_TRIES, &result) > 0);
         assert_se(S_ISREG(result.st.st_mode));
-        assert_se(streq_ptr(result.version, "2"));
+        ASSERT_STREQ(result.version, "2");
         assert_se(result.tries_left == 4);
         assert_se(result.tries_done == 6);
         assert_se(endswith(result.path, "quux_2_s390+4-6.raw"));
index 271af24c8040af4d5a89be7a296f2880def43643..423a9be380259bc5683d966a27a0ea61eb13c8cc 100644 (file)
@@ -54,7 +54,7 @@ int main(int argc, char *argv[]) {
         assert_se(pid >= 0);
 
         assert_se(hashmap_isempty(m->watch_pids));
-        assert_se(manager_get_unit_by_pid(m, pid) == NULL);
+        ASSERT_NULL(manager_get_unit_by_pid(m, pid));
 
         assert_se(unit_watch_pid(a, pid, false) >= 0);
         assert_se(manager_get_unit_by_pid(m, pid) == a);
@@ -93,10 +93,10 @@ int main(int argc, char *argv[]) {
         assert_se(manager_get_unit_by_pid(m, pid) == c);
 
         unit_unwatch_pid(c, pid);
-        assert_se(manager_get_unit_by_pid(m, pid) == NULL);
+        ASSERT_NULL(manager_get_unit_by_pid(m, pid));
 
         unit_unwatch_pid(c, pid);
-        assert_se(manager_get_unit_by_pid(m, pid) == NULL);
+        ASSERT_NULL(manager_get_unit_by_pid(m, pid));
 
         return 0;
 }
index b473165a3e0058748d0a6643cbb30d211850064d..c754ac526e85877d69c8abdf101c93d85026c045 100644 (file)
@@ -51,7 +51,7 @@ TEST(getxattr_at_malloc) {
         fd = open(x, O_PATH|O_CLOEXEC);
         assert_se(fd >= 0);
         assert_se(getxattr_at_malloc(fd, NULL, "user.foo", 0, &value) == 3);
-        assert_se(streq(value, "bar"));
+        ASSERT_STREQ(value, "bar");
 }
 
 TEST(getcrtime) {
@@ -83,7 +83,7 @@ static void verify_xattr(int dfd, const char *expected) {
         _cleanup_free_ char *value = NULL;
 
         assert_se(getxattr_at_malloc(dfd, "test", "user.foo", 0, &value) == (int) strlen(expected));
-        assert_se(streq(value, expected));
+        ASSERT_STREQ(value, expected);
 }
 
 TEST(xsetxattr) {
index a8cb635aa31d6db4d550b4468e12782b32970e7a..6367f2726373bed36e6de8bd5241fa951fb2a04d 100644 (file)
@@ -29,7 +29,7 @@ static void test_one(const char *data, ...) {
                         break;
 
                 nn = va_arg(ap, const char *);
-                assert_se(streq_ptr(nn, name));
+                ASSERT_STREQ(nn, name);
         }
 
         va_end(ap);
index 32e7bbd5b4defeb7cc07da202b2132d33072d5c2..4840ba47b1c1b386b8623a2ec0e5c19fe79b6e4b 100644 (file)
@@ -46,6 +46,7 @@
 #define NULL_ADJTIME_LOCAL "0.0 0 0\n0\nLOCAL\n"
 
 #define UNIT_LIST_DIRS (const char* const*) CONF_PATHS_STRV("systemd/ntp-units.d")
+#define SET_NTP_IN_FLIGHT_MAX 16
 
 typedef struct UnitStatusInfo {
         char *name;
@@ -62,6 +63,7 @@ typedef struct Context {
         bool local_rtc;
         Hashmap *polkit_registry;
         sd_bus_message *cache;
+        Set *set_ntp_calls;
 
         sd_bus_slot *slot_job_removed;
 
@@ -122,6 +124,7 @@ static void context_clear(Context *c) {
         free(c->zone);
         hashmap_free(c->polkit_registry);
         sd_bus_message_unref(c->cache);
+        set_free(c->set_ntp_calls);
 
         sd_bus_slot_unref(c->slot_job_removed);
 
@@ -462,11 +465,19 @@ static int match_job_removed(sd_bus_message *m, void *userdata, sd_bus_error *er
                         n += !!u->path;
 
         if (n == 0) {
+                sd_bus_message *cm;
+
                 c->slot_job_removed = sd_bus_slot_unref(c->slot_job_removed);
 
                 (void) sd_bus_emit_properties_changed(sd_bus_message_get_bus(m),
                                                       "/org/freedesktop/timedate1", "org.freedesktop.timedate1", "NTP",
                                                       NULL);
+                while ((cm = set_steal_first(c->set_ntp_calls))) {
+                        r = sd_bus_reply_method_return(cm, NULL);
+                        if (r < 0)
+                                log_debug_errno(r, "Failed to reply to SetNTP method call, ignoring: %m");
+                        sd_bus_message_unref(cm);
+                }
         }
 
         return 0;
@@ -939,6 +950,9 @@ static int method_set_ntp(sd_bus_message *m, void *userdata, sd_bus_error *error
         LIST_FOREACH(units, u, c->units)
                 u->path = mfree(u->path);
 
+        if (set_size(c->set_ntp_calls) >= SET_NTP_IN_FLIGHT_MAX)
+                return sd_bus_error_set_errnof(error, EAGAIN, "Too many calls in flight.");
+
         if (!c->slot_job_removed) {
                 r = bus_match_signal_async(
                                 bus,
@@ -993,11 +1007,12 @@ static int method_set_ntp(sd_bus_message *m, void *userdata, sd_bus_error *error
                 c->slot_job_removed = TAKE_PTR(slot);
 
         if (selected)
-                log_info("Set NTP to enabled (%s).", selected->name);
+                log_info("Set NTP to be enabled (%s).", selected->name);
         else
-                log_info("Set NTP to disabled.");
+                log_info("Set NTP to be disabled.");
 
-        return sd_bus_reply_method_return(m, NULL);
+        /* Asynchronous reply to m in match_job_removed() */
+        return set_ensure_consume(&c->set_ntp_calls, &bus_message_hash_ops, sd_bus_message_ref(m));
 }
 
 static int method_list_timezones(sd_bus_message *m, void *userdata, sd_bus_error *error) {
index 684448097c6294f8887e96467b02ff8edf8c7a33..d843ed78bfa4292672e48426ec1ec5f8204cc972 100644 (file)
@@ -21,8 +21,7 @@ if get_option('link-timesyncd-shared')
         timesyncd_link_with = [libshared]
 else
         timesyncd_link_with = [libsystemd_static,
-                               libshared_static,
-                               libbasic_gcrypt]
+                               libshared_static]
 endif
 
 libtimesyncd_core = static_library(
index 8a24a21a274ee046be77ce5a8f76a3ccc6036cf6..2e918509a7b45ce972507bedb673401a2379fe4c 100644 (file)
@@ -21,7 +21,6 @@ executables += [
                 'c_args' : '-DSTANDALONE',
                 'link_with' : [
                         libbasic,
-                        libbasic_gcrypt,
                         libshared_static,
                         libsystemd_static,
                 ],
index 824ec478039fa75ba2b1a6515cf90ef31ecf00d6..3535551e7440dbd12e6b9f6f217ba94c1684a171 100644 (file)
@@ -114,7 +114,7 @@ libudevd_core = static_library(
         include_directories : includes + include_directories('net'),
         link_with : udev_link_with,
         dependencies : [libblkid,
-                        libkmod,
+                        libkmod_cflags,
                         userspace],
         build_by_default : false)
 
@@ -205,6 +205,7 @@ executables += [
         },
         udev_test_template + {
                 'sources' : files('net/test-link-config-tables.c'),
+                'include_directories' : includes + include_directories('.'),
                 'suite' : 'udev',
         },
         udev_test_template + {
@@ -240,6 +241,7 @@ executables += [
         },
         udev_fuzz_template + {
                 'sources' : files('net/fuzz-link-parser.c'),
+                'include_directories' : includes + include_directories('.'),
         },
         udev_fuzz_template + {
                 'sources' : files('fuzz-udev-rule-parse-value.c'),
index 7f16b17467d3da9477053de049c643c9790a82ee..647cdeeb9dba604f9aa2cad9490f0fca923c7519 100644 (file)
@@ -483,7 +483,7 @@ int link_get_config(LinkConfigContext *ctx, Link *link) {
         return -ENOENT;
 }
 
-static int link_apply_ethtool_settings(Link *link, int *ethtool_fd) {
+static int link_apply_ethtool_settings(Link *link, int *ethtool_fd, EventMode mode) {
         LinkConfig *config;
         const char *name;
         int r;
@@ -492,6 +492,11 @@ static int link_apply_ethtool_settings(Link *link, int *ethtool_fd) {
         assert(link->config);
         assert(ethtool_fd);
 
+        if (mode != EVENT_UDEV_WORKER) {
+                log_link_debug(link, "Running in test mode, skipping application of ethtool settings.");
+                return 0;
+        }
+
         config = link->config;
         name = link->ifname;
 
@@ -684,7 +689,7 @@ finalize:
         return 0;
 }
 
-static int link_apply_rtnl_settings(Link *link, sd_netlink **rtnl) {
+static int link_apply_rtnl_settings(Link *link, sd_netlink **rtnl, EventMode mode) {
         struct hw_addr_data hw_addr = {};
         LinkConfig *config;
         int r;
@@ -693,6 +698,11 @@ static int link_apply_rtnl_settings(Link *link, sd_netlink **rtnl) {
         assert(link->config);
         assert(rtnl);
 
+        if (mode != EVENT_UDEV_WORKER) {
+                log_link_debug(link, "Running in test mode, skipping application of rtnl settings.");
+                return 0;
+        }
+
         config = link->config;
 
         (void) link_generate_new_hw_addr(link, &hw_addr);
@@ -896,7 +906,7 @@ static int sr_iov_configure(Link *link, sd_netlink **rtnl, SRIOV *sr_iov) {
         return 0;
 }
 
-static int link_apply_sr_iov_config(Link *link, sd_netlink **rtnl) {
+static int link_apply_sr_iov_config(Link *link, sd_netlink **rtnl, EventMode mode) {
         SRIOV *sr_iov;
         uint32_t n;
         int r;
@@ -905,6 +915,11 @@ static int link_apply_sr_iov_config(Link *link, sd_netlink **rtnl) {
         assert(link->config);
         assert(link->device);
 
+        if (mode != EVENT_UDEV_WORKER) {
+                log_link_debug(link, "Running in test mode, skipping application of SR-IOV settings.");
+                return 0;
+        }
+
         r = sr_iov_set_num_vfs(link->device, link->config->sr_iov_num_vfs, link->config->sr_iov_by_section);
         if (r < 0)
                 log_link_warning_errno(link, r, "Failed to set the number of SR-IOV virtual functions, ignoring: %m");
@@ -938,7 +953,7 @@ static int link_apply_sr_iov_config(Link *link, sd_netlink **rtnl) {
         return 0;
 }
 
-static int link_apply_rps_cpu_mask(Link *link) {
+static int link_apply_rps_cpu_mask(Link *link, EventMode mode) {
         _cleanup_free_ char *mask_str = NULL;
         LinkConfig *config;
         int r;
@@ -946,6 +961,11 @@ static int link_apply_rps_cpu_mask(Link *link) {
         assert(link);
         config = ASSERT_PTR(link->config);
 
+        if (mode != EVENT_UDEV_WORKER) {
+                log_link_debug(link, "Running in test mode, skipping application of RPS setting.");
+                return 0;
+        }
+
         /* Skip if the config is not specified. */
         if (!config->rps_cpu_mask)
                 return 0;
@@ -981,7 +1001,7 @@ static int link_apply_rps_cpu_mask(Link *link) {
         return 0;
 }
 
-static int link_apply_udev_properties(Link *link, bool test) {
+static int link_apply_udev_properties(Link *link, EventMode mode) {
         LinkConfig *config;
         sd_device *device;
 
@@ -992,7 +1012,7 @@ static int link_apply_udev_properties(Link *link, bool test) {
 
         /* 1. apply ImportProperty=. */
         STRV_FOREACH(p, config->import_properties)
-                (void) udev_builtin_import_property(device, link->device_db_clone, test, *p);
+                (void) udev_builtin_import_property(device, link->device_db_clone, mode, *p);
 
         /* 2. apply Property=. */
         STRV_FOREACH(p, config->properties) {
@@ -1007,15 +1027,15 @@ static int link_apply_udev_properties(Link *link, bool test) {
                 if (!key)
                         return log_oom();
 
-                (void) udev_builtin_add_property(device, test, key, eq + 1);
+                (void) udev_builtin_add_property(device, mode, key, eq + 1);
         }
 
         /* 3. apply UnsetProperty=. */
         STRV_FOREACH(p, config->unset_properties)
-                (void) udev_builtin_add_property(device, test, *p, NULL);
+                (void) udev_builtin_add_property(device, mode, *p, NULL);
 
         /* 4. set the default properties. */
-        (void) udev_builtin_add_property(device, test, "ID_NET_LINK_FILE", config->filename);
+        (void) udev_builtin_add_property(device, mode, "ID_NET_LINK_FILE", config->filename);
 
         _cleanup_free_ char *joined = NULL;
         STRV_FOREACH(d, config->dropins) {
@@ -1029,26 +1049,26 @@ static int link_apply_udev_properties(Link *link, bool test) {
                         return log_oom();
         }
 
-        (void) udev_builtin_add_property(device, test, "ID_NET_LINK_FILE_DROPINS", joined);
+        (void) udev_builtin_add_property(device, mode, "ID_NET_LINK_FILE_DROPINS", joined);
 
         if (link->new_name)
-                (void) udev_builtin_add_property(device, test, "ID_NET_NAME", link->new_name);
+                (void) udev_builtin_add_property(device, mode, "ID_NET_NAME", link->new_name);
 
         return 0;
 }
 
-int link_apply_config(LinkConfigContext *ctx, sd_netlink **rtnl, Link *link, bool test) {
+int link_apply_config(LinkConfigContext *ctx, sd_netlink **rtnl, Link *link, EventMode mode) {
         int r;
 
         assert(ctx);
         assert(rtnl);
         assert(link);
 
-        r = link_apply_ethtool_settings(link, &ctx->ethtool_fd);
+        r = link_apply_ethtool_settings(link, &ctx->ethtool_fd, mode);
         if (r < 0)
                 return r;
 
-        r = link_apply_rtnl_settings(link, rtnl);
+        r = link_apply_rtnl_settings(link, rtnl, mode);
         if (r < 0)
                 return r;
 
@@ -1060,19 +1080,15 @@ int link_apply_config(LinkConfigContext *ctx, sd_netlink **rtnl, Link *link, boo
         if (r < 0)
                 return r;
 
-        r = link_apply_sr_iov_config(link, rtnl);
+        r = link_apply_sr_iov_config(link, rtnl, mode);
         if (r < 0)
                 return r;
 
-        r = link_apply_udev_properties(link, test);
+        r = link_apply_rps_cpu_mask(link, mode);
         if (r < 0)
                 return r;
 
-        r = link_apply_rps_cpu_mask(link);
-        if (r < 0)
-                return r;
-
-        return 0;
+        return link_apply_udev_properties(link, mode);
 }
 
 int config_parse_udev_property(
index f6abff89e8b3bc8a9fc65ffc84b19d7c45f4936f..103343f5a5172119a5fbf38202aa2190c08b2db0 100644 (file)
@@ -12,6 +12,7 @@
 #include "list.h"
 #include "net-condition.h"
 #include "netif-naming-scheme.h"
+#include "udev-event.h"
 
 typedef struct LinkConfigContext LinkConfigContext;
 typedef struct LinkConfig LinkConfig;
@@ -106,7 +107,7 @@ 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, bool test);
+int link_apply_config(LinkConfigContext *ctx, sd_netlink **rtnl, Link *link, EventMode mode);
 
 const char *mac_address_policy_to_string(MACAddressPolicy p) _const_;
 MACAddressPolicy mac_address_policy_from_string(const char *p) _pure_;
index ee3e401b7a98cc42b4ba4943bf667faf3f49fca7..ebb8ae9008be9010f75b68a61e1426d1163ef041 100644 (file)
@@ -1,17 +1,7 @@
 /* SPDX-License-Identifier: GPL-2.0-or-later */
 #pragma once
 
-/*
- * scsi.h
- *
- * General scsi and linux scsi specific defines and structs.
- *
- * Copyright (C) IBM Corp. 2003
- *
- *        This program is free software; you can redistribute it and/or modify it
- *        under the terms of the GNU General Public License as published by the
- *        Free Software Foundation version 2 of the License.
- */
+/* Copyright (C) IBM Corp. 2003 */
 
 #include <scsi/scsi.h>
 
index f5aa38ac3e65d7d184d37cd2985a87880bb4857d..10da645d0df7a8623b8ce75214b07330c8cef3bc 100644 (file)
@@ -142,7 +142,7 @@ static int run(int argc, char *argv[]) {
         if (r < 0)
                 return log_debug_errno(r, "Failed to open device '%s'", devpath);
 
-        assert_se(event = udev_event_new(dev, NULL));
+        assert_se(event = udev_event_new(dev, NULL, EVENT_TEST_RULE_RUNNER));
 
         assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, SIGHUP, SIGCHLD) >= 0);
 
index 0102d8d707e0be171780b3db9c01798ca883c2c5..7cbccf32a600049745fbf895887f05e11c43b5d6 100644 (file)
@@ -17,7 +17,7 @@ static void test_event_spawn_core(bool with_pidfd, const char *cmd, char *result
         assert_se(setenv("SYSTEMD_PIDFD", yes_no(with_pidfd), 1) >= 0);
 
         assert_se(sd_device_new_from_syspath(&dev, "/sys/class/net/lo") >= 0);
-        assert_se(event = udev_event_new(dev, NULL));
+        assert_se(event = udev_event_new(dev, NULL, EVENT_TEST_SPAWN));
         assert_se(udev_event_spawn(event, false, cmd, result_buf, buf_size, NULL) == 0);
 
         assert_se(unsetenv("SYSTEMD_PIDFD") >= 0);
index 11419a3e6113b85635b9b7b7bb7018169dbaf7b0..aec36507812781bf0e416611c9285727591192ee 100644 (file)
 #include "strxcpyx.h"
 #include "udev-builtin.h"
 
-static void print_property(sd_device *dev, bool test, const char *name, const char *value) {
+static void print_property(sd_device *dev, EventMode mode, const char *name, const char *value) {
         char s[256];
 
         s[0] = '\0';
 
         if (streq(name, "TYPE")) {
-                udev_builtin_add_property(dev, test, "ID_FS_TYPE", value);
+                udev_builtin_add_property(dev, mode, "ID_FS_TYPE", value);
 
         } else if (streq(name, "USAGE")) {
-                udev_builtin_add_property(dev, test, "ID_FS_USAGE", value);
+                udev_builtin_add_property(dev, mode, "ID_FS_USAGE", value);
 
         } else if (streq(name, "VERSION")) {
-                udev_builtin_add_property(dev, test, "ID_FS_VERSION", value);
+                udev_builtin_add_property(dev, mode, "ID_FS_VERSION", value);
 
         } else if (streq(name, "UUID")) {
                 blkid_safe_string(value, s, sizeof(s));
-                udev_builtin_add_property(dev, test, "ID_FS_UUID", s);
+                udev_builtin_add_property(dev, mode, "ID_FS_UUID", s);
                 blkid_encode_string(value, s, sizeof(s));
-                udev_builtin_add_property(dev, test, "ID_FS_UUID_ENC", s);
+                udev_builtin_add_property(dev, mode, "ID_FS_UUID_ENC", s);
 
         } else if (streq(name, "UUID_SUB")) {
                 blkid_safe_string(value, s, sizeof(s));
-                udev_builtin_add_property(dev, test, "ID_FS_UUID_SUB", s);
+                udev_builtin_add_property(dev, mode, "ID_FS_UUID_SUB", s);
                 blkid_encode_string(value, s, sizeof(s));
-                udev_builtin_add_property(dev, test, "ID_FS_UUID_SUB_ENC", s);
+                udev_builtin_add_property(dev, mode, "ID_FS_UUID_SUB_ENC", s);
 
         } else if (streq(name, "LABEL")) {
                 blkid_safe_string(value, s, sizeof(s));
-                udev_builtin_add_property(dev, test, "ID_FS_LABEL", s);
+                udev_builtin_add_property(dev, mode, "ID_FS_LABEL", s);
                 blkid_encode_string(value, s, sizeof(s));
-                udev_builtin_add_property(dev, test, "ID_FS_LABEL_ENC", s);
+                udev_builtin_add_property(dev, mode, "ID_FS_LABEL_ENC", s);
 
         } else if (STR_IN_SET(name, "FSSIZE", "FSLASTBLOCK", "FSBLOCKSIZE")) {
                 strscpyl(s, sizeof(s), "ID_FS_", name + 2, NULL);
-                udev_builtin_add_property(dev, test, s, value);
+                udev_builtin_add_property(dev, mode, s, value);
 
         } else if (streq(name, "PTTYPE")) {
-                udev_builtin_add_property(dev, test, "ID_PART_TABLE_TYPE", value);
+                udev_builtin_add_property(dev, mode, "ID_PART_TABLE_TYPE", value);
 
         } else if (streq(name, "PTUUID")) {
-                udev_builtin_add_property(dev, test, "ID_PART_TABLE_UUID", value);
+                udev_builtin_add_property(dev, mode, "ID_PART_TABLE_UUID", value);
 
         } else if (streq(name, "PART_ENTRY_NAME")) {
                 blkid_encode_string(value, s, sizeof(s));
-                udev_builtin_add_property(dev, test, "ID_PART_ENTRY_NAME", s);
+                udev_builtin_add_property(dev, mode, "ID_PART_ENTRY_NAME", s);
 
         } else if (streq(name, "PART_ENTRY_TYPE")) {
                 blkid_encode_string(value, s, sizeof(s));
-                udev_builtin_add_property(dev, test, "ID_PART_ENTRY_TYPE", s);
+                udev_builtin_add_property(dev, mode, "ID_PART_ENTRY_TYPE", s);
 
         } else if (startswith(name, "PART_ENTRY_")) {
                 strscpyl(s, sizeof(s), "ID_", name, NULL);
-                udev_builtin_add_property(dev, test, s, value);
+                udev_builtin_add_property(dev, mode, s, value);
 
         } else if (streq(name, "SYSTEM_ID")) {
                 blkid_encode_string(value, s, sizeof(s));
-                udev_builtin_add_property(dev, test, "ID_FS_SYSTEM_ID", s);
+                udev_builtin_add_property(dev, mode, "ID_FS_SYSTEM_ID", s);
 
         } else if (streq(name, "PUBLISHER_ID")) {
                 blkid_encode_string(value, s, sizeof(s));
-                udev_builtin_add_property(dev, test, "ID_FS_PUBLISHER_ID", s);
+                udev_builtin_add_property(dev, mode, "ID_FS_PUBLISHER_ID", s);
 
         } else if (streq(name, "APPLICATION_ID")) {
                 blkid_encode_string(value, s, sizeof(s));
-                udev_builtin_add_property(dev, test, "ID_FS_APPLICATION_ID", s);
+                udev_builtin_add_property(dev, mode, "ID_FS_APPLICATION_ID", s);
 
         } else if (streq(name, "BOOT_SYSTEM_ID")) {
                 blkid_encode_string(value, s, sizeof(s));
-                udev_builtin_add_property(dev, test, "ID_FS_BOOT_SYSTEM_ID", s);
+                udev_builtin_add_property(dev, mode, "ID_FS_BOOT_SYSTEM_ID", s);
 
         } else if (streq(name, "VOLUME_ID")) {
                 blkid_encode_string(value, s, sizeof(s));
-                udev_builtin_add_property(dev, test, "ID_FS_VOLUME_ID", s);
+                udev_builtin_add_property(dev, mode, "ID_FS_VOLUME_ID", s);
 
         } else if (streq(name, "LOGICAL_VOLUME_ID")) {
                 blkid_encode_string(value, s, sizeof(s));
-                udev_builtin_add_property(dev, test, "ID_FS_LOGICAL_VOLUME_ID", s);
+                udev_builtin_add_property(dev, mode, "ID_FS_LOGICAL_VOLUME_ID", s);
 
         } else if (streq(name, "VOLUME_SET_ID")) {
                 blkid_encode_string(value, s, sizeof(s));
-                udev_builtin_add_property(dev, test, "ID_FS_VOLUME_SET_ID", s);
+                udev_builtin_add_property(dev, mode, "ID_FS_VOLUME_SET_ID", s);
 
         } else if (streq(name, "DATA_PREPARER_ID")) {
                 blkid_encode_string(value, s, sizeof(s));
-                udev_builtin_add_property(dev, test, "ID_FS_DATA_PREPARER_ID", s);
+                udev_builtin_add_property(dev, mode, "ID_FS_DATA_PREPARER_ID", s);
         }
 }
 
-static int find_gpt_root(sd_device *dev, blkid_probe pr, bool test) {
+static int find_gpt_root(sd_device *dev, blkid_probe pr, EventMode mode) {
 
 #if defined(SD_GPT_ROOT_NATIVE) && ENABLE_EFI
 
@@ -201,7 +201,7 @@ static int find_gpt_root(sd_device *dev, blkid_probe pr, bool test) {
         /* We found the ESP/XBOOTLDR on this disk, and also found a root partition, nice! Let's export its
          * UUID */
         if (found_esp_or_xbootldr && !sd_id128_is_null(root_id))
-                udev_builtin_add_property(dev, test, "ID_PART_GPT_AUTO_ROOT_UUID", SD_ID128_TO_UUID_STRING(root_id));
+                udev_builtin_add_property(dev, mode, "ID_PART_GPT_AUTO_ROOT_UUID", SD_ID128_TO_UUID_STRING(root_id));
 #endif
 
         return 0;
@@ -315,7 +315,7 @@ notloop:
         return 0;
 }
 
-static int builtin_blkid(UdevEvent *event, int argc, char *argv[], bool test) {
+static int builtin_blkid(UdevEvent *event, int argc, char *argv[]) {
         sd_device *dev = ASSERT_PTR(ASSERT_PTR(event)->dev);
         const char *devnode, *root_partition = NULL, *data, *name;
         _cleanup_(blkid_free_probep) blkid_probe pr = NULL;
@@ -422,7 +422,7 @@ static int builtin_blkid(UdevEvent *event, int argc, char *argv[], bool test) {
                 if (blkid_probe_get_value(pr, i, &name, &data, NULL) < 0)
                         continue;
 
-                print_property(dev, test, name, data);
+                print_property(dev, event->event_mode, name, data);
 
                 /* Is this a disk with GPT partition table? */
                 if (streq(name, "PTTYPE") && streq(data, "gpt"))
@@ -431,11 +431,11 @@ static int builtin_blkid(UdevEvent *event, int argc, char *argv[], bool test) {
                 /* Is this a partition that matches the root partition
                  * property inherited from the parent? */
                 if (root_partition && streq(name, "PART_ENTRY_UUID") && streq(data, root_partition))
-                        udev_builtin_add_property(dev, test, "ID_PART_GPT_AUTO_ROOT", "1");
+                        udev_builtin_add_property(dev, event->event_mode, "ID_PART_GPT_AUTO_ROOT", "1");
         }
 
         if (is_gpt)
-                find_gpt_root(dev, pr, test);
+                find_gpt_root(dev, pr, event->event_mode);
 
         r = read_loopback_backing_inode(
                         dev,
@@ -446,8 +446,8 @@ static int builtin_blkid(UdevEvent *event, int argc, char *argv[], bool test) {
         if (r < 0)
                 log_device_debug_errno(dev, r, "Failed to read loopback backing inode, ignoring: %m");
         else if (r > 0) {
-                udev_builtin_add_propertyf(dev, test, "ID_LOOP_BACKING_DEVICE", DEVNUM_FORMAT_STR, DEVNUM_FORMAT_VAL(backing_devno));
-                udev_builtin_add_propertyf(dev, test, "ID_LOOP_BACKING_INODE", "%" PRIu64, (uint64_t) backing_inode);
+                udev_builtin_add_propertyf(dev, event->event_mode, "ID_LOOP_BACKING_DEVICE", DEVNUM_FORMAT_STR, DEVNUM_FORMAT_VAL(backing_devno));
+                udev_builtin_add_propertyf(dev, event->event_mode, "ID_LOOP_BACKING_INODE", "%" PRIu64, (uint64_t) backing_inode);
 
                 if (backing_fname) {
                         /* In the worst case blkid_encode_string() will blow up to 4x the string
@@ -458,8 +458,8 @@ static int builtin_blkid(UdevEvent *event, int argc, char *argv[], bool test) {
                         assert(strlen(backing_fname) < ELEMENTSOF(encoded) / 4);
                         blkid_encode_string(backing_fname, encoded, ELEMENTSOF(encoded));
 
-                        udev_builtin_add_property(dev, test, "ID_LOOP_BACKING_FILENAME", backing_fname);
-                        udev_builtin_add_property(dev, test, "ID_LOOP_BACKING_FILENAME_ENC", encoded);
+                        udev_builtin_add_property(dev, event->event_mode, "ID_LOOP_BACKING_FILENAME", backing_fname);
+                        udev_builtin_add_property(dev, event->event_mode, "ID_LOOP_BACKING_FILENAME_ENC", encoded);
                 }
         }
 
index 9b12aebb3ad4bc46b5e93bee994fcb63b6045ff0..fe030d05b91ff7294d2b4ad9f84375926ea32660 100644 (file)
@@ -12,7 +12,7 @@
 #include "strxcpyx.h"
 #include "udev-builtin.h"
 
-static int builtin_btrfs(UdevEvent *event, int argc, char *argv[], bool test) {
+static int builtin_btrfs(UdevEvent *event, int argc, char *argv[]) {
         sd_device *dev = ASSERT_PTR(ASSERT_PTR(event)->dev);
         struct btrfs_ioctl_vol_args args = {};
         _cleanup_close_ int fd = -EBADF;
@@ -27,7 +27,7 @@ static int builtin_btrfs(UdevEvent *event, int argc, char *argv[], bool test) {
                         /* Driver not installed? Then we aren't ready. This is useful in initrds that lack
                          * btrfs.ko. After the host transition (where btrfs.ko will hopefully become
                          * available) the device can be retriggered and will then be considered ready. */
-                        udev_builtin_add_property(dev, test, "ID_BTRFS_READY", "0");
+                        udev_builtin_add_property(dev, event->event_mode, "ID_BTRFS_READY", "0");
                         return 0;
                 }
 
@@ -39,7 +39,7 @@ static int builtin_btrfs(UdevEvent *event, int argc, char *argv[], bool test) {
         if (r < 0)
                 return log_device_debug_errno(dev, errno, "Failed to call BTRFS_IOC_DEVICES_READY: %m");
 
-        udev_builtin_add_property(dev, test, "ID_BTRFS_READY", one_zero(r == 0));
+        udev_builtin_add_property(dev, event->event_mode, "ID_BTRFS_READY", one_zero(r == 0));
         return 0;
 }
 
index 4540c33d982e6ce7b69d80a14dbc2ccc3b4d3046..e33a7febd6fe43c6f9d2cc63679a1657f62054e7 100644 (file)
 
 static sd_hwdb *hwdb;
 
-int udev_builtin_hwdb_lookup(sd_device *dev,
-                             const char *prefix, const char *modalias,
-                             const char *filter, bool test) {
+int udev_builtin_hwdb_lookup(
+                sd_device *dev,
+                const char *prefix,
+                const char *modalias,
+                const char *filter,
+                EventMode mode) {
+
         _cleanup_free_ char *lookup = NULL;
         const char *key, *value;
         int n = 0, r;
@@ -38,7 +42,7 @@ int udev_builtin_hwdb_lookup(sd_device *dev,
                 if (filter && fnmatch(filter, key, FNM_NOESCAPE) != 0)
                         continue;
 
-                r = udev_builtin_add_property(dev, test, key, value);
+                r = udev_builtin_add_property(dev, mode, key, value);
                 if (r < 0)
                         return r;
                 n++;
@@ -64,9 +68,14 @@ static const char *modalias_usb(sd_device *dev, char *s, size_t size) {
         return s;
 }
 
-static int udev_builtin_hwdb_search(sd_device *dev, sd_device *srcdev,
-                                    const char *subsystem, const char *prefix,
-                                    const char *filter, bool test) {
+static int udev_builtin_hwdb_search(
+                sd_device *dev,
+                sd_device *srcdev,
+                const char *subsystem,
+                const char *prefix,
+                const char *filter,
+                EventMode mode) {
+
         char s[LINE_MAX];
         bool last = false;
         int r = 0;
@@ -99,7 +108,7 @@ static int udev_builtin_hwdb_search(sd_device *dev, sd_device *srcdev,
 
                 log_device_debug(dev, "hwdb modalias key: \"%s\"", modalias);
 
-                r = udev_builtin_hwdb_lookup(dev, prefix, modalias, filter, test);
+                r = udev_builtin_hwdb_lookup(dev, prefix, modalias, filter, mode);
                 if (r > 0)
                         break;
 
@@ -113,7 +122,7 @@ next:
         return r;
 }
 
-static int builtin_hwdb(UdevEvent *event, int argc, char *argv[], bool test) {
+static int builtin_hwdb(UdevEvent *event, int argc, char *argv[]) {
         static const struct option options[] = {
                 { "filter", required_argument, NULL, 'f' },
                 { "device", required_argument, NULL, 'd' },
@@ -160,7 +169,7 @@ static int builtin_hwdb(UdevEvent *event, int argc, char *argv[], bool test) {
 
         /* query a specific key given as argument */
         if (argv[optind]) {
-                r = udev_builtin_hwdb_lookup(dev, prefix, argv[optind], filter, test);
+                r = udev_builtin_hwdb_lookup(dev, prefix, argv[optind], filter, event->event_mode);
                 if (r < 0)
                         return log_device_debug_errno(dev, r, "Failed to look up hwdb: %m");
                 if (r == 0)
@@ -175,7 +184,7 @@ static int builtin_hwdb(UdevEvent *event, int argc, char *argv[], bool test) {
                         return log_device_debug_errno(dev, r, "Failed to create sd_device object '%s': %m", device);
         }
 
-        r = udev_builtin_hwdb_search(dev, srcdev, subsystem, prefix, filter, test);
+        r = udev_builtin_hwdb_search(dev, srcdev, subsystem, prefix, filter, event->event_mode);
         if (r < 0)
                 return log_device_debug_errno(dev, r, "Failed to look up hwdb: %m");
         if (r == 0)
index 295e8d215940e2926a6edac996175d84b6e94611..6f75d9df22c9a32dd8631f3d531b977ec03d4083 100644 (file)
@@ -44,7 +44,7 @@ static int abs_size_mm(const struct input_absinfo *absinfo) {
         return (absinfo->maximum - absinfo->minimum) / absinfo->resolution;
 }
 
-static void extract_info(sd_device *dev, bool test) {
+static void extract_info(sd_device *dev, EventMode mode) {
         char width[DECIMAL_STR_MAX(int)], height[DECIMAL_STR_MAX(int)];
         struct input_absinfo xabsinfo = {}, yabsinfo = {};
         _cleanup_close_ int fd = -EBADF;
@@ -63,8 +63,8 @@ static void extract_info(sd_device *dev, bool test) {
         xsprintf(width, "%d", abs_size_mm(&xabsinfo));
         xsprintf(height, "%d", abs_size_mm(&yabsinfo));
 
-        udev_builtin_add_property(dev, test, "ID_INPUT_WIDTH_MM", width);
-        udev_builtin_add_property(dev, test, "ID_INPUT_HEIGHT_MM", height);
+        udev_builtin_add_property(dev, mode, "ID_INPUT_WIDTH_MM", width);
+        udev_builtin_add_property(dev, mode, "ID_INPUT_HEIGHT_MM", height);
 }
 
 /*
@@ -73,9 +73,13 @@ static void extract_info(sd_device *dev, bool test) {
  * @param attr sysfs attribute name (e. g. "capabilities/key")
  * @param bitmask: Output array which has a sizeof of bitmask_size
  */
-static void get_cap_mask(sd_device *pdev, const char* attr,
-                         unsigned long *bitmask, size_t bitmask_size,
-                         bool test) {
+static void get_cap_mask(
+                sd_device *pdev,
+                const char* attr,
+                unsigned long *bitmask,
+                size_t bitmask_size,
+                EventMode mode) {
+
         const char *v;
         char text[4096];
         unsigned i;
@@ -110,7 +114,7 @@ static void get_cap_mask(sd_device *pdev, const char* attr,
         else
                 log_device_debug(pdev, "Ignoring %s block %lX which is larger than maximum size", attr, val);
 
-        if (test && DEBUG_LOGGING) {
+        if (mode == EVENT_UDEVADM_TEST_BUILTIN && DEBUG_LOGGING) {
                 log_device_debug(pdev, "%s decoded bit map:", attr);
 
                 val = bitmask_size / sizeof (unsigned long);
@@ -144,14 +148,16 @@ static struct input_id get_input_id(sd_device *dev) {
 }
 
 /* pointer devices */
-static bool test_pointers(sd_device *dev,
-                          const struct input_id *id,
-                          const unsigned long* bitmask_ev,
-                          const unsigned long* bitmask_abs,
-                          const unsigned long* bitmask_key,
-                          const unsigned long* bitmask_rel,
-                          const unsigned long* bitmask_props,
-                          bool test) {
+static bool test_pointers(
+                sd_device *dev,
+                const struct input_id *id,
+                const unsigned long* bitmask_ev,
+                const unsigned long* bitmask_abs,
+                const unsigned long* bitmask_key,
+                const unsigned long* bitmask_rel,
+                const unsigned long* bitmask_props,
+                EventMode mode) {
+
         bool has_abs_coordinates = false;
         bool has_rel_coordinates = false;
         bool has_mt_coordinates = false;
@@ -186,7 +192,7 @@ static bool test_pointers(sd_device *dev,
                 is_accelerometer = true;
 
         if (is_accelerometer) {
-                udev_builtin_add_property(dev, test, "ID_INPUT_ACCELEROMETER", "1");
+                udev_builtin_add_property(dev, mode, "ID_INPUT_ACCELEROMETER", "1");
                 return true;
         }
 
@@ -309,28 +315,29 @@ static bool test_pointers(sd_device *dev,
         }
 
         if (is_pointing_stick)
-                udev_builtin_add_property(dev, test, "ID_INPUT_POINTINGSTICK", "1");
+                udev_builtin_add_property(dev, mode, "ID_INPUT_POINTINGSTICK", "1");
         if (is_mouse || is_abs_mouse)
-                udev_builtin_add_property(dev, test, "ID_INPUT_MOUSE", "1");
+                udev_builtin_add_property(dev, mode, "ID_INPUT_MOUSE", "1");
         if (is_touchpad)
-                udev_builtin_add_property(dev, test, "ID_INPUT_TOUCHPAD", "1");
+                udev_builtin_add_property(dev, mode, "ID_INPUT_TOUCHPAD", "1");
         if (is_touchscreen)
-                udev_builtin_add_property(dev, test, "ID_INPUT_TOUCHSCREEN", "1");
+                udev_builtin_add_property(dev, mode, "ID_INPUT_TOUCHSCREEN", "1");
         if (is_joystick)
-                udev_builtin_add_property(dev, test, "ID_INPUT_JOYSTICK", "1");
+                udev_builtin_add_property(dev, mode, "ID_INPUT_JOYSTICK", "1");
         if (is_tablet)
-                udev_builtin_add_property(dev, test, "ID_INPUT_TABLET", "1");
+                udev_builtin_add_property(dev, mode, "ID_INPUT_TABLET", "1");
         if (is_tablet_pad)
-                udev_builtin_add_property(dev, test, "ID_INPUT_TABLET_PAD", "1");
+                udev_builtin_add_property(dev, mode, "ID_INPUT_TABLET_PAD", "1");
 
         return is_tablet || is_mouse || is_abs_mouse || is_touchpad || is_touchscreen || is_joystick || is_pointing_stick;
 }
 
 /* key like devices */
-static bool test_key(sd_device *dev,
-                     const unsigned long* bitmask_ev,
-                     const unsigned long* bitmask_key,
-                     bool test) {
+static bool test_key(
+                sd_device *dev,
+                const unsigned long* bitmask_ev,
+                const unsigned long* bitmask_key,
+                EventMode mode) {
 
         bool found = false;
 
@@ -357,19 +364,19 @@ static bool test_key(sd_device *dev,
                         }
 
         if (found)
-                udev_builtin_add_property(dev, test, "ID_INPUT_KEY", "1");
+                udev_builtin_add_property(dev, mode, "ID_INPUT_KEY", "1");
 
         /* the first 32 bits are ESC, numbers, and Q to D; if we have all of
          * those, consider it a full keyboard; do not test KEY_RESERVED, though */
         if (FLAGS_SET(bitmask_key[0], 0xFFFFFFFE)) {
-                udev_builtin_add_property(dev, test, "ID_INPUT_KEYBOARD", "1");
+                udev_builtin_add_property(dev, mode, "ID_INPUT_KEYBOARD", "1");
                 return true;
         }
 
         return found;
 }
 
-static int builtin_input_id(UdevEvent *event, int argc, char *argv[], bool test) {
+static int builtin_input_id(UdevEvent *event, int argc, char *argv[]) {
         sd_device *pdev, *dev = ASSERT_PTR(ASSERT_PTR(event)->dev);
         unsigned long bitmask_ev[NBITS(EV_MAX)];
         unsigned long bitmask_abs[NBITS(ABS_MAX)];
@@ -400,28 +407,28 @@ static int builtin_input_id(UdevEvent *event, int argc, char *argv[], bool test)
 
                 /* Use this as a flag that input devices were detected, so that this
                  * program doesn't need to be called more than once per device */
-                udev_builtin_add_property(dev, test, "ID_INPUT", "1");
-                get_cap_mask(pdev, "capabilities/ev", bitmask_ev, sizeof(bitmask_ev), test);
-                get_cap_mask(pdev, "capabilities/abs", bitmask_abs, sizeof(bitmask_abs), test);
-                get_cap_mask(pdev, "capabilities/rel", bitmask_rel, sizeof(bitmask_rel), test);
-                get_cap_mask(pdev, "capabilities/key", bitmask_key, sizeof(bitmask_key), test);
-                get_cap_mask(pdev, "properties", bitmask_props, sizeof(bitmask_props), test);
+                udev_builtin_add_property(dev, event->event_mode, "ID_INPUT", "1");
+                get_cap_mask(pdev, "capabilities/ev", bitmask_ev, sizeof(bitmask_ev), event->event_mode);
+                get_cap_mask(pdev, "capabilities/abs", bitmask_abs, sizeof(bitmask_abs), event->event_mode);
+                get_cap_mask(pdev, "capabilities/rel", bitmask_rel, sizeof(bitmask_rel), event->event_mode);
+                get_cap_mask(pdev, "capabilities/key", bitmask_key, sizeof(bitmask_key), event->event_mode);
+                get_cap_mask(pdev, "properties", bitmask_props, sizeof(bitmask_props), event->event_mode);
                 is_pointer = test_pointers(dev, &id, bitmask_ev, bitmask_abs,
                                            bitmask_key, bitmask_rel,
-                                           bitmask_props, test);
-                is_key = test_key(dev, bitmask_ev, bitmask_key, test);
+                                           bitmask_props, event->event_mode);
+                is_key = test_key(dev, bitmask_ev, bitmask_key, event->event_mode);
                 /* Some evdev nodes have only a scrollwheel */
                 if (!is_pointer && !is_key && test_bit(EV_REL, bitmask_ev) &&
                     (test_bit(REL_WHEEL, bitmask_rel) || test_bit(REL_HWHEEL, bitmask_rel)))
-                        udev_builtin_add_property(dev, test, "ID_INPUT_KEY", "1");
+                        udev_builtin_add_property(dev, event->event_mode, "ID_INPUT_KEY", "1");
                 if (test_bit(EV_SW, bitmask_ev))
-                        udev_builtin_add_property(dev, test, "ID_INPUT_SWITCH", "1");
+                        udev_builtin_add_property(dev, event->event_mode, "ID_INPUT_SWITCH", "1");
 
         }
 
         if (sd_device_get_sysname(dev, &sysname) >= 0 &&
             startswith(sysname, "event"))
-                extract_info(dev, test);
+                extract_info(dev, event->event_mode);
 
         return 0;
 }
index 3903bc463f3661c29a718f516b41d18793f91bb3..e1e6de0426921ef2c7cbf159078546bfd25ca2f2 100644 (file)
@@ -159,7 +159,7 @@ static int set_trackpoint_sensitivity(sd_device *dev, const char *value) {
         return 0;
 }
 
-static int builtin_keyboard(UdevEvent *event, int argc, char *argv[], bool test) {
+static int builtin_keyboard(UdevEvent *event, int argc, char *argv[]) {
         sd_device *dev = ASSERT_PTR(ASSERT_PTR(event)->dev);
         unsigned release[1024];
         unsigned release_count = 0;
@@ -167,6 +167,11 @@ static int builtin_keyboard(UdevEvent *event, int argc, char *argv[], bool test)
         const char *node;
         int has_abs = -1, r;
 
+        if (event->event_mode != EVENT_UDEV_WORKER) {
+                log_device_debug(dev, "Running in test mode, skipping execution of 'keyboard' builtin command.");
+                return 0;
+        }
+
         r = sd_device_get_devname(dev, &node);
         if (r < 0)
                 return log_device_error_errno(dev, r, "Failed to get device name: %m");
index 3ab5c485f80d79fc3c6b58c7b980998ced9e3ad7..f4aa4802c95c78e4bcbb0aac860f99ef0676ec96 100644 (file)
 
 static struct kmod_ctx *ctx = NULL;
 
-_printf_(6,0) static void udev_kmod_log(void *data, int priority, const char *file, int line, const char *fn, const char *format, va_list args) {
-        log_internalv(priority, 0, file, line, fn, format, args);
-}
-
-static int builtin_kmod(UdevEvent *event, int argc, char *argv[], bool test) {
+static int builtin_kmod(UdevEvent *event, int argc, char *argv[]) {
         sd_device *dev = ASSERT_PTR(ASSERT_PTR(event)->dev);
         int r;
 
+        if (event->event_mode != EVENT_UDEV_WORKER) {
+                log_device_debug(dev, "Running in test mode, skipping execution of 'kmod' builtin command.");
+                return 0;
+        }
+
         if (!ctx)
                 return 0;
 
@@ -39,7 +40,7 @@ static int builtin_kmod(UdevEvent *event, int argc, char *argv[], bool test) {
 
                 r = sd_device_get_property_value(dev, "MODALIAS", &modalias);
                 if (r < 0)
-                        return log_device_warning_errno(dev, r, "Failed to read property \"MODALIAS\".");
+                        return log_device_warning_errno(dev, r, "Failed to read property \"MODALIAS\": %m");
 
                 (void) module_load_and_warn(ctx, modalias, /* verbose = */ false);
         } else
@@ -51,23 +52,28 @@ static int builtin_kmod(UdevEvent *event, int argc, char *argv[], bool test) {
 
 /* called at udev startup and reload */
 static int builtin_kmod_init(void) {
+        int r;
+
         if (ctx)
                 return 0;
 
-        ctx = kmod_new(NULL, NULL);
-        if (!ctx)
-                return -ENOMEM;
-
         log_debug("Loading kernel module index.");
-        kmod_set_log_fn(ctx, udev_kmod_log, NULL);
-        kmod_load_resources(ctx);
+
+        r = module_setup_context(&ctx);
+        if (r < 0)
+                return log_error_errno(r, "Failed to initialize libkmod context: %m");
+
         return 0;
 }
 
 /* called on udev shutdown and reload request */
 static void builtin_kmod_exit(void) {
         log_debug("Unload kernel module index.");
-        ctx = kmod_unref(ctx);
+
+        if (!ctx)
+                return;
+
+        ctx = sym_kmod_unref(ctx);
 }
 
 /* called every couple of seconds during event activity; 'true' if config has changed */
@@ -75,7 +81,7 @@ static bool builtin_kmod_should_reload(void) {
         if (!ctx)
                 return false;
 
-        if (kmod_validate_resources(ctx) != KMOD_RESOURCES_OK) {
+        if (sym_kmod_validate_resources(ctx) != KMOD_RESOURCES_OK) {
                 log_debug("Kernel module index needs reloading.");
                 return true;
         }
index f1642a491dccb049d32e12d4f38d2ee890fe534a..90a9e8d22f76154c86fd915f96b1d1815132059b 100644 (file)
@@ -9,7 +9,7 @@
 #include "string-util.h"
 #include "udev-builtin.h"
 
-static int builtin_net_driver_set_driver(UdevEvent *event, int argc, char **argv, bool test) {
+static int builtin_net_driver_set_driver(UdevEvent *event, int argc, char **argv) {
         sd_device *dev = ASSERT_PTR(ASSERT_PTR(event)->dev);
         _cleanup_close_ int ethtool_fd = -EBADF;
         _cleanup_free_ char *driver = NULL;
@@ -32,7 +32,7 @@ static int builtin_net_driver_set_driver(UdevEvent *event, int argc, char **argv
         if (r < 0)
                 return log_device_warning_errno(dev, r, "Failed to get driver for '%s': %m", sysname);
 
-        return udev_builtin_add_property(event->dev, test, "ID_NET_DRIVER", driver);
+        return udev_builtin_add_property(event->dev, event->event_mode, "ID_NET_DRIVER", driver);
 }
 
 const UdevBuiltin udev_builtin_net_driver = {
index 8ef3abada958e54e97a4b1a3f8659908b600ee6b..ac4541edd7177572f4d11573c69cedeb49edc363 100644 (file)
  * When the code here is changed, man/systemd.net-naming-scheme.xml must be updated too.
  */
 
+/* Make sure the net/if.h header is included before any linux/ one */
+#include <net/if.h>
 #include <errno.h>
 #include <fcntl.h>
-#include <net/if.h>
 #include <stdarg.h>
 #include <unistd.h>
 #include <linux/if.h>
@@ -298,7 +299,7 @@ static int pci_get_onboard_index(sd_device *dev, unsigned *ret) {
         return 0;
 }
 
-static int names_pci_onboard(sd_device *dev, sd_device *pci_dev, const char *prefix, const char *suffix, bool test) {
+static int names_pci_onboard(sd_device *dev, sd_device *pci_dev, const char *prefix, const char *suffix, EventMode mode) {
         _cleanup_free_ char *port = NULL;
         unsigned idx = 0;  /* avoid false maybe-uninitialized warning */
         int r;
@@ -318,7 +319,7 @@ static int names_pci_onboard(sd_device *dev, sd_device *pci_dev, const char *pre
 
         char str[ALTIFNAMSIZ];
         if (snprintf_ok(str, sizeof str, "%so%u%s%s", prefix, idx, strempty(port), strempty(suffix)))
-                udev_builtin_add_property(dev, test, "ID_NET_NAME_ONBOARD", str);
+                udev_builtin_add_property(dev, mode, "ID_NET_NAME_ONBOARD", str);
 
         log_device_debug(dev, "Onboard index identifier: index=%u port=%s %s %s",
                          idx, strna(port),
@@ -327,7 +328,7 @@ static int names_pci_onboard(sd_device *dev, sd_device *pci_dev, const char *pre
         return 0;
 }
 
-static int names_pci_onboard_label(sd_device *dev, sd_device *pci_dev, const char *prefix, bool test) {
+static int names_pci_onboard_label(sd_device *dev, sd_device *pci_dev, const char *prefix, EventMode mode) {
         const char *label;
         int r;
 
@@ -343,7 +344,7 @@ static int names_pci_onboard_label(sd_device *dev, sd_device *pci_dev, const cha
         if (snprintf_ok(str, sizeof str, "%s%s",
                         naming_scheme_has(NAMING_LABEL_NOPREFIX) ? "" : prefix,
                         label))
-                udev_builtin_add_property(dev, test, "ID_NET_LABEL_ONBOARD", str);
+                udev_builtin_add_property(dev, mode, "ID_NET_LABEL_ONBOARD", str);
 
         log_device_debug(dev, "Onboard label from PCI device: %s", label);
         return 0;
@@ -613,7 +614,7 @@ static int get_pci_slot_specifiers(
         return 0;
 }
 
-static int names_pci_slot(sd_device *dev, sd_device *pci_dev, const char *prefix, const char *suffix, bool test) {
+static int names_pci_slot(sd_device *dev, sd_device *pci_dev, const char *prefix, const char *suffix, EventMode mode) {
         _cleanup_free_ char *domain = NULL, *bus_and_slot = NULL, *func = NULL, *port = NULL;
         uint32_t hotplug_slot = 0;  /* avoid false maybe-uninitialized warning */
         char str[ALTIFNAMSIZ];
@@ -634,7 +635,7 @@ static int names_pci_slot(sd_device *dev, sd_device *pci_dev, const char *prefix
         /* compose a name based on the raw kernel's PCI bus, slot numbers */
         if (snprintf_ok(str, sizeof str, "%s%s%s%s%s%s",
                         prefix, strempty(domain), bus_and_slot, strempty(func), strempty(port), strempty(suffix)))
-                udev_builtin_add_property(dev, test, "ID_NET_NAME_PATH", str);
+                udev_builtin_add_property(dev, mode, "ID_NET_NAME_PATH", str);
 
         log_device_debug(dev, "PCI path identifier: domain=%s bus_and_slot=%s func=%s port=%s %s %s",
                          strna(domain), bus_and_slot, strna(func), strna(port),
@@ -650,7 +651,7 @@ static int names_pci_slot(sd_device *dev, sd_device *pci_dev, const char *prefix
 
         if (snprintf_ok(str, sizeof str, "%s%ss%"PRIu32"%s%s%s",
                         prefix, strempty(domain), hotplug_slot, strempty(func), strempty(port), strempty(suffix)))
-                udev_builtin_add_property(dev, test, "ID_NET_NAME_SLOT", str);
+                udev_builtin_add_property(dev, mode, "ID_NET_NAME_SLOT", str);
 
         log_device_debug(dev, "Slot identifier: domain=%s slot=%"PRIu32" func=%s port=%s %s %s",
                          strna(domain), hotplug_slot, strna(func), strna(port),
@@ -659,7 +660,7 @@ static int names_pci_slot(sd_device *dev, sd_device *pci_dev, const char *prefix
         return 0;
 }
 
-static int names_vio(sd_device *dev, const char *prefix, bool test) {
+static int names_vio(sd_device *dev, const char *prefix, EventMode mode) {
         _cleanup_free_ char *s = NULL;
         unsigned slotid;
         int r;
@@ -698,13 +699,13 @@ static int names_vio(sd_device *dev, const char *prefix, bool test) {
 
         char str[ALTIFNAMSIZ];
         if (snprintf_ok(str, sizeof str, "%sv%u", prefix, slotid))
-                udev_builtin_add_property(dev, test, "ID_NET_NAME_SLOT", str);
+                udev_builtin_add_property(dev, mode, "ID_NET_NAME_SLOT", str);
         log_device_debug(dev, "Vio slot identifier: slotid=%u %s %s",
                          slotid, special_glyph(SPECIAL_GLYPH_ARROW_RIGHT), str + strlen(prefix));
         return 0;
 }
 
-static int names_platform(sd_device *dev, const char *prefix, bool test) {
+static int names_platform(sd_device *dev, const char *prefix, EventMode mode) {
         _cleanup_free_ char *p = NULL;
         const char *validchars;
         char *vendor, *model_str, *instance_str;
@@ -760,13 +761,13 @@ static int names_platform(sd_device *dev, const char *prefix, bool test) {
 
         char str[ALTIFNAMSIZ];
         if (snprintf_ok(str, sizeof str, "%sa%s%xi%u", prefix, vendor, model, instance))
-                udev_builtin_add_property(dev, test, "ID_NET_NAME_PATH", str);
+                udev_builtin_add_property(dev, mode, "ID_NET_NAME_PATH", str);
         log_device_debug(dev, "Platform identifier: vendor=%s model=%x instance=%u %s %s",
                          vendor, model, instance, special_glyph(SPECIAL_GLYPH_ARROW_RIGHT), str + strlen(prefix));
         return 0;
 }
 
-static int names_devicetree(sd_device *dev, const char *prefix, bool test) {
+static int names_devicetree(sd_device *dev, const char *prefix, EventMode mode) {
         _cleanup_(sd_device_unrefp) sd_device *aliases_dev = NULL, *ofnode_dev = NULL, *devicetree_dev = NULL;
         const char *ofnode_path, *ofnode_syspath, *devicetree_syspath;
         sd_device *parent;
@@ -858,7 +859,7 @@ static int names_devicetree(sd_device *dev, const char *prefix, bool test) {
 
                 char str[ALTIFNAMSIZ];
                 if (snprintf_ok(str, sizeof str, "%sd%u", prefix, i))
-                        udev_builtin_add_property(dev, test, "ID_NET_NAME_ONBOARD", str);
+                        udev_builtin_add_property(dev, mode, "ID_NET_NAME_ONBOARD", str);
                 log_device_debug(dev, "devicetree identifier: alias_index=%u %s \"%s\"",
                                  i, special_glyph(SPECIAL_GLYPH_ARROW_RIGHT), str + strlen(prefix));
                 return 0;
@@ -867,7 +868,7 @@ static int names_devicetree(sd_device *dev, const char *prefix, bool test) {
         return -ENOENT;
 }
 
-static int names_pci(sd_device *dev, const char *prefix, bool test) {
+static int names_pci(sd_device *dev, const char *prefix, EventMode mode) {
         _cleanup_(sd_device_unrefp) sd_device *physfn_pcidev = NULL;
         _cleanup_free_ char *virtfn_suffix = NULL;
         sd_device *parent;
@@ -884,10 +885,10 @@ static int names_pci(sd_device *dev, const char *prefix, bool test) {
             get_virtfn_info(parent, &physfn_pcidev, &virtfn_suffix) >= 0)
                 parent = physfn_pcidev;
         else
-                (void) names_pci_onboard_label(dev, parent, prefix, test);
+                (void) names_pci_onboard_label(dev, parent, prefix, mode);
 
-        (void) names_pci_onboard(dev, parent, prefix, virtfn_suffix, test);
-        (void) names_pci_slot(dev, parent, prefix, virtfn_suffix, test);
+        (void) names_pci_onboard(dev, parent, prefix, virtfn_suffix, mode);
+        (void) names_pci_slot(dev, parent, prefix, virtfn_suffix, mode);
         return 0;
 }
 
@@ -950,7 +951,7 @@ static int get_usb_specifier(sd_device *dev, char **ret) {
         return 0;
 }
 
-static int names_usb(sd_device *dev, const char *prefix, bool test) {
+static int names_usb(sd_device *dev, const char *prefix, EventMode mode) {
         _cleanup_free_ char *suffix = NULL;
         sd_device *usbdev, *pcidev;
         int r;
@@ -971,7 +972,7 @@ static int names_usb(sd_device *dev, const char *prefix, bool test) {
         /* If the USB bus is on PCI bus, then suffix the USB specifier to the name based on the PCI bus. */
         r = sd_device_get_parent_with_subsystem_devtype(usbdev, "pci", NULL, &pcidev);
         if (r >= 0)
-                return names_pci_slot(dev, pcidev, prefix, suffix, test);
+                return names_pci_slot(dev, pcidev, prefix, suffix, mode);
 
         if (r != -ENOENT || !naming_scheme_has(NAMING_USB_HOST))
                 return log_device_debug_errno(usbdev, r, "Failed to get parent PCI bus: %m");
@@ -979,7 +980,7 @@ static int names_usb(sd_device *dev, const char *prefix, bool test) {
         /* Otherwise, e.g. on-chip asics that have USB ports, use the USB specifier as is. */
         char str[ALTIFNAMSIZ];
         if (snprintf_ok(str, sizeof str, "%s%s", prefix, suffix))
-                udev_builtin_add_property(dev, test, "ID_NET_NAME_PATH", str);
+                udev_builtin_add_property(dev, mode, "ID_NET_NAME_PATH", str);
 
         return 0;
 }
@@ -1014,7 +1015,7 @@ static int get_bcma_specifier(sd_device *dev, char **ret) {
         return 0;
 }
 
-static int names_bcma(sd_device *dev, const char *prefix, bool test) {
+static int names_bcma(sd_device *dev, const char *prefix, EventMode mode) {
         _cleanup_free_ char *suffix = NULL;
         sd_device *bcmadev, *pcidev;
         int r;
@@ -1034,10 +1035,10 @@ static int names_bcma(sd_device *dev, const char *prefix, bool test) {
         if (r < 0)
                 return r;
 
-        return names_pci_slot(dev, pcidev, prefix, suffix, test);
+        return names_pci_slot(dev, pcidev, prefix, suffix, mode);
 }
 
-static int names_ccw(sd_device *dev, const char *prefix, bool test) {
+static int names_ccw(sd_device *dev, const char *prefix, EventMode mode) {
         sd_device *cdev;
         const char *bus_id;
         size_t bus_id_start, bus_id_len;
@@ -1079,14 +1080,14 @@ static int names_ccw(sd_device *dev, const char *prefix, bool test) {
         /* Use the CCW bus-ID as network device name */
         char str[ALTIFNAMSIZ];
         if (snprintf_ok(str, sizeof str, "%sc%s", prefix, bus_id))
-                udev_builtin_add_property(dev, test, "ID_NET_NAME_PATH", str);
+                udev_builtin_add_property(dev, mode, "ID_NET_NAME_PATH", str);
         log_device_debug(dev, "CCW identifier: ccw_busid=%s %s \"%s\"",
                          bus_id, special_glyph(SPECIAL_GLYPH_ARROW_RIGHT), str + strlen(prefix));
         return 0;
 }
 
 /* IEEE Organizationally Unique Identifier vendor string */
-static int ieee_oui(sd_device *dev, const struct hw_addr_data *hw_addr, bool test) {
+static int ieee_oui(sd_device *dev, const struct hw_addr_data *hw_addr, EventMode mode) {
         char str[32];
 
         assert(dev);
@@ -1109,10 +1110,10 @@ static int ieee_oui(sd_device *dev, const struct hw_addr_data *hw_addr, bool tes
                  hw_addr->bytes[4],
                  hw_addr->bytes[5]);
 
-        return udev_builtin_hwdb_lookup(dev, NULL, str, NULL, test);
+        return udev_builtin_hwdb_lookup(dev, NULL, str, NULL, mode);
 }
 
-static int names_mac(sd_device *dev, const char *prefix, bool test) {
+static int names_mac(sd_device *dev, const char *prefix, EventMode mode) {
         unsigned iftype, assign_type;
         struct hw_addr_data hw_addr;
         const char *s;
@@ -1156,16 +1157,16 @@ static int names_mac(sd_device *dev, const char *prefix, bool test) {
 
         char str[ALTIFNAMSIZ];
         xsprintf(str, "%sx%s", prefix, HW_ADDR_TO_STR_FULL(&hw_addr, HW_ADDR_TO_STRING_NO_COLON));
-        udev_builtin_add_property(dev, test, "ID_NET_NAME_MAC", str);
+        udev_builtin_add_property(dev, mode, "ID_NET_NAME_MAC", str);
         log_device_debug(dev, "MAC address identifier: hw_addr=%s %s %s",
                          HW_ADDR_TO_STR(&hw_addr),
                          special_glyph(SPECIAL_GLYPH_ARROW_RIGHT), str + strlen(prefix));
 
-        (void) ieee_oui(dev, &hw_addr, test);
+        (void) ieee_oui(dev, &hw_addr, mode);
         return 0;
 }
 
-static int names_netdevsim(sd_device *dev, const char *prefix, bool test) {
+static int names_netdevsim(sd_device *dev, const char *prefix, EventMode mode) {
         sd_device *netdevsimdev;
         const char *sysnum, *phys_port_name;
         unsigned addr;
@@ -1200,13 +1201,13 @@ static int names_netdevsim(sd_device *dev, const char *prefix, bool test) {
 
         char str[ALTIFNAMSIZ];
         if (snprintf_ok(str, sizeof str, "%si%un%s", prefix, addr, phys_port_name))
-                udev_builtin_add_property(dev, test, "ID_NET_NAME_PATH", str);
+                udev_builtin_add_property(dev, mode, "ID_NET_NAME_PATH", str);
         log_device_debug(dev, "Netdevsim identifier: address=%u, port_name=%s %s %s",
                          addr, phys_port_name, special_glyph(SPECIAL_GLYPH_ARROW_RIGHT), str + strlen(prefix));
         return 0;
 }
 
-static int names_xen(sd_device *dev, const char *prefix, bool test) {
+static int names_xen(sd_device *dev, const char *prefix, EventMode mode) {
         _cleanup_free_ char *vif = NULL;
         const char *p;
         unsigned id;
@@ -1240,7 +1241,7 @@ static int names_xen(sd_device *dev, const char *prefix, bool test) {
 
         char str[ALTIFNAMSIZ];
         if (snprintf_ok(str, sizeof str, "%sX%u", prefix, id))
-                udev_builtin_add_property(dev, test, "ID_NET_NAME_SLOT", str);
+                udev_builtin_add_property(dev, mode, "ID_NET_NAME_SLOT", str);
         log_device_debug(dev, "Xen identifier: id=%u %s %s",
                          id, special_glyph(SPECIAL_GLYPH_ARROW_RIGHT), str + strlen(prefix));
         return 0;
@@ -1300,7 +1301,7 @@ static int device_is_stacked(sd_device *dev) {
         return ifindex != iflink;
 }
 
-static int builtin_net_id(UdevEvent *event, int argc, char *argv[], bool test) {
+static int builtin_net_id(UdevEvent *event, int argc, char *argv[]) {
         sd_device *dev = ASSERT_PTR(ASSERT_PTR(event)->dev);
         const char *prefix;
         int r;
@@ -1318,18 +1319,18 @@ static int builtin_net_id(UdevEvent *event, int argc, char *argv[], bool test) {
                 return 0;
         }
 
-        udev_builtin_add_property(dev, test, "ID_NET_NAMING_SCHEME", naming_scheme()->name);
-
-        (void) names_mac(dev, prefix, test);
-        (void) names_devicetree(dev, prefix, test);
-        (void) names_ccw(dev, prefix, test);
-        (void) names_vio(dev, prefix, test);
-        (void) names_platform(dev, prefix, test);
-        (void) names_netdevsim(dev, prefix, test);
-        (void) names_xen(dev, prefix, test);
-        (void) names_pci(dev, prefix, test);
-        (void) names_usb(dev, prefix, test);
-        (void) names_bcma(dev, prefix, test);
+        udev_builtin_add_property(dev, event->event_mode, "ID_NET_NAMING_SCHEME", naming_scheme()->name);
+
+        (void) names_mac(dev, prefix, event->event_mode);
+        (void) names_devicetree(dev, prefix, event->event_mode);
+        (void) names_ccw(dev, prefix, event->event_mode);
+        (void) names_vio(dev, prefix, event->event_mode);
+        (void) names_platform(dev, prefix, event->event_mode);
+        (void) names_netdevsim(dev, prefix, event->event_mode);
+        (void) names_xen(dev, prefix, event->event_mode);
+        (void) names_pci(dev, prefix, event->event_mode);
+        (void) names_usb(dev, prefix, event->event_mode);
+        (void) names_bcma(dev, prefix, event->event_mode);
 
         return 0;
 }
index df89679de340c502f83fb8d541742f8b3162d009..8cfcaa932fb8886c089c3fb29d9ef36377388946 100644 (file)
@@ -12,7 +12,7 @@
 
 static LinkConfigContext *ctx = NULL;
 
-static int builtin_net_setup_link(UdevEvent *event, int argc, char **argv, bool test) {
+static int builtin_net_setup_link(UdevEvent *event, int argc, char **argv) {
         sd_device *dev = ASSERT_PTR(ASSERT_PTR(event)->dev);
         _cleanup_(link_freep) Link *link = NULL;
         int r;
@@ -30,13 +30,13 @@ static int builtin_net_setup_link(UdevEvent *event, int argc, char **argv, bool
                                  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");
+                (void) udev_builtin_import_property(dev, event->dev_db_clone, event->event_mode, "ID_NET_LINK_FILE");
+                (void) udev_builtin_import_property(dev, event->dev_db_clone, event->event_mode, "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);
+                        (void) udev_builtin_add_property(dev, event->event_mode, "ID_NET_NAME", value);
 
                 return 0;
         }
@@ -59,7 +59,7 @@ 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, test);
+        r = link_apply_config(ctx, &event->rtnl, link, event->event_mode);
         if (r == -ENODEV)
                 log_device_debug_errno(dev, r, "Link vanished while applying configuration, ignoring.");
         else if (r < 0)
index dc0630596b9eed98e9e98b965634eaa0e7601ba5..a23b32db3e9e4b0c9cac5c288eac9dd257e47fdb 100644 (file)
@@ -642,7 +642,7 @@ static int find_real_nvme_parent(sd_device *dev, sd_device **ret) {
         return 0;
 }
 
-static void add_id_with_usb_revision(sd_device *dev, bool test, char *path) {
+static void add_id_with_usb_revision(sd_device *dev, EventMode mode, char *path) {
         char *p;
 
         assert(dev);
@@ -659,13 +659,13 @@ static void add_id_with_usb_revision(sd_device *dev, bool test, char *path) {
         if (p[1] != '-')
                 return;
 
-        (void) udev_builtin_add_property(dev, test, "ID_PATH_WITH_USB_REVISION", path);
+        (void) udev_builtin_add_property(dev, mode, "ID_PATH_WITH_USB_REVISION", path);
 
         /* Drop the USB revision specifier for backward compatibility. */
         memmove(p - 1, p + 1, strlen(p + 1) + 1);
 }
 
-static void add_id_tag(sd_device *dev, bool test, const char *path) {
+static void add_id_tag(sd_device *dev, EventMode mode, const char *path) {
         char tag[UDEV_NAME_SIZE];
         size_t i = 0;
 
@@ -693,10 +693,10 @@ static void add_id_tag(sd_device *dev, bool test, const char *path) {
                 i--;
         tag[i] = '\0';
 
-        (void) udev_builtin_add_property(dev, test, "ID_PATH_TAG", tag);
+        (void) udev_builtin_add_property(dev, mode, "ID_PATH_TAG", tag);
 }
 
-static int builtin_path_id(UdevEvent *event, int argc, char *argv[], bool test) {
+static int builtin_path_id(UdevEvent *event, int argc, char *argv[]) {
         sd_device *dev = ASSERT_PTR(ASSERT_PTR(event)->dev);
         _cleanup_(sd_device_unrefp) sd_device *dev_other_branch = NULL;
         _cleanup_free_ char *path = NULL, *compat_path = NULL;
@@ -851,11 +851,11 @@ static int builtin_path_id(UdevEvent *event, int argc, char *argv[], bool test)
         if (device_in_subsystem(dev, "block") && !supported_transport)
                 return -ENOENT;
 
-        add_id_with_usb_revision(dev, test, path);
+        add_id_with_usb_revision(dev, event->event_mode, path);
 
-        (void) udev_builtin_add_property(dev, test, "ID_PATH", path);
+        (void) udev_builtin_add_property(dev, event->event_mode, "ID_PATH", path);
 
-        add_id_tag(dev, test, path);
+        add_id_tag(dev, event->event_mode, path);
 
         /*
          * Compatible link generation for ATA devices
@@ -863,7 +863,7 @@ static int builtin_path_id(UdevEvent *event, int argc, char *argv[], bool test)
          * ID_PATH_ATA_COMPAT
          */
         if (compat_path)
-                (void) udev_builtin_add_property(dev, test, "ID_PATH_ATA_COMPAT", compat_path);
+                (void) udev_builtin_add_property(dev, event->event_mode, "ID_PATH_ATA_COMPAT", compat_path);
 
         return 0;
 }
index da42ef59b035008f0ad37532da12b7d6aafb020c..805d048e8195b2f80a218f4569ccf93e7e28e60d 100644 (file)
 #include "log.h"
 #include "udev-builtin.h"
 
-static int builtin_uaccess(UdevEvent *event, int argc, char *argv[], bool test) {
+static int builtin_uaccess(UdevEvent *event, int argc, char *argv[]) {
         sd_device *dev = ASSERT_PTR(ASSERT_PTR(event)->dev);
         const char *path = NULL, *seat;
         bool changed_acl = false;
         uid_t uid;
         int r;
 
+        if (event->event_mode != EVENT_UDEV_WORKER) {
+                log_device_debug(dev, "Running in test mode, skipping execution of 'uaccess' builtin command.");
+                return 0;
+        }
+
         umask(0022);
 
         /* don't muck around with ACLs when the system is not running systemd */
index f5b8fe9e9adf4916010ed02fc82ff21bd000f740..2413f9ce9e035f1a6c2ae6d26a2fada4ad57673b 100644 (file)
@@ -224,7 +224,7 @@ static int dev_if_packed_info(sd_device *dev, char *ifs_str, size_t len) {
  * 6.) If the device supplies a serial number, this number
  *     is concatenated with the identification with an underscore '_'.
  */
-static int builtin_usb_id(UdevEvent *event, int argc, char *argv[], bool test) {
+static int builtin_usb_id(UdevEvent *event, int argc, char *argv[]) {
         sd_device *dev = ASSERT_PTR(ASSERT_PTR(event)->dev);
         char vendor_str[64] = "";
         char vendor_str_enc[256];
@@ -429,55 +429,55 @@ fallback:
         if (sd_device_get_property_value(dev, "ID_BUS", NULL) >= 0)
                 log_device_debug(dev, "ID_BUS property is already set, setting only properties prefixed with \"ID_USB_\".");
         else {
-                udev_builtin_add_property(dev, test, "ID_BUS", "usb");
+                udev_builtin_add_property(dev, event->event_mode, "ID_BUS", "usb");
 
-                udev_builtin_add_property(dev, test, "ID_MODEL", model_str);
-                udev_builtin_add_property(dev, test, "ID_MODEL_ENC", model_str_enc);
-                udev_builtin_add_property(dev, test, "ID_MODEL_ID", product_id);
+                udev_builtin_add_property(dev, event->event_mode, "ID_MODEL", model_str);
+                udev_builtin_add_property(dev, event->event_mode, "ID_MODEL_ENC", model_str_enc);
+                udev_builtin_add_property(dev, event->event_mode, "ID_MODEL_ID", product_id);
 
-                udev_builtin_add_property(dev, test, "ID_SERIAL", serial);
+                udev_builtin_add_property(dev, event->event_mode, "ID_SERIAL", serial);
                 if (!isempty(serial_str))
-                        udev_builtin_add_property(dev, test, "ID_SERIAL_SHORT", serial_str);
+                        udev_builtin_add_property(dev, event->event_mode, "ID_SERIAL_SHORT", serial_str);
 
-                udev_builtin_add_property(dev, test, "ID_VENDOR", vendor_str);
-                udev_builtin_add_property(dev, test, "ID_VENDOR_ENC", vendor_str_enc);
-                udev_builtin_add_property(dev, test, "ID_VENDOR_ID", vendor_id);
+                udev_builtin_add_property(dev, event->event_mode, "ID_VENDOR", vendor_str);
+                udev_builtin_add_property(dev, event->event_mode, "ID_VENDOR_ENC", vendor_str_enc);
+                udev_builtin_add_property(dev, event->event_mode, "ID_VENDOR_ID", vendor_id);
 
-                udev_builtin_add_property(dev, test, "ID_REVISION", revision_str);
+                udev_builtin_add_property(dev, event->event_mode, "ID_REVISION", revision_str);
 
                 if (!isempty(type_str))
-                        udev_builtin_add_property(dev, test, "ID_TYPE", type_str);
+                        udev_builtin_add_property(dev, event->event_mode, "ID_TYPE", type_str);
 
                 if (!isempty(instance_str))
-                        udev_builtin_add_property(dev, test, "ID_INSTANCE", instance_str);
+                        udev_builtin_add_property(dev, event->event_mode, "ID_INSTANCE", instance_str);
         }
 
         /* Also export the same values in the above by prefixing ID_USB_. */
-        udev_builtin_add_property(dev, test, "ID_USB_MODEL", model_str);
-        udev_builtin_add_property(dev, test, "ID_USB_MODEL_ENC", model_str_enc);
-        udev_builtin_add_property(dev, test, "ID_USB_MODEL_ID", product_id);
-        udev_builtin_add_property(dev, test, "ID_USB_SERIAL", serial);
+        udev_builtin_add_property(dev, event->event_mode, "ID_USB_MODEL", model_str);
+        udev_builtin_add_property(dev, event->event_mode, "ID_USB_MODEL_ENC", model_str_enc);
+        udev_builtin_add_property(dev, event->event_mode, "ID_USB_MODEL_ID", product_id);
+        udev_builtin_add_property(dev, event->event_mode, "ID_USB_SERIAL", serial);
         if (!isempty(serial_str))
-                udev_builtin_add_property(dev, test, "ID_USB_SERIAL_SHORT", serial_str);
+                udev_builtin_add_property(dev, event->event_mode, "ID_USB_SERIAL_SHORT", serial_str);
 
-        udev_builtin_add_property(dev, test, "ID_USB_VENDOR", vendor_str);
-        udev_builtin_add_property(dev, test, "ID_USB_VENDOR_ENC", vendor_str_enc);
-        udev_builtin_add_property(dev, test, "ID_USB_VENDOR_ID", vendor_id);
+        udev_builtin_add_property(dev, event->event_mode, "ID_USB_VENDOR", vendor_str);
+        udev_builtin_add_property(dev, event->event_mode, "ID_USB_VENDOR_ENC", vendor_str_enc);
+        udev_builtin_add_property(dev, event->event_mode, "ID_USB_VENDOR_ID", vendor_id);
 
-        udev_builtin_add_property(dev, test, "ID_USB_REVISION", revision_str);
+        udev_builtin_add_property(dev, event->event_mode, "ID_USB_REVISION", revision_str);
 
         if (!isempty(type_str))
-                udev_builtin_add_property(dev, test, "ID_USB_TYPE", type_str);
+                udev_builtin_add_property(dev, event->event_mode, "ID_USB_TYPE", type_str);
 
         if (!isempty(instance_str))
-                udev_builtin_add_property(dev, test, "ID_USB_INSTANCE", instance_str);
+                udev_builtin_add_property(dev, event->event_mode, "ID_USB_INSTANCE", instance_str);
 
         if (!isempty(packed_if_str))
-                udev_builtin_add_property(dev, test, "ID_USB_INTERFACES", packed_if_str);
+                udev_builtin_add_property(dev, event->event_mode, "ID_USB_INTERFACES", packed_if_str);
         if (ifnum)
-                udev_builtin_add_property(dev, test, "ID_USB_INTERFACE_NUM", ifnum);
+                udev_builtin_add_property(dev, event->event_mode, "ID_USB_INTERFACE_NUM", ifnum);
         if (driver)
-                udev_builtin_add_property(dev, test, "ID_USB_DRIVER", driver);
+                udev_builtin_add_property(dev, event->event_mode, "ID_USB_DRIVER", driver);
         return 0;
 }
 
index 6caea8eccee8cec4a60f80464d35ca686e901818..1a1cb3734d0e6062dd4896e77626a8a456d9c515 100644 (file)
@@ -99,7 +99,7 @@ UdevBuiltinCommand udev_builtin_lookup(const char *command) {
         return _UDEV_BUILTIN_INVALID;
 }
 
-int udev_builtin_run(UdevEvent *event, UdevBuiltinCommand cmd, const char *command, bool test) {
+int udev_builtin_run(UdevEvent *event, UdevBuiltinCommand cmd, const char *command) {
         _cleanup_strv_free_ char **argv = NULL;
         int r;
 
@@ -117,10 +117,10 @@ int udev_builtin_run(UdevEvent *event, UdevBuiltinCommand cmd, const char *comma
 
         /* we need '0' here to reset the internal state */
         optind = 0;
-        return builtins[cmd]->cmd(event, strv_length(argv), argv, test);
+        return builtins[cmd]->cmd(event, strv_length(argv), argv);
 }
 
-int udev_builtin_add_property(sd_device *dev, bool test, const char *key, const char *val) {
+int udev_builtin_add_property(sd_device *dev, EventMode mode, const char *key, const char *val) {
         int r;
 
         assert(dev);
@@ -131,13 +131,13 @@ int udev_builtin_add_property(sd_device *dev, bool test, const char *key, const
                 return log_device_debug_errno(dev, r, "Failed to add property '%s%s%s'",
                                               key, val ? "=" : "", strempty(val));
 
-        if (test)
+        if (mode == EVENT_UDEVADM_TEST_BUILTIN)
                 printf("%s=%s\n", key, strempty(val));
 
         return 0;
 }
 
-int udev_builtin_add_propertyf(sd_device *dev, bool test, const char *key, const char *valf, ...) {
+int udev_builtin_add_propertyf(sd_device *dev, EventMode mode, const char *key, const char *valf, ...) {
         _cleanup_free_ char *val = NULL;
         va_list ap;
         int r;
@@ -152,10 +152,10 @@ int udev_builtin_add_propertyf(sd_device *dev, bool test, const char *key, const
         if (r < 0)
                 return log_oom_debug();
 
-        return udev_builtin_add_property(dev, test, key, val);
+        return udev_builtin_add_property(dev, mode, key, val);
 }
 
-int udev_builtin_import_property(sd_device *dev, sd_device *src, bool test, const char *key) {
+int udev_builtin_import_property(sd_device *dev, sd_device *src, EventMode mode, const char *key) {
         const char *val;
         int r;
 
@@ -171,7 +171,7 @@ int udev_builtin_import_property(sd_device *dev, sd_device *src, bool test, cons
         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);
+        r = udev_builtin_add_property(dev, mode, key, val);
         if (r < 0)
                 return r;
 
index c7a48b0201cee858ad96230dfa198c9e08b71122..0d82bebf23a481b6af13a6bd526c7111619daf46 100644 (file)
@@ -34,7 +34,7 @@ typedef enum UdevBuiltinCommand {
 
 typedef struct UdevBuiltin {
         const char *name;
-        int (*cmd)(UdevEvent *event, int argc, char *argv[], bool test);
+        int (*cmd)(UdevEvent *event, int argc, char *argv[]);
         const char *help;
         int (*init)(void);
         void (*exit)(void);
@@ -79,11 +79,11 @@ void udev_builtin_exit(void);
 UdevBuiltinCommand udev_builtin_lookup(const char *command);
 const char *udev_builtin_name(UdevBuiltinCommand cmd);
 bool udev_builtin_run_once(UdevBuiltinCommand cmd);
-int udev_builtin_run(UdevEvent *event, UdevBuiltinCommand cmd, const char *command, bool test);
+int udev_builtin_run(UdevEvent *event, UdevBuiltinCommand cmd, const char *command);
 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_add_property(sd_device *dev, EventMode mode, const char *key, const char *val);
+int udev_builtin_add_propertyf(sd_device *dev, EventMode mode, const char *key, const char *valf, ...) _printf_(4, 5);
+int udev_builtin_import_property(sd_device *dev, sd_device *src, EventMode mode, const char *key);
 int udev_builtin_hwdb_lookup(sd_device *dev, const char *prefix, const char *modalias,
-                             const char *filter, bool test);
+                             const char *filter, EventMode mode);
index 4541b0e0fb1ebefb093cfeea5c03aa6b48f02ff5..607071a8cf6c42cb52c7ee5d2ddb4675ecca9660 100644 (file)
@@ -16,7 +16,7 @@
 #include "udev-util.h"
 #include "user-util.h"
 
-UdevEvent *udev_event_new(sd_device *dev, UdevWorker *worker) {
+UdevEvent *udev_event_new(sd_device *dev, UdevWorker *worker, EventMode mode) {
         int log_level = worker ? worker->log_level : log_get_max_level();
         UdevEvent *event;
 
@@ -36,6 +36,7 @@ UdevEvent *udev_event_new(sd_device *dev, UdevWorker *worker) {
                 .mode = MODE_INVALID,
                 .log_level_was_debug = log_level == LOG_DEBUG,
                 .default_log_level = log_level,
+                .event_mode = mode,
         };
 
         return event;
@@ -110,6 +111,9 @@ static int rename_netif(UdevEvent *event) {
 
         assert(event);
 
+        if (!EVENT_MODE_DESTRUCTIVE(event))
+                return 0;
+
         if (!event->name)
                 return 0; /* No new name is requested. */
 
@@ -222,6 +226,9 @@ static int assign_altnames(UdevEvent *event) {
         int ifindex, r;
         const char *s;
 
+        if (!EVENT_MODE_DESTRUCTIVE(event))
+                return 0;
+
         if (strv_isempty(event->altnames))
                 return 0;
 
@@ -250,6 +257,9 @@ static int update_devnode(UdevEvent *event) {
         sd_device *dev = ASSERT_PTR(ASSERT_PTR(event)->dev);
         int r;
 
+        if (!EVENT_MODE_DESTRUCTIVE(event))
+                return 0;
+
         r = sd_device_get_devnum(dev, NULL);
         if (r == -ENOENT)
                 return 0;
@@ -291,18 +301,22 @@ static int event_execute_rules_on_remove(UdevEvent *event, UdevRules *rules) {
         if (r < 0)
                 log_device_debug_errno(dev, r, "Failed to read database under /run/udev/data/: %m");
 
-        r = device_tag_index(dev, NULL, false);
-        if (r < 0)
-                log_device_debug_errno(dev, r, "Failed to remove corresponding tag files under /run/udev/tag/, ignoring: %m");
+        if (EVENT_MODE_DESTRUCTIVE(event)) {
+                r = device_tag_index(dev, NULL, false);
+                if (r < 0)
+                        log_device_debug_errno(dev, r, "Failed to remove corresponding tag files under /run/udev/tag/, ignoring: %m");
 
-        r = device_delete_db(dev);
-        if (r < 0)
-                log_device_debug_errno(dev, r, "Failed to delete database under /run/udev/data/, ignoring: %m");
+                r = device_delete_db(dev);
+                if (r < 0)
+                        log_device_debug_errno(dev, r, "Failed to delete database under /run/udev/data/, ignoring: %m");
+        }
 
         r = udev_rules_apply_to_event(rules, event);
 
-        if (sd_device_get_devnum(dev, NULL) >= 0)
-                (void) udev_node_remove(dev);
+        if (EVENT_MODE_DESTRUCTIVE(event)) {
+                if (sd_device_get_devnum(dev, NULL) >= 0)
+                        (void) udev_node_remove(dev);
+        }
 
         return r;
 }
@@ -324,12 +338,45 @@ static int copy_all_tags(sd_device *d, sd_device *s) {
         return 0;
 }
 
+static int update_clone(UdevEvent *event) {
+        sd_device *dev = ASSERT_PTR(ASSERT_PTR(event)->dev_db_clone);
+        int r;
+
+        if (!EVENT_MODE_DESTRUCTIVE(event))
+                return 0;
+
+        /* Drop previously added property for safety to make IMPORT{db}="ID_RENAMING" not work. This is
+         * mostly for 'move' uevent, but let's do unconditionally. Why? If a network interface is renamed in
+         * initrd, then udevd may lose the 'move' uevent during switching root. Usually, we do not set the
+         * persistent flag for network interfaces, but user may set it. Just for safety. */
+
+        r = device_add_property(dev, "ID_RENAMING", NULL);
+        if (r < 0)
+                return log_device_debug_errno(dev, r, "Failed to remove 'ID_RENAMING' property: %m");
+
+        /* If the database file already exists, append ID_PROCESSING property to the existing database,
+         * to indicate that the device is being processed by udevd. */
+        if (device_has_db(dev) > 0) {
+                r = device_add_property(dev, "ID_PROCESSING", "1");
+                if (r < 0)
+                        return log_device_warning_errno(dev, r, "Failed to add 'ID_PROCESSING' property: %m");
+
+                r = device_update_db(dev);
+                if (r < 0)
+                        return log_device_warning_errno(dev, r, "Failed to update database under /run/udev/data/: %m");
+        }
+
+        return 0;
+}
+
 int udev_event_execute_rules(UdevEvent *event, UdevRules *rules) {
         sd_device_action_t action;
         sd_device *dev;
         int r;
 
-        dev = ASSERT_PTR(ASSERT_PTR(event)->dev);
+        assert(event);
+        assert(IN_SET(event->event_mode, EVENT_UDEV_WORKER, EVENT_UDEVADM_TEST, EVENT_TEST_RULE_RUNNER));
+        dev = ASSERT_PTR(event->dev);
         assert(rules);
 
         r = sd_device_get_action(dev, &action);
@@ -347,25 +394,9 @@ int udev_event_execute_rules(UdevEvent *event, UdevRules *rules) {
         if (r < 0)
                 log_device_warning_errno(dev, r, "Failed to copy all tags from old database entry, ignoring: %m");
 
-        /* Drop previously added property for safety to make IMPORT{db}="ID_RENAMING" not work. This is
-         * mostly for 'move' uevent, but let's do unconditionally. Why? If a network interface is renamed in
-         * initrd, then udevd may lose the 'move' uevent during switching root. Usually, we do not set the
-         * persistent flag for network interfaces, but user may set it. Just for safety. */
-        r = device_add_property(event->dev_db_clone, "ID_RENAMING", NULL);
+        r = update_clone(event);
         if (r < 0)
-                return log_device_debug_errno(dev, r, "Failed to remove 'ID_RENAMING' property: %m");
-
-        /* If the database file already exists, append ID_PROCESSING property to the existing database,
-         * to indicate that the device is being processed by udevd. */
-        if (device_has_db(event->dev_db_clone) > 0) {
-                r = device_add_property(event->dev_db_clone, "ID_PROCESSING", "1");
-                if (r < 0)
-                        return log_device_warning_errno(event->dev_db_clone, r, "Failed to add 'ID_PROCESSING' property: %m");
-
-                r = device_update_db(event->dev_db_clone);
-                if (r < 0)
-                        return log_device_warning_errno(event->dev_db_clone, r, "Failed to update database under /run/udev/data/: %m");
-        }
+                return r;
 
         DEVICE_TRACE_POINT(rules_start, dev);
 
@@ -392,10 +423,12 @@ int udev_event_execute_rules(UdevEvent *event, UdevRules *rules) {
         if (r < 0)
                 return log_device_debug_errno(dev, r, "Failed to set initialization timestamp: %m");
 
-        /* (re)write database file */
-        r = device_tag_index(dev, event->dev_db_clone, true);
-        if (r < 0)
-                return log_device_debug_errno(dev, r, "Failed to update tags under /run/udev/tag/: %m");
+        if (EVENT_MODE_DESTRUCTIVE(event)) {
+                /* (re)write database file */
+                r = device_tag_index(dev, event->dev_db_clone, true);
+                if (r < 0)
+                        return log_device_debug_errno(dev, r, "Failed to update tags under /run/udev/tag/: %m");
+        }
 
         /* If the database file for the device will be created below, add ID_PROCESSING=1 to indicate that
          * the device is still being processed by udevd, as commands specified in RUN are invoked after
@@ -406,9 +439,11 @@ int udev_event_execute_rules(UdevEvent *event, UdevRules *rules) {
                         return log_device_warning_errno(dev, r, "Failed to add 'ID_PROCESSING' property: %m");
         }
 
-        r = device_update_db(dev);
-        if (r < 0)
-                return log_device_debug_errno(dev, r, "Failed to update database under /run/udev/data/: %m");
+        if (EVENT_MODE_DESTRUCTIVE(event)) {
+                r = device_update_db(dev);
+                if (r < 0)
+                        return log_device_debug_errno(dev, r, "Failed to update database under /run/udev/data/: %m");
+        }
 
         device_set_is_initialized(dev);
 
index 5ff7bd7ec1b9f7f23f170ab0bf8673cdd23bc7e0..3dc89365bb308394a940848582319911ddd481ee 100644 (file)
 #include "udev-worker.h"
 #include "user-util.h"
 
+typedef enum EventMode {
+        EVENT_UDEV_WORKER,
+        EVENT_UDEVADM_TEST,
+        EVENT_UDEVADM_TEST_BUILTIN,
+        EVENT_TEST_RULE_RUNNER,
+        EVENT_TEST_SPAWN,
+} EventMode;
+
 typedef struct UdevEvent {
         UdevWorker *worker;
         sd_netlink *rtnl;
@@ -47,10 +55,16 @@ typedef struct UdevEvent {
         bool run_final;
         bool log_level_was_debug;
         int default_log_level;
+        EventMode event_mode;
 } UdevEvent;
 
-UdevEvent *udev_event_new(sd_device *dev, UdevWorker *worker);
+UdevEvent *udev_event_new(sd_device *dev, UdevWorker *worker, EventMode mode);
 UdevEvent *udev_event_free(UdevEvent *event);
 DEFINE_TRIVIAL_CLEANUP_FUNC(UdevEvent*, udev_event_free);
 
 int udev_event_execute_rules(UdevEvent *event, UdevRules *rules);
+
+static inline bool EVENT_MODE_DESTRUCTIVE(UdevEvent *event) {
+        assert(event);
+        return IN_SET(event->event_mode, EVENT_UDEV_WORKER, EVENT_TEST_RULE_RUNNER);
+}
index 3ec675746bc6e0282070b65ea84740c6882425c6..51732080e85ba8429653c6db8d82f3dd22f097a4 100644 (file)
@@ -2100,7 +2100,7 @@ static int udev_rule_apply_token_to_event(
                         return false;
                 }
 
-                log_event_debug(dev, token, "Running PROGRAM '%s'", buf);
+                log_event_debug(dev, token, "Running PROGRAM=\"%s\"", buf);
 
                 r = udev_event_spawn(event, /* accept_failure = */ true, buf, result, sizeof(result), NULL);
                 if (r != 0) {
@@ -2264,7 +2264,7 @@ static int udev_rule_apply_token_to_event(
 
                 log_event_debug(dev, token, "Importing properties from results of builtin command '%s'", buf);
 
-                r = udev_builtin_run(event, cmd, buf, false);
+                r = udev_builtin_run(event, cmd, buf);
                 if (r < 0) {
                         /* remember failure */
                         log_event_debug_errno(dev, token, r, "Failed to run builtin '%s': %m", buf);
@@ -2702,14 +2702,18 @@ static int udev_rule_apply_token_to_event(
                         break;
                 }
 
-                log_event_debug(dev, token, "ATTR '%s' writing '%s'", buf, value);
-                r = write_string_file(buf, value,
-                                      WRITE_STRING_FILE_VERIFY_ON_FAILURE |
-                                      WRITE_STRING_FILE_DISABLE_BUFFER |
-                                      WRITE_STRING_FILE_AVOID_NEWLINE |
-                                      WRITE_STRING_FILE_VERIFY_IGNORE_NEWLINE);
-                if (r < 0)
-                        log_event_error_errno(dev, token, r, "Failed to write ATTR{%s}, ignoring: %m", buf);
+                if (EVENT_MODE_DESTRUCTIVE(event)) {
+                        log_event_debug(dev, token, "Writing ATTR{'%s'}=\"%s\".", buf, value);
+                        r = write_string_file(buf, value,
+                                              WRITE_STRING_FILE_VERIFY_ON_FAILURE |
+                                              WRITE_STRING_FILE_DISABLE_BUFFER |
+                                              WRITE_STRING_FILE_AVOID_NEWLINE |
+                                              WRITE_STRING_FILE_VERIFY_IGNORE_NEWLINE);
+                        if (r < 0)
+                                log_event_error_errno(dev, token, r, "Failed to write ATTR{%s}=\"%s\", ignoring: %m", buf, value);
+                } else
+                        log_event_debug(dev, token, "Running in test mode, skipping writing ATTR{%s}=\"%s\".", buf, value);
+
                 break;
         }
         case TK_A_SYSCTL: {
@@ -2731,10 +2735,15 @@ static int udev_rule_apply_token_to_event(
                 }
 
                 sysctl_normalize(buf);
-                log_event_debug(dev, token, "SYSCTL '%s' writing '%s'", buf, value);
-                r = sysctl_write(buf, value);
-                if (r < 0)
-                        log_event_error_errno(dev, token, r, "Failed to write SYSCTL{%s}='%s', ignoring: %m", buf, value);
+
+                if (EVENT_MODE_DESTRUCTIVE(event)) {
+                        log_event_debug(dev, token, "Writing SYSCTL{%s}=\"%s\".", buf, value);
+                        r = sysctl_write(buf, value);
+                        if (r < 0)
+                                log_event_error_errno(dev, token, r, "Failed to write SYSCTL{%s}=\"%s\", ignoring: %m", buf, value);
+                } else
+                        log_event_debug(dev, token, "Running in test mode, skipping writing SYSCTL{%s}=\"%s\".", buf, value);
+
                 break;
         }
         case TK_A_RUN_BUILTIN:
index 8f7c9fec42c9eed1c0388e20c6510d47d6b3d0fc..01a6dcdaa82d2c2312d2115426847c2e0bfc45af 100644 (file)
@@ -225,9 +225,19 @@ int udev_event_spawn(
         int r;
 
         assert(event);
+        assert(IN_SET(event->event_mode, EVENT_UDEV_WORKER, EVENT_UDEVADM_TEST, EVENT_TEST_RULE_RUNNER, EVENT_TEST_SPAWN));
         assert(event->dev);
+        assert(cmd);
         assert(result || result_size == 0);
 
+        if (event->event_mode == EVENT_UDEVADM_TEST &&
+            !STARTSWITH_SET(cmd, "ata_id", "cdrom_id", "dmi_memory_id", "fido_id", "mtd_probe", "scsi_id")) {
+                log_device_debug(event->dev, "Running in test mode, skipping execution of '%s'.", cmd);
+                result[0] = '\0';
+                ret_truncated = false;
+                return 0;
+        }
+
         int timeout_signal = event->worker ? event->worker->timeout_signal : SIGKILL;
         usec_t timeout_usec = event->worker ? event->worker->timeout_usec : DEFAULT_WORKER_TIMEOUT_USEC;
         usec_t now_usec = now(CLOCK_MONOTONIC);
@@ -333,7 +343,7 @@ void udev_event_execute_run(UdevEvent *event) {
 
                 if (builtin_cmd != _UDEV_BUILTIN_INVALID) {
                         log_device_debug(event->dev, "Running built-in command \"%s\"", command);
-                        r = udev_builtin_run(event, builtin_cmd, command, false);
+                        r = udev_builtin_run(event, builtin_cmd, command);
                         if (r < 0)
                                 log_device_debug_errno(event->dev, r, "Failed to run built-in command \"%s\", ignoring: %m", command);
                 } else {
index b038b680993ff1a3aeb7bf390d5f31eb577fcbbb..97c5679b74ca5e487e68722fb5f25613e100948a 100644 (file)
@@ -172,7 +172,7 @@ static int worker_process_device(UdevWorker *worker, sd_device *dev) {
 
         log_device_uevent(dev, "Processing device");
 
-        udev_event = udev_event_new(dev, worker);
+        udev_event = udev_event_new(dev, worker, EVENT_UDEV_WORKER);
         if (!udev_event)
                 return -ENOMEM;
 
index 64615f545396e33c1307216d593ca41720b6a6e6..29dc88330c6d84ac5f0d45bdd3c00745eb262a09 100644 (file)
@@ -1,15 +1,4 @@
 /* SPDX-License-Identifier: GPL-2.0-or-later */
-/*
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- */
 
 #include <errno.h>
 #include <getopt.h>
@@ -19,6 +8,7 @@
 #include <string.h>
 #include <unistd.h>
 
+#include "creds-util.h"
 #include "parse-util.h"
 #include "process-util.h"
 #include "static-destruct.h"
@@ -37,9 +27,21 @@ static bool arg_exit = false;
 static int arg_max_children = -1;
 static int arg_log_level = -1;
 static int arg_start_exec_queue = -1;
+static bool arg_load_credentials = false;
 
 STATIC_DESTRUCTOR_REGISTER(arg_env, strv_freep);
 
+static bool arg_has_control_commands(void) {
+        return
+                arg_exit ||
+                arg_log_level >= 0 ||
+                arg_start_exec_queue >= 0 ||
+                arg_reload ||
+                !strv_isempty(arg_env) ||
+                arg_max_children >= 0 ||
+                arg_ping;
+}
+
 static int help(void) {
         printf("%s control OPTION\n\n"
                "Control the udev daemon.\n\n"
@@ -53,7 +55,8 @@ static int help(void) {
                "  -p --property=KEY=VALUE  Set a global property for all events\n"
                "  -m --children-max=N      Maximum number of children\n"
                "     --ping                Wait for udev to respond to a ping message\n"
-               "  -t --timeout=SECONDS     Maximum time to block for a reply\n",
+               "  -t --timeout=SECONDS     Maximum time to block for a reply\n"
+               "     --load-credentials    Load udev rules from credentials\n",
                program_invocation_short_name);
 
         return 0;
@@ -62,23 +65,25 @@ static int help(void) {
 static int parse_argv(int argc, char *argv[]) {
         enum {
                 ARG_PING = 0x100,
+                ARG_LOAD_CREDENTIALS,
         };
 
         static const struct option options[] = {
-                { "exit",             no_argument,       NULL, 'e'      },
-                { "log-level",        required_argument, NULL, 'l'      },
-                { "log-priority",     required_argument, NULL, 'l'      }, /* for backward compatibility */
-                { "stop-exec-queue",  no_argument,       NULL, 's'      },
-                { "start-exec-queue", no_argument,       NULL, 'S'      },
-                { "reload",           no_argument,       NULL, 'R'      },
-                { "reload-rules",     no_argument,       NULL, 'R'      }, /* alias for -R */
-                { "property",         required_argument, NULL, 'p'      },
-                { "env",              required_argument, NULL, 'p'      }, /* alias for -p */
-                { "children-max",     required_argument, NULL, 'm'      },
-                { "ping",             no_argument,       NULL, ARG_PING },
-                { "timeout",          required_argument, NULL, 't'      },
-                { "version",          no_argument,       NULL, 'V'      },
-                { "help",             no_argument,       NULL, 'h'      },
+                { "exit",             no_argument,       NULL, 'e'                  },
+                { "log-level",        required_argument, NULL, 'l'                  },
+                { "log-priority",     required_argument, NULL, 'l'                  }, /* for backward compatibility */
+                { "stop-exec-queue",  no_argument,       NULL, 's'                  },
+                { "start-exec-queue", no_argument,       NULL, 'S'                  },
+                { "reload",           no_argument,       NULL, 'R'                  },
+                { "reload-rules",     no_argument,       NULL, 'R'                  }, /* alias for -R */
+                { "property",         required_argument, NULL, 'p'                  },
+                { "env",              required_argument, NULL, 'p'                  }, /* alias for -p */
+                { "children-max",     required_argument, NULL, 'm'                  },
+                { "ping",             no_argument,       NULL, ARG_PING             },
+                { "timeout",          required_argument, NULL, 't'                  },
+                { "load-credentials", no_argument,       NULL, ARG_LOAD_CREDENTIALS },
+                { "version",          no_argument,       NULL, 'V'                  },
+                { "help",             no_argument,       NULL, 'h'                  },
                 {}
         };
 
@@ -87,10 +92,6 @@ static int parse_argv(int argc, char *argv[]) {
         assert(argc >= 0);
         assert(argv);
 
-        if (argc <= 1)
-                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
-                                       "This command expects one or more options.");
-
         while ((c = getopt_long(argc, argv, "el:sSRp:m:t:Vh", options, NULL)) >= 0)
                 switch (c) {
 
@@ -145,6 +146,10 @@ static int parse_argv(int argc, char *argv[]) {
                                 return log_error_errno(r, "Failed to parse timeout value '%s': %m", optarg);
                         break;
 
+                case ARG_LOAD_CREDENTIALS:
+                        arg_load_credentials = true;
+                        break;
+
                 case 'V':
                         return print_version();
 
@@ -158,6 +163,10 @@ static int parse_argv(int argc, char *argv[]) {
                         assert_not_reached();
                 }
 
+        if (!arg_has_control_commands() && !arg_load_credentials)
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+                                       "No control command option is specified.");
+
         if (optind < argc)
                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
                                        "Extraneous argument: %s", argv[optind]);
@@ -165,19 +174,10 @@ static int parse_argv(int argc, char *argv[]) {
         return 1;
 }
 
-int control_main(int argc, char *argv[], void *userdata) {
+static int send_control_commands(void) {
         _cleanup_(udev_ctrl_unrefp) UdevCtrl *uctrl = NULL;
         int r;
 
-        if (running_in_chroot() > 0) {
-                log_info("Running in chroot, ignoring request.");
-                return 0;
-        }
-
-        r = parse_argv(argc, argv);
-        if (r <= 0)
-                return r;
-
         r = udev_ctrl_new(&uctrl);
         if (r < 0)
                 return log_error_errno(r, "Failed to initialize udev control: %m");
@@ -237,3 +237,34 @@ int control_main(int argc, char *argv[], void *userdata) {
 
         return 0;
 }
+
+int control_main(int argc, char *argv[], void *userdata) {
+        int r;
+
+        if (running_in_chroot() > 0) {
+                log_info("Running in chroot, ignoring request.");
+                return 0;
+        }
+
+        r = parse_argv(argc, argv);
+        if (r <= 0)
+                return r;
+
+        if (arg_load_credentials) {
+                static const PickUpCredential table[] = {
+                        { "udev.conf.",  "/run/udev/udev.conf.d/", ".conf"  },
+                        { "udev.rules.", "/run/udev/rules.d/",     ".rules" },
+                };
+                r = pick_up_credentials(table, ELEMENTSOF(table));
+                if (r < 0)
+                        return r;
+        }
+
+        if (arg_has_control_commands()) {
+                r = send_control_commands();
+                if (r < 0)
+                        return r;
+        }
+
+        return 0;
+}
index fdbdb6f59a7060af99fd7da73f331c3c5ca6340b..88319497176d0f2d4304597e2e08cc3113dbe06b 100644 (file)
@@ -100,7 +100,7 @@ int builtin_main(int argc, char *argv[], void *userdata) {
                 goto finish;
         }
 
-        event = udev_event_new(dev, NULL);
+        event = udev_event_new(dev, NULL, EVENT_UDEVADM_TEST_BUILTIN);
         if (!event) {
                 r = log_oom();
                 goto finish;
@@ -115,7 +115,7 @@ int builtin_main(int argc, char *argv[], void *userdata) {
                 }
         }
 
-        r = udev_builtin_run(event, cmd, arg_command, true);
+        r = udev_builtin_run(event, cmd, arg_command);
         if (r < 0) {
                 log_debug_errno(r, "Builtin command '%s' fails: %m", arg_command);
                 goto finish;
index 6c4a01a30e30b6800582aa772260bf194503cb32..b0d2f9b20c229e46ca01174f64bec45dc2f18727 100644 (file)
 
 #include "device-private.h"
 #include "device-util.h"
+#include "format-util.h"
 #include "path-util.h"
 #include "string-util.h"
+#include "strv.h"
 #include "strxcpyx.h"
+#include "terminal-util.h"
 #include "udev-builtin.h"
 #include "udev-event.h"
 #include "udev-format.h"
 #include "udevadm-util.h"
 #include "udevadm.h"
+#include "user-util.h"
 
 static sd_device_action_t arg_action = SD_DEVICE_ADD;
 static ResolveNameTiming arg_resolve_name_timing = RESOLVE_NAME_EARLY;
@@ -89,9 +93,7 @@ int test_main(int argc, char *argv[], void *userdata) {
         _cleanup_(udev_rules_freep) UdevRules *rules = NULL;
         _cleanup_(udev_event_freep) UdevEvent *event = NULL;
         _cleanup_(sd_device_unrefp) sd_device *dev = NULL;
-        const char *cmd;
         sigset_t mask, sigmask_orig;
-        void *val;
         int r;
 
         log_set_max_level(LOG_DEBUG);
@@ -125,24 +127,89 @@ int test_main(int argc, char *argv[], void *userdata) {
         /* don't read info from the db */
         device_seal(dev);
 
-        event = udev_event_new(dev, NULL);
+        event = udev_event_new(dev, NULL, EVENT_UDEVADM_TEST);
 
         assert_se(sigfillset(&mask) >= 0);
         assert_se(sigprocmask(SIG_SETMASK, &mask, &sigmask_orig) >= 0);
 
         udev_event_execute_rules(event, rules);
 
+        printf("%sProperties:%s\n", ansi_highlight(), ansi_normal());
         FOREACH_DEVICE_PROPERTY(dev, key, value)
-                printf("%s=%s\n", key, value);
+                printf("  %s=%s\n", key, value);
 
-        ORDERED_HASHMAP_FOREACH_KEY(val, cmd, event->run_list) {
-                char program[UDEV_PATH_SIZE];
-                bool truncated;
+        if (sd_device_get_tag_first(dev)) {
+                printf("%sTags:%s\n", ansi_highlight(), ansi_normal());
+                FOREACH_DEVICE_TAG(dev, tag)
+                        printf("  %s\n", tag);
+        }
+
+        if (sd_device_get_devnum(dev, NULL) >= 0) {
+
+                if (sd_device_get_devlink_first(dev)) {
+                        int prio;
+                        device_get_devlink_priority(dev, &prio);
+                        printf("%sDevice node symlinks:%s (priority=%i)\n", ansi_highlight(), ansi_normal(), prio);
+                        FOREACH_DEVICE_DEVLINK(dev, devlink)
+                                printf("  %s\n", devlink);
+                }
+
+                printf("%sInotify watch:%s\n  %s\n", ansi_highlight(), ansi_normal(), enabled_disabled(event->inotify_watch));
+
+                uid_t uid = event->uid;
+                if (!uid_is_valid(uid))
+                        (void) device_get_devnode_uid(dev, &uid);
+                if (uid_is_valid(uid)) {
+                        _cleanup_free_ char *user = uid_to_name(uid);
+                        printf("%sDevice node owner:%s\n  %s (uid="UID_FMT")\n", ansi_highlight(), ansi_normal(), strna(user), uid);
+                }
+
+                gid_t gid = event->gid;
+                if (!gid_is_valid(uid))
+                        (void) device_get_devnode_gid(dev, &gid);
+                if (gid_is_valid(gid)) {
+                        _cleanup_free_ char *group = gid_to_name(gid);
+                        printf("%sDevice node group:%s\n  %s (gid="GID_FMT")\n", ansi_highlight(), ansi_normal(), strna(group), gid);
+                }
 
-                (void) udev_event_apply_format(event, cmd, program, sizeof(program), false, &truncated);
-                if (truncated)
-                        log_warning("The command '%s' is truncated while substituting into '%s'.", program, cmd);
-                printf("run: '%s'\n", program);
+                mode_t mode = event->mode;
+                if (mode == MODE_INVALID)
+                        (void) device_get_devnode_mode(dev, &mode);
+                if (mode != MODE_INVALID)
+                        printf("%sDevice node permission:%s\n  %04o\n", ansi_highlight(), ansi_normal(), mode);
+
+                if (!ordered_hashmap_isempty(event->seclabel_list)) {
+                        const char *name, *label;
+                        printf("%sDevice node security label:%s\n", ansi_highlight(), ansi_normal());
+                        ORDERED_HASHMAP_FOREACH_KEY(label, name, event->seclabel_list)
+                                printf("  %s : %s\n", name, label);
+                }
+        }
+
+        if (sd_device_get_ifindex(dev, NULL) >= 0) {
+                if (!isempty(event->name))
+                        printf("%sNetwork interface name:%s\n  %s\n", ansi_highlight(), ansi_normal(), event->name);
+
+                if (!strv_isempty(event->altnames)) {
+                        bool space = true;
+                        printf("%sAlternative interface names:%s", ansi_highlight(), ansi_normal());
+                        fputstrv(stdout, event->altnames, "\n  ", &space);
+                        puts("");
+                }
+        }
+
+        if (!ordered_hashmap_isempty(event->run_list)) {
+                void *val;
+                const char *command;
+                printf("%sQueued commands:%s\n", ansi_highlight(), ansi_normal());
+                ORDERED_HASHMAP_FOREACH_KEY(val, command, event->run_list) {
+                        UdevBuiltinCommand builtin_cmd = PTR_TO_UDEV_BUILTIN_CMD(val);
+
+                        if (builtin_cmd != _UDEV_BUILTIN_INVALID)
+                                printf("  RUN{builtin} : %s\n", command);
+                        else
+                                printf("  RUN{program} : %s\n", command);
+                }
         }
 
         r = 0;
index e6620c25dd1e571031434c8992f3e7c23c74541f..6ffc86bbf1efe91f3ccf5d5f73a7fb7071fca4ee 100644 (file)
@@ -67,7 +67,7 @@ static int check_device(const char *path) {
                 return r;
 
         if (arg_wait_until == WAIT_UNTIL_INITIALIZED)
-                return sd_device_get_is_initialized(dev);
+                return device_is_processed(dev);
 
         return true;
 }
index 30527e9556d4c95878c95f11bee49b3351fa61e1..5c540659f35bf78f78007cf9d755f1eb15e4f21d 100644 (file)
@@ -1,16 +1,6 @@
 /* SPDX-License-Identifier: GPL-2.0-or-later */
 /*
  * Copyright (c) 2009 Filippo Argiolas <filippo.argiolas@gmail.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details:
  */
 
 #include <ctype.h>
index ecbdcb0f41fe19ed49f63ae2675e2a4c94abd7e2..918b4d772f28ea126783e0fad82945a2ff32880e 100644 (file)
@@ -174,19 +174,19 @@ static int table_add_uid_boundaries(Table *table, const UIDRange *p) {
 
         assert(table);
 
-        for (size_t i = 0; i < ELEMENTSOF(uid_range_table); i++) {
+        FOREACH_ARRAY(i, uid_range_table, ELEMENTSOF(uid_range_table)) {
                 _cleanup_free_ char *name = NULL, *comment = NULL;
 
-                if (!uid_range_covers(p, uid_range_table[i].first, uid_range_table[i].last - uid_range_table[i].first + 1))
+                if (!uid_range_covers(p, i->first, i->last - i->first + 1))
                         continue;
 
                 name = strjoin(special_glyph(SPECIAL_GLYPH_ARROW_DOWN),
-                               " begin ", uid_range_table[i].name, " users ",
+                               " begin ", i->name, " users ",
                                special_glyph(SPECIAL_GLYPH_ARROW_DOWN));
                 if (!name)
                         return log_oom();
 
-                comment = strjoin("First ", uid_range_table[i].name, " user");
+                comment = strjoin("First ", i->name, " user");
                 if (!comment)
                         return log_oom();
 
@@ -195,9 +195,9 @@ static int table_add_uid_boundaries(Table *table, const UIDRange *p) {
                                 TABLE_STRING, special_glyph(SPECIAL_GLYPH_TREE_TOP),
                                 TABLE_STRING, name,
                                 TABLE_SET_COLOR, ansi_grey(),
-                                TABLE_STRING, user_disposition_to_string(uid_range_table[i].disposition),
+                                TABLE_STRING, user_disposition_to_string(i->disposition),
                                 TABLE_SET_COLOR, ansi_grey(),
-                                TABLE_UID, uid_range_table[i].first,
+                                TABLE_UID, i->first,
                                 TABLE_SET_COLOR, ansi_grey(),
                                 TABLE_EMPTY,
                                 TABLE_STRING, comment,
@@ -210,13 +210,13 @@ static int table_add_uid_boundaries(Table *table, const UIDRange *p) {
 
                 free(name);
                 name = strjoin(special_glyph(SPECIAL_GLYPH_ARROW_UP),
-                               " end ", uid_range_table[i].name, " users ",
+                               " end ", i->name, " users ",
                                special_glyph(SPECIAL_GLYPH_ARROW_UP));
                 if (!name)
                         return log_oom();
 
                 free(comment);
-                comment = strjoin("Last ", uid_range_table[i].name, " user");
+                comment = strjoin("Last ", i->name, " user");
                 if (!comment)
                         return log_oom();
 
@@ -225,9 +225,9 @@ static int table_add_uid_boundaries(Table *table, const UIDRange *p) {
                                 TABLE_STRING, special_glyph(SPECIAL_GLYPH_TREE_RIGHT),
                                 TABLE_STRING, name,
                                 TABLE_SET_COLOR, ansi_grey(),
-                                TABLE_STRING, user_disposition_to_string(uid_range_table[i].disposition),
+                                TABLE_STRING, user_disposition_to_string(i->disposition),
                                 TABLE_SET_COLOR, ansi_grey(),
-                                TABLE_UID, uid_range_table[i].last,
+                                TABLE_UID, i->last,
                                 TABLE_SET_COLOR, ansi_grey(),
                                 TABLE_EMPTY,
                                 TABLE_STRING, comment,
@@ -310,9 +310,10 @@ static int table_add_uid_map(
         assert(table);
         assert(add_unavailable);
 
-        for (size_t i = 0; p && i < p->n_entries; i++) {
-                UIDRangeEntry *x = p->entries + i;
+        if (!p)
+                return 0;
 
+        FOREACH_ARRAY(x, p->entries, p->n_entries) {
                 if (focus < x->start) {
                         r = add_unavailable(table, focus, x->start-1);
                         if (r < 0)
@@ -428,7 +429,7 @@ static int display_user(int argc, char *argv[], void *userdata) {
                 _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");
+                r = uid_range_load_userns(/* path = */ NULL, UID_RANGE_USERNS_INSIDE, &uid_range);
                 if (r < 0)
                         log_debug_errno(r, "Failed to load /proc/self/uid_map, ignoring: %m");
 
@@ -531,19 +532,19 @@ static int table_add_gid_boundaries(Table *table, const UIDRange *p) {
 
         assert(table);
 
-        for (size_t i = 0; i < ELEMENTSOF(uid_range_table); i++) {
+        FOREACH_ARRAY(i, uid_range_table, ELEMENTSOF(uid_range_table)) {
                 _cleanup_free_ char *name = NULL, *comment = NULL;
 
-                if (!uid_range_covers(p, uid_range_table[i].first, uid_range_table[i].last))
+                if (!uid_range_covers(p, i->first, i->last - i->first + 1))
                         continue;
 
                 name = strjoin(special_glyph(SPECIAL_GLYPH_ARROW_DOWN),
-                               " begin ", uid_range_table[i].name, " groups ",
+                               " begin ", i->name, " groups ",
                                special_glyph(SPECIAL_GLYPH_ARROW_DOWN));
                 if (!name)
                         return log_oom();
 
-                comment = strjoin("First ", uid_range_table[i].name, " group");
+                comment = strjoin("First ", i->name, " group");
                 if (!comment)
                         return log_oom();
 
@@ -552,9 +553,9 @@ static int table_add_gid_boundaries(Table *table, const UIDRange *p) {
                                 TABLE_STRING, special_glyph(SPECIAL_GLYPH_TREE_TOP),
                                 TABLE_STRING, name,
                                 TABLE_SET_COLOR, ansi_grey(),
-                                TABLE_STRING, user_disposition_to_string(uid_range_table[i].disposition),
+                                TABLE_STRING, user_disposition_to_string(i->disposition),
                                 TABLE_SET_COLOR, ansi_grey(),
-                                TABLE_GID, uid_range_table[i].first,
+                                TABLE_GID, i->first,
                                 TABLE_SET_COLOR, ansi_grey(),
                                 TABLE_STRING, comment,
                                 TABLE_SET_COLOR, ansi_grey(),
@@ -564,13 +565,13 @@ static int table_add_gid_boundaries(Table *table, const UIDRange *p) {
 
                 free(name);
                 name = strjoin(special_glyph(SPECIAL_GLYPH_ARROW_UP),
-                               " end ", uid_range_table[i].name, " groups ",
+                               " end ", i->name, " groups ",
                                special_glyph(SPECIAL_GLYPH_ARROW_UP));
                 if (!name)
                         return log_oom();
 
                 free(comment);
-                comment = strjoin("Last ", uid_range_table[i].name, " group");
+                comment = strjoin("Last ", i->name, " group");
                 if (!comment)
                         return log_oom();
 
@@ -579,9 +580,9 @@ static int table_add_gid_boundaries(Table *table, const UIDRange *p) {
                                 TABLE_STRING, special_glyph(SPECIAL_GLYPH_TREE_RIGHT),
                                 TABLE_STRING, name,
                                 TABLE_SET_COLOR, ansi_grey(),
-                                TABLE_STRING, user_disposition_to_string(uid_range_table[i].disposition),
+                                TABLE_STRING, user_disposition_to_string(i->disposition),
                                 TABLE_SET_COLOR, ansi_grey(),
-                                TABLE_GID, uid_range_table[i].last,
+                                TABLE_GID, i->last,
                                 TABLE_SET_COLOR, ansi_grey(),
                                 TABLE_STRING, comment,
                                 TABLE_SET_COLOR, ansi_grey(),
@@ -731,7 +732,7 @@ static int display_group(int argc, char *argv[], void *userdata) {
                 _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");
+                r = uid_range_load_userns(/* path = */ NULL, GID_RANGE_USERNS_INSIDE, &gid_range);
                 if (r < 0)
                         log_debug_errno(r, "Failed to load /proc/self/gid_map, ignoring: %m");
 
index a8d27afa113e50618a296d980cd3a54edaa17857..58f6781b77ab237d9f8d4a5eec7cc79e2dc1df01 100644 (file)
@@ -56,10 +56,12 @@ int start_transient_scope(sd_bus *bus, const char *machine_name, bool allow_pidf
         if (r < 0)
                 return bus_log_create_error(r);
 
-        r = sd_bus_message_append(m, "(sa)(sa)(sa)",
+        r = sd_bus_message_append(m, "(sv)(sv)(sv)",
                                   "Description", "s",  description,
                                   "AddRef",      "b",  1,
                                   "CollectMode", "s",  "inactive-or-failed");
+        if (r < 0)
+                return bus_log_create_error(r);
 
         _cleanup_(pidref_done) PidRef pidref = PIDREF_NULL;
         r = pidref_set_self(&pidref);
index a98040a213a8b990f140ac4570f52faed3975bb1..9b2b45e1e63ad2bc8522ea846c0bdc169430c52b 100644 (file)
 #include "strv.h"
 #include "vmspawn-util.h"
 
+static const char* const architecture_to_qemu_table[_ARCHITECTURE_MAX] = {
+        [ARCHITECTURE_ARM64]       = "aarch64",     /* differs from our name */
+        [ARCHITECTURE_ARM]         = "arm",
+        [ARCHITECTURE_ALPHA]       = "alpha",
+        [ARCHITECTURE_X86_64]      = "x86_64",      /* differs from our name */
+        [ARCHITECTURE_X86]         = "i386",        /* differs from our name */
+        [ARCHITECTURE_LOONGARCH64] = "loongarch64",
+        [ARCHITECTURE_MIPS64_LE]   = "mips",        /* differs from our name */
+        [ARCHITECTURE_MIPS_LE]     = "mips",        /* differs from our name */
+        [ARCHITECTURE_PARISC]      = "hppa",        /* differs from our name */
+        [ARCHITECTURE_PPC64_LE]    = "ppc",         /* differs from our name */
+        [ARCHITECTURE_PPC64]       = "ppc",         /* differs from our name */
+        [ARCHITECTURE_PPC]         = "ppc",
+        [ARCHITECTURE_RISCV32]     = "riscv32",
+        [ARCHITECTURE_RISCV64]     = "riscv64",
+        [ARCHITECTURE_S390X]       = "s390x",
+};
+
+static int native_arch_as_qemu(const char **ret) {
+        const char *s = architecture_to_qemu_table[native_architecture()];
+        if (!s)
+                return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Architecture %s not supported by qemu", architecture_to_string(native_architecture()));
+
+        if (ret)
+                *ret = s;
+
+        return 0;
+}
+
 OvmfConfig* ovmf_config_free(OvmfConfig *config) {
         if (!config)
                 return NULL;
@@ -87,6 +116,7 @@ typedef struct FirmwareData {
         char *firmware_format;
         char *vars;
         char *vars_format;
+        char **architectures;
 } FirmwareData;
 
 static bool firmware_data_supports_sb(const FirmwareData *fwd) {
@@ -104,6 +134,7 @@ static FirmwareData* firmware_data_free(FirmwareData *fwd) {
         free(fwd->firmware_format);
         free(fwd->vars);
         free(fwd->vars_format);
+        strv_free(fwd->architectures);
 
         return mfree(fwd);
 }
@@ -140,6 +171,32 @@ static int firmware_mapping(const char *name, JsonVariant *v, JsonDispatchFlags
         return json_dispatch(v, table, flags, userdata);
 }
 
+static int target_architecture(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) {
+        int r;
+        JsonVariant *e;
+        char ***supported_architectures = ASSERT_PTR(userdata);
+
+        static const JsonDispatch table[] = {
+                { "architecture", JSON_VARIANT_STRING, json_dispatch_string, 0, JSON_MANDATORY },
+                { "machines",     JSON_VARIANT_ARRAY,  NULL,                 0, JSON_MANDATORY },
+                {}
+        };
+
+        JSON_VARIANT_ARRAY_FOREACH(e, v) {
+                _cleanup_free_ char *arch = NULL;
+
+                r = json_dispatch(e, table, flags, &arch);
+                if (r < 0)
+                        return r;
+
+                r = strv_consume(supported_architectures, TAKE_PTR(arch));
+                if (r < 0)
+                        return r;
+        }
+
+        return 0;
+}
+
 static int get_firmware_search_dirs(char ***ret) {
         int r;
 
@@ -206,12 +263,12 @@ static int load_firmware_data(const char *path, FirmwareData **ret) {
                 return r;
 
         static const JsonDispatch table[] = {
-                { "description",     JSON_VARIANT_STRING, NULL,               0,                                JSON_MANDATORY },
-                { "interface-types", JSON_VARIANT_ARRAY,  NULL,               0,                                JSON_MANDATORY },
-                { "mapping",         JSON_VARIANT_OBJECT, firmware_mapping,   0,                                JSON_MANDATORY },
-                { "targets",         JSON_VARIANT_ARRAY,  NULL,               0,                                JSON_MANDATORY },
-                { "features",        JSON_VARIANT_ARRAY,  json_dispatch_strv, offsetof(FirmwareData, features), JSON_MANDATORY },
-                { "tags",            JSON_VARIANT_ARRAY,  NULL,               0,                                JSON_MANDATORY },
+                { "description",     JSON_VARIANT_STRING, NULL,                0,                                     JSON_MANDATORY },
+                { "interface-types", JSON_VARIANT_ARRAY,  NULL,                0,                                     JSON_MANDATORY },
+                { "mapping",         JSON_VARIANT_OBJECT, firmware_mapping,    0,                                     JSON_MANDATORY },
+                { "targets",         JSON_VARIANT_ARRAY,  target_architecture, offsetof(FirmwareData, architectures), JSON_MANDATORY },
+                { "features",        JSON_VARIANT_ARRAY,  json_dispatch_strv,  offsetof(FirmwareData, features),      JSON_MANDATORY },
+                { "tags",            JSON_VARIANT_ARRAY,  NULL,                0,                                     JSON_MANDATORY },
                 {}
         };
 
@@ -266,10 +323,15 @@ int load_ovmf_config(const char *path, OvmfConfig **ret) {
 int find_ovmf_config(int search_sb, OvmfConfig **ret) {
         _cleanup_(ovmf_config_freep) OvmfConfig *config = NULL;
         _cleanup_strv_free_ char **conf_files = NULL;
+        const char* native_arch_qemu;
         int r;
 
         assert(ret);
 
+        r = native_arch_as_qemu(&native_arch_qemu);
+        if (r < 0)
+                return r;
+
         /* Search in:
          * - $XDG_CONFIG_HOME/qemu/firmware
          * - /etc/qemu/firmware
@@ -296,6 +358,11 @@ int find_ovmf_config(int search_sb, OvmfConfig **ret) {
                         continue;
                 }
 
+                if (!strv_contains(fwd->architectures, native_arch_qemu)) {
+                        log_debug("Skipping %s, firmware doesn't support the native architecture.", *file);
+                        continue;
+                }
+
                 /* exclude firmware which doesn't match our Secure Boot requirements */
                 if (search_sb >= 0 && !!search_sb != firmware_data_supports_sb(fwd)) {
                         log_debug("Skipping %s, firmware doesn't fit required Secure Boot configuration.", *file);
@@ -320,6 +387,7 @@ int find_ovmf_config(int search_sb, OvmfConfig **ret) {
 }
 
 int find_qemu_binary(char **ret_qemu_binary) {
+        const char *native_arch_qemu;
         int r;
 
         /*
@@ -329,24 +397,6 @@ int find_qemu_binary(char **ret_qemu_binary) {
          * If the native architecture is not supported by qemu -EOPNOTSUPP will be returned;
          */
 
-        static const char *architecture_to_qemu_table[_ARCHITECTURE_MAX] = {
-                [ARCHITECTURE_ARM64]       = "aarch64",     /* differs from our name */
-                [ARCHITECTURE_ARM]         = "arm",
-                [ARCHITECTURE_ALPHA]       = "alpha",
-                [ARCHITECTURE_X86_64]      = "x86_64",      /* differs from our name */
-                [ARCHITECTURE_X86]         = "i386",        /* differs from our name */
-                [ARCHITECTURE_LOONGARCH64] = "loongarch64",
-                [ARCHITECTURE_MIPS64_LE]   = "mips",        /* differs from our name */
-                [ARCHITECTURE_MIPS_LE]     = "mips",        /* differs from our name */
-                [ARCHITECTURE_PARISC]      = "hppa",        /* differs from our name */
-                [ARCHITECTURE_PPC64_LE]    = "ppc",         /* differs from our name */
-                [ARCHITECTURE_PPC64]       = "ppc",         /* differs from our name */
-                [ARCHITECTURE_PPC]         = "ppc",
-                [ARCHITECTURE_RISCV32]     = "riscv32",
-                [ARCHITECTURE_RISCV64]     = "riscv64",
-                [ARCHITECTURE_S390X]       = "s390x",
-        };
-
         FOREACH_STRING(s, "qemu", "qemu-kvm") {
                 r = find_executable(s, ret_qemu_binary);
                 if (r == 0)
@@ -356,12 +406,12 @@ int find_qemu_binary(char **ret_qemu_binary) {
                         return r;
         }
 
-        const char *arch_qemu = architecture_to_qemu_table[native_architecture()];
-        if (!arch_qemu)
-                return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Architecture %s not supported by qemu", architecture_to_string(native_architecture()));
+        r = native_arch_as_qemu(&native_arch_qemu);
+        if (r < 0)
+                return r;
 
         _cleanup_free_ char *qemu_arch_specific = NULL;
-        qemu_arch_specific = strjoin("qemu-system-", arch_qemu);
+        qemu_arch_specific = strjoin("qemu-system-", native_arch_qemu);
         if (!qemu_arch_specific)
                 return -ENOMEM;
 
index 8d2348d74872846d863cd5ba6a98b601785b80d2..cb74b5c97ed5d217fb726456dfb35165c7a7fb52 100644 (file)
@@ -97,6 +97,7 @@ static char **arg_extra_drives = NULL;
 static char *arg_background = NULL;
 static bool arg_pass_ssh_key = true;
 static char *arg_ssh_key_type = NULL;
+static bool arg_discard_disk = true;
 
 STATIC_DESTRUCTOR_REGISTER(arg_directory, freep);
 STATIC_DESTRUCTOR_REGISTER(arg_image, freep);
@@ -146,6 +147,7 @@ static int help(void) {
                "     --network-user-mode   Use user mode networking\n"
                "     --secure-boot=BOOL    Enable searching for firmware supporting SecureBoot\n"
                "     --firmware=PATH|list  Select firmware definition file (or list available)\n"
+               "     --discard-disk=BOOL   Control processing of discard requests\n"
                "\n%3$sSystem Identity:%4$s\n"
                "  -M --machine=NAME        Set the machine name for the VM\n"
                "     --uuid=UUID           Set a specific machine UUID for the VM\n"
@@ -213,6 +215,7 @@ static int parse_argv(int argc, char *argv[]) {
                 ARG_SET_CREDENTIAL,
                 ARG_LOAD_CREDENTIAL,
                 ARG_FIRMWARE,
+                ARG_DISCARD_DISK,
                 ARG_CONSOLE,
                 ARG_BACKGROUND,
         };
@@ -254,6 +257,7 @@ static int parse_argv(int argc, char *argv[]) {
                 { "set-credential",    required_argument, NULL, ARG_SET_CREDENTIAL    },
                 { "load-credential",   required_argument, NULL, ARG_LOAD_CREDENTIAL   },
                 { "firmware",          required_argument, NULL, ARG_FIRMWARE          },
+                { "discard-disk",      required_argument, NULL, ARG_DISCARD_DISK      },
                 { "background",        required_argument, NULL, ARG_BACKGROUND        },
                 {}
         };
@@ -405,13 +409,9 @@ static int parse_argv(int argc, char *argv[]) {
                         break;
 
                 case ARG_REGISTER:
-                        r = parse_boolean(optarg);
-                        if (r < 0) {
-                                log_error("Failed to parse --register= argument: %s", optarg);
+                        r = parse_boolean_argument("--register=", optarg, &arg_register);
+                        if (r < 0)
                                 return r;
-                        }
-
-                        arg_register = r;
                         break;
 
                 case ARG_BIND:
@@ -455,11 +455,9 @@ static int parse_argv(int argc, char *argv[]) {
                         break;
 
                 case ARG_PASS_SSH_KEY:
-                        r = parse_boolean(optarg);
+                        r = parse_boolean_argument("--pass-ssh-key=", optarg, &arg_pass_ssh_key);
                         if (r < 0)
-                                return log_error_errno(r, "Failed to parse --pass-ssh-key= argument: %s", optarg);
-
-                        arg_pass_ssh_key = r;
+                                return r;
                         break;
 
                 case ARG_SSH_KEY_TYPE:
@@ -505,7 +503,7 @@ static int parse_argv(int argc, char *argv[]) {
                         }
 
                         if (!isempty(optarg) && !path_is_absolute(optarg) && !startswith(optarg, "./"))
-                                return log_error_errno(SYNTHETIC_ERRNO(errno), "Absolute path or path starting with './' required.");
+                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Absolute path or path starting with './' required.");
 
                         r = parse_path_argument(optarg, /* suppress_root= */ false, &arg_firmware);
                         if (r < 0)
@@ -513,6 +511,12 @@ static int parse_argv(int argc, char *argv[]) {
 
                         break;
 
+                case ARG_DISCARD_DISK:
+                        r = parse_boolean_argument("--discard-disk=", optarg, &arg_discard_disk);
+                        if (r < 0)
+                                return r;
+                        break;
+
                 case ARG_BACKGROUND:
                         r = free_and_strdup_warn(&arg_background, optarg);
                         if (r < 0)
@@ -1258,7 +1262,8 @@ static int run_virtual_machine(int kvm_device_fd, int vhost_device_fd) {
                 "-smp", arg_cpus ?: "1",
                 "-m", mem,
                 "-object", "rng-random,filename=/dev/urandom,id=rng0",
-                "-device", "virtio-rng-pci,rng=rng0,id=rng-device0"
+                "-device", "virtio-rng-pci,rng=rng0,id=rng-device0",
+                "-device", "virtio-balloon,free-page-reporting=on"
         );
         if (!cmdline)
                 return log_oom();
@@ -1538,7 +1543,7 @@ static int run_virtual_machine(int kvm_device_fd, int vhost_device_fd) {
                 if (!escaped_image)
                         log_oom();
 
-                r = strv_extendf(&cmdline, "if=none,id=mkosi,file=%s,format=raw", escaped_image);
+                r = strv_extendf(&cmdline, "if=none,id=mkosi,file=%s,format=raw,discard=%s", escaped_image, on_off(arg_discard_disk));
                 if (r < 0)
                         return log_oom();
 
index 0b5fec7d3fec31ab58ebf5001e219d71138385a0..0c04e2d4a6a40549c5d0d8dc2975f267438c2f57 100644 (file)
@@ -297,6 +297,51 @@ The results are then located in the `results.csv` file as a comma separated
 values list (obviously), which is the most human-friendly output format the
 CodeQL utility provides (so far).
 
+Running Coverity locally
+========================
+
+Note: this requires a Coverity license, as the public tool tarball (from [0])
+doesn't contain cov-analyze and friends, so the usefulness of this guide is
+somewhat limited.
+
+Debugging certain pesky Coverity defects can be painful, especially since the
+OSS Coverity instance has a very strict limit on how many builds we can send it
+per day/week, so if you have an access to a non-OSS Coverity license, knowing
+how to debug defects locally might come in handy.
+
+After installing the necessary tooling we need to populate the emit DB first:
+
+$ rm -rf build cov
+$ meson setup build -Dman=false
+$ cov-build --dir=./cov ninja -C build
+
+From there it depends if you're interested in a specific defect or all of them.
+For the latter run:
+
+$ cov-analyze --dir=./cov --wait-for-license
+
+If you want to debug a specific defect, telling that to cov-analyze speeds
+things up a bit:
+
+$ cov-analyze --dir=./cov --wait-for-license --disable-default --enable ASSERT_SIDE_EFFECT
+
+The final step is getting the actual report which can be generated in multiple
+formats, for example:
+
+$ cov-format-errors --dir ./cov --text-output-style multiline
+$ cov-format-errors --dir=./cov --emacs-style
+$ cov-format-errors --dir=./cov --html-output html-out
+
+Which generate a text report, an emacs-compatible text report, and an HTML
+report respectively.
+
+Other useful options for cov-format-error include --file <file> to filter out
+defects for a specific file, --checker-regex DEFECT_TYPE to filter our only a
+specific defect (if this wasn't done already by cov-analyze), and many others,
+see --help for an exhaustive list.
+
+[0] https://scan.coverity.com/download
+
 Code coverage
 =============
 
index 757c39a449db039d9e0594a46ec2a8a0de6b3c8e..93b447f583d8d10ff12a0788106acd3a299dafbd 100755 (executable)
@@ -18,6 +18,9 @@ KERNEL_OPTIONS=(
     "luks.name=$PART_UUID=$DM_NAME"
     "luks.key=$PART_UUID=/keyfile:LABEL=varcrypt_keydev"
     "luks.options=$PART_UUID=x-initrd.attach"
+    # Forward journal to console to make debugging easier (or possible at all) if we fail to bring the
+    # encrypted /var up during boot
+    "systemd.journald.forward_to_console=1"
 )
 KERNEL_APPEND+=" ${KERNEL_OPTIONS[*]}"
 QEMU_OPTIONS+=" -drive format=raw,cache=unsafe,file=${STATEDIR:?}/keydev.img"
index 613bb086ce3b630fcf189657b9f68c1d42acc93b..b93697b73c75af417d912e46bcae80a2dc27fd5c 100755 (executable)
@@ -12,6 +12,15 @@ TEST_INSTALL_VERITY_MINIMAL=1
 # shellcheck source=test/test-functions
 . "${TEST_BASE_DIR:?}/test-functions"
 
+# On Ubuntu the BPF LSM is not enabled by default, so we need to do it via the
+# kernel command line on boot
+if [ "$LOOKS_LIKE_UBUNTU" = "yes" ]; then
+    KERNEL_OPTIONS=(
+        "lsm=lockdown,capability,landlock,yama,apparmor,bpf"
+    )
+    KERNEL_APPEND+=" ${KERNEL_OPTIONS[*]}"
+fi
+
 test_require_bin mksquashfs veritysetup sfdisk
 
 test_append_files() {
index c0a9d7a53d506cb33ecdaa1c5a3a267130799990..afcb348166865a1ff2ad74bb1c4539057e64a397 100755 (executable)
@@ -9,6 +9,19 @@ NSPAWN_CREDS=(
 )
 NSPAWN_ARGUMENTS="${NSPAWN_ARGUMENTS:-} ${NSPAWN_CREDS[*]}"
 
+UNIT_CRED=$(base64 -w 0 <<EOF
+[Service]
+Type=oneshot
+ExecStart=touch /tmp/unit-cred
+EOF
+)
+DROPIN_CRED=$(base64 -w 0 <<EOF
+[Service]
+ExecStart=
+ExecStart=touch /tmp/unit-dropin
+EOF
+)
+
 QEMU_CREDS=(
     "-fw_cfg name=opt/io.systemd.credentials/myqemucredential,string=othervalue"
     "-smbios type=11,value=io.systemd.credential:smbioscredential=magicdata"
@@ -17,6 +30,8 @@ QEMU_CREDS=(
     "-smbios type=11,value=io.systemd.credential.binary:tmpfiles.extra=ZiAvdG1wL3NvdXJjZWRmcm9tY3JlZGVudGlhbCAtIC0gLSAtIHRtcGZpbGVzc2VjcmV0Cg=="
     "-smbios type=11,value=io.systemd.credential.binary:fstab.extra=aW5qZWN0ZWQgL2luamVjdGVkIHRtcGZzIFgtbW91bnQubWtkaXIgMCAwCg=="
     "-smbios type=11,value=io.systemd.credential:getty.ttys.container=idontexist"
+    "-smbios type=11,value=io.systemd.credential.binary:systemd.extra-unit.my-service.service=$UNIT_CRED"
+    "-smbios type=11,value=io.systemd.credential.binary:systemd.unit-dropin.my-service.service=$DROPIN_CRED"
 )
 QEMU_OPTIONS="${QEMU_OPTIONS:-} ${QEMU_CREDS[*]}"
 
index b9e7bdf18ad14376d3c40ff11cedbc0114b38bf3..9d4c90f88e1c7bf86eaacaf5daec52ff9777e20f 100755 (executable)
@@ -268,24 +268,15 @@ testcase_virtio_scsi_identically_named_partitions() {
     # and attach them to a virtio-scsi controller
     local qemu_opts=("-device virtio-scsi-pci,id=scsi0,num_queues=4")
     local diskpath="${TESTDIR:?}/namedpart0.img"
-    local i lodev num_disk num_part qemu_timeout
+    local i num_disk qemu_timeout
 
     if get_bool "${IS_BUILT_WITH_ASAN:=}" || ! get_bool "$QEMU_KVM"; then
         num_disk=4
-        num_part=4
     else
         num_disk=16
-        num_part=8
     fi
 
     dd if=/dev/zero of="$diskpath" bs=1M count=18
-    lodev="$(losetup --show -f -P "$diskpath")"
-    sfdisk "${lodev:?}" <<EOF
-label: gpt
-
-$(for ((i = 1; i <= num_part; i++)); do echo 'name="Hello world", size=2M'; done)
-EOF
-    losetup -d "$lodev"
 
     for ((i = 0; i < num_disk; i++)); do
         diskpath="${TESTDIR:?}/namedpart$i.img"
@@ -325,19 +316,9 @@ testcase_multipath_basic_failover() {
 
     local qemu_opts=("-device virtio-scsi-pci,id=scsi")
     local partdisk="${TESTDIR:?}/multipathpartitioned.img"
-    local image lodev nback ndisk wwn
+    local image nback ndisk wwn
 
     dd if=/dev/zero of="$partdisk" bs=1M count=16
-    lodev="$(losetup --show -f -P "$partdisk")"
-    sfdisk "${lodev:?}" <<EOF
-label: gpt
-
-name="first_partition", size=5M
-uuid="deadbeef-dead-dead-beef-000000000000", name="failover_part", size=5M
-EOF
-    udevadm settle
-    mkfs.ext4 -U "deadbeef-dead-dead-beef-111111111111" -L "failover_vol" "${lodev}p2"
-    losetup -d "$lodev"
 
     # Add 16 multipath devices, each backed by 4 paths
     for ndisk in {0..15}; do
@@ -475,18 +456,6 @@ testcase_long_sysfs_path() {
     )
 
     dd if=/dev/zero of="$testdisk" bs=1M count=64
-    lodev="$(losetup --show -f -P "$testdisk")"
-    sfdisk "${lodev:?}" <<EOF
-label: gpt
-
-name="test_swap", size=32M
-uuid="deadbeef-dead-dead-beef-000000000000", name="test_part", size=5M
-EOF
-    udevadm settle
-    mkswap -U "deadbeef-dead-dead-beef-111111111111" -L "swap_vol" "${lodev}p1"
-    mkfs.ext4 -U "deadbeef-dead-dead-beef-222222222222" -L "data_vol" "${lodev}p2"
-    losetup -d "$lodev"
-
     # Create 25 additional PCI bridges, each one connected to the previous one
     # (basically a really long extension cable), and attach a virtio drive to
     # the last one. This should force udev into attempting to create a device
index 0e12857618d94be0cfe78925ab066c5a10ff8cfe..0229d896d4168dd32eca13894443a85c352dcf28 100755 (executable)
@@ -17,7 +17,7 @@ SYSTEMD_NSPAWN="${STATEDIR:?}/run-nspawn"
 setup_nspawn_root_hook() {
     cat >"${STATEDIR:?}/run-nspawn" <<EOF
 #!/bin/bash
-exec "${TEST_BASE_DIR:?}/test-shutdown.py" -v -- "$_ORIG_NSPAWN" "\$@"
+exec "${TEST_BASE_DIR:?}/test-shutdown.py" -v -- "$_ORIG_NSPAWN" --background= "\$@"
 exit 1
 EOF
     chmod 755 "${STATEDIR:?}"/run-nspawn
diff --git a/test/TEST-77-OPENFILE/Makefile b/test/TEST-77-OPENFILE/Makefile
deleted file mode 120000 (symlink)
index e9f93b1..0000000
+++ /dev/null
@@ -1 +0,0 @@
-../TEST-01-BASIC/Makefile
\ No newline at end of file
diff --git a/test/TEST-77-OPENFILE/test.sh b/test/TEST-77-OPENFILE/test.sh
deleted file mode 100755 (executable)
index 92afa4c..0000000
+++ /dev/null
@@ -1,16 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -e
-
-TEST_DESCRIPTION="Openfile tests"
-
-# shellcheck source=test/test-functions
-. "${TEST_BASE_DIR:?}/test-functions"
-
-test_append_files() {
-    local workspace="${1:?}"
-    echo "Open" >"$workspace/test-77-open.dat"
-    echo "File" >"$workspace/test-77-file.dat"
-}
-
-do_test "$@"
diff --git a/test/test-execute/exec-personality-s390x.service b/test/test-execute/exec-personality-s390x.service
new file mode 100644 (file)
index 0000000..4545dee
--- /dev/null
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=Test for Personality=s390x
+
+[Service]
+ExecStart=sh -x -c 'c=$$(uname -m); test "$$c" = "s390x"'
+Type=oneshot
+Personality=s390x
index 0c0ffa1545edcceea60ee99a2d4dfc65286fcceb..67896fb30f6d3796862fcc96f50d8c7c56ca7186 100644 (file)
@@ -39,6 +39,8 @@ os_release=$(test -e /etc/os-release && echo /etc/os-release || echo /usr/lib/os
 # shellcheck source=/dev/null
 source "$os_release"
 [[ "$ID" == "debian" || " $ID_LIKE " == *" debian "* ]] && LOOKS_LIKE_DEBIAN=yes || LOOKS_LIKE_DEBIAN=no
+# shellcheck disable=SC2034
+[[ "$ID" == "ubuntu" ]] && LOOKS_LIKE_UBUNTU=yes || LOOKS_LIKE_UBUNTU=no
 [[ "$ID" == "arch" || " $ID_LIKE " == *" arch "* ]] && LOOKS_LIKE_ARCH=yes || LOOKS_LIKE_ARCH=no
 [[ "$ID" == "fedora" ]] && LOOKS_LIKE_FEDORA=yes || LOOKS_LIKE_FEDORA=no
 [[ " $ID_LIKE " == *" suse "* ]] && LOOKS_LIKE_SUSE=yes || LOOKS_LIKE_SUSE=no
@@ -733,6 +735,7 @@ install_verity_minimal() {
         BASICTOOLS=(
             bash
             cat
+            echo
             grep
             mount
             sleep
@@ -1114,6 +1117,9 @@ install_fs_tools() {
     # we use mkfs in system-repart tests
     image_install /sbin/mkfs.ext4
     image_install /sbin/mkfs.vfat
+
+    # we use mkswap in udev-storage tests
+    image_install /sbin/mkswap
 }
 
 install_modules() {
@@ -1494,7 +1500,7 @@ install_systemd() {
         # units using DynamicUser=yes. Do this only for services with test- prefix and a couple of
         # known-to-use DynamicUser=yes services, as setting this system-wide has many undesirable
         # side-effects, as it creates its own namespace.
-        for service in test- systemd-journal-{gatewayd,upload}; do
+        for service in capsule@ test- systemd-journal-{gatewayd,upload}; do
             mkdir -p "$initdir/etc/systemd/system/$service.service.d/"
             echo -ne "[Service]\nReadWritePaths=${BUILD_DIR:?}\n" >"$initdir/etc/systemd/system/$service.service.d/99-gcov-rwpaths-override.conf"
         done
@@ -1555,7 +1561,7 @@ install_missing_libraries() {
     local lib path
     # A number of dependencies is now optional via dlopen, so the install
     # script will not pick them up, since it looks at linkage.
-    for lib in libcryptsetup libidn libidn2 pwquality libqrencode tss2-esys tss2-rc tss2-mu tss2-tcti-device libfido2 libbpf libelf libdw xkbcommon p11-kit-1 libarchive; do
+    for lib in libcryptsetup libidn libidn2 pwquality libqrencode tss2-esys tss2-rc tss2-mu tss2-tcti-device libfido2 libbpf libelf libdw xkbcommon p11-kit-1 libarchive libgcrypt libkmod; do
         ddebug "Searching for $lib via pkg-config"
         if pkg-config --exists "$lib"; then
                 path="$(pkg-config --variable=libdir "$lib")"
index 0a5fa4d22e765ba293b94c6259d96176e8f0acd3..12254acdab91616625f0d2e4daf25f8d1874fd09 100755 (executable)
@@ -49,7 +49,7 @@ run_network_generator() {
 
     rm -rf "${WORK_DIR:?}"/*
     stderr="$WORK_DIR/stderr"
-    if ! "$GENERATOR_BIN" --root "$WORK_DIR" 2>"$stderr"; then
+    if ! SYSTEMD_LOG_LEVEL="info" "$GENERATOR_BIN" --root "$WORK_DIR" 2>"$stderr"; then
         echo >&2 "Generator failed when parsing $SYSTEMD_PROC_CMDLINE"
         cat >&2 "$stderr"
         return 1
index d5cc6d3334b883d1379f902dd156e69725393b7d..abe3fa259690ad3dcd051977822b04ea6d65f722 100644 (file)
@@ -6,3 +6,5 @@ Name=veth-peer
 IPv6AcceptRA=no
 Address=2600::1/0
 Address=192.168.5.1/24
+# To make the kernel send NA with IsRouter flag.
+IPv6Forwarding=yes
index ac50700401484aeb0195f4802be40674ec67aa3a..11502fdb53599d682f87476227accf8e9e2b7781 100644 (file)
@@ -6,8 +6,6 @@ Name=veth99
 IPv6AcceptRA=true
 
 [IPv6AcceptRA]
-Token=prefixstable:2002:da8:1::
-Token=prefixstable:2002:da8:1::,86b123b969ba4b7eb8b3d8605123525a
 # invalid tokens
 Token=prefixstable:2002:da8:1::,00000000000000000000000000000000
 Token=prefixstable:2002:da8:1::,
@@ -17,3 +15,10 @@ Token=prefixstable@
 Token=static
 Token=static:
 Token=static:::
+# valid token
+Token=prefixstable:2002:da8:1::
+Token=prefixstable:2002:da8:1::,86b123b969ba4b7eb8b3d8605123525a
+# reset token
+Token=
+# set token again
+Token=prefixstable:2002:da8:1::,86b123b969ba4b7eb8b3d8605123525a
diff --git a/test/test-network/conf/25-veth-peer-no-address.network b/test/test-network/conf/25-veth-peer-no-address.network
new file mode 100644 (file)
index 0000000..dbea3bd
--- /dev/null
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Match]
+Name=veth-peer
+
+[Network]
+IPv6AcceptRA=no
diff --git a/test/test-network/conf/persist-leases-no.conf b/test/test-network/conf/persist-leases-no.conf
new file mode 100644 (file)
index 0000000..5025265
--- /dev/null
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[DHCPServer]
+PersistLeases=no
index 866c548fbe404a4c25511f6e7bb662647d010490..cf601c84afc8b8b208cbeb3967549b12abd23cd5 100755 (executable)
@@ -60,6 +60,7 @@ networkctl_bin = shutil.which('networkctl', path=which_paths)
 resolvectl_bin = shutil.which('resolvectl', path=which_paths)
 timedatectl_bin = shutil.which('timedatectl', path=which_paths)
 udevadm_bin = shutil.which('udevadm', path=which_paths)
+test_ndisc_send = None
 build_dir = None
 source_dir = None
 
@@ -1128,6 +1129,16 @@ class Utilities():
 
         self.assertRegex(output, route_regex)
 
+    def wait_route_dropped(self, link, route_regex, table='main', ipv='', timeout_sec=100):
+        for i in range(timeout_sec):
+            if i > 0:
+                time.sleep(1)
+            output = check_output(f'ip {ipv} route show dev {link} table {table}')
+            if not re.search(route_regex, output):
+                break
+
+        self.assertNotRegex(output, route_regex)
+
     def check_netlabel(self, interface, address, label='system_u:object_r:root_t:s0'):
         if not shutil.which('selinuxenabled'):
             print('## Checking NetLabel skipped: selinuxenabled command not found.')
@@ -4612,6 +4623,23 @@ class NetworkdTCTests(unittest.TestCase, Utilities):
         print(output)
         self.assertRegex(output, 'qdisc teql1 31: root')
 
+    @expectedFailureIfModuleIsNotAvailable('sch_fq', 'sch_sfq', 'sch_tbf')
+    def test_qdisc_drop(self):
+        copy_network_unit('12-dummy.netdev', '12-dummy.network')
+        start_networkd()
+        self.wait_online('dummy98:routable')
+
+        # Test case for issue #32247 and #32254.
+        for _ in range(20):
+            check_output('tc qdisc replace dev dummy98 root fq')
+            self.assertFalse(networkd_is_failed())
+            check_output('tc qdisc replace dev dummy98 root fq pacing')
+            self.assertFalse(networkd_is_failed())
+            check_output('tc qdisc replace dev dummy98 handle 10: root tbf rate 0.5mbit burst 5kb latency 70ms peakrate 1mbit minburst 1540')
+            self.assertFalse(networkd_is_failed())
+            check_output('tc qdisc add dev dummy98 parent 10:1 handle 100: sfq')
+            self.assertFalse(networkd_is_failed())
+
 class NetworkdStateFileTests(unittest.TestCase, Utilities):
 
     def setUp(self):
@@ -5529,15 +5557,127 @@ class NetworkdRATests(unittest.TestCase, Utilities):
 
         self.check_ipv6_token_static()
 
+    def test_ndisc_redirect(self):
+        if not os.path.exists(test_ndisc_send):
+            self.skipTest(f"{test_ndisc_send} does not exist.")
+
+        copy_network_unit('25-veth.netdev', '25-ipv6-prefix.network', '25-ipv6-prefix-veth-token-static.network')
+        start_networkd()
+
+        self.check_ipv6_token_static()
+
+        # Introduce two redirect routes.
+        check_output(f'{test_ndisc_send} --interface veth-peer --type redirect --target-address 2002:da8:1:1:1a:2b:3c:4d --redirect-destination 2002:da8:1:1:1a:2b:3c:4d')
+        check_output(f'{test_ndisc_send} --interface veth-peer --type redirect --target-address 2002:da8:1:2:1a:2b:3c:4d --redirect-destination 2002:da8:1:2:1a:2b:3c:4d')
+        self.wait_route('veth99', '2002:da8:1:1:1a:2b:3c:4d proto redirect', ipv='-6', timeout_sec=10)
+        self.wait_route('veth99', '2002:da8:1:2:1a:2b:3c:4d proto redirect', ipv='-6', timeout_sec=10)
+
+        # Change the target address of the redirects.
+        check_output(f'{test_ndisc_send} --interface veth-peer --type redirect --target-address fe80::1 --redirect-destination 2002:da8:1:1:1a:2b:3c:4d')
+        check_output(f'{test_ndisc_send} --interface veth-peer --type redirect --target-address fe80::2 --redirect-destination 2002:da8:1:2:1a:2b:3c:4d')
+        self.wait_route_dropped('veth99', '2002:da8:1:1:1a:2b:3c:4d proto redirect', ipv='-6', timeout_sec=10)
+        self.wait_route_dropped('veth99', '2002:da8:1:2:1a:2b:3c:4d proto redirect', ipv='-6', timeout_sec=10)
+        self.wait_route('veth99', '2002:da8:1:1:1a:2b:3c:4d via fe80::1 proto redirect', ipv='-6', timeout_sec=10)
+        self.wait_route('veth99', '2002:da8:1:2:1a:2b:3c:4d via fe80::2 proto redirect', ipv='-6', timeout_sec=10)
+
+        # Send Neighbor Advertisement without the router flag to announce the default router is not available anymore.
+        # Then, verify that all redirect routes and the default route are dropped.
+        output = check_output('ip -6 address show dev veth-peer scope link')
+        veth_peer_ipv6ll = re.search('fe80:[:0-9a-f]*', output).group()
+        print(f'veth-peer IPv6LL address: {veth_peer_ipv6ll}')
+        check_output(f'{test_ndisc_send} --interface veth-peer --type neighbor-advertisement --target-address {veth_peer_ipv6ll} --is-router no')
+        self.wait_route_dropped('veth99', 'proto ra', ipv='-6', timeout_sec=10)
+
+        output = check_output('ip -6 route show dev veth99')
+        print(output)
+        self.assertIn('2002:da8:1:1:1a:2b:3c:4d via fe80::1 proto redirect', output)
+        self.assertIn('2002:da8:1:2:1a:2b:3c:4d via fe80::2 proto redirect', output)
+
+    def check_ndisc_mtu(self, mtu):
+        for _ in range(20):
+            output = read_ipv6_sysctl_attr('veth99', 'mtu')
+            if output == f'{mtu}':
+                break
+            time.sleep(0.5)
+        else:
+            self.fail(f'IPv6 MTU does not matches: value={output}, expected={mtu}')
+
+    def test_ndisc_mtu(self):
+        if not os.path.exists(test_ndisc_send):
+            self.skipTest(f"{test_ndisc_send} does not exist.")
+
+        copy_network_unit('25-veth.netdev',
+                          '25-veth-peer-no-address.network',
+                          '25-ipv6-prefix-veth-token-static.network')
+        start_networkd()
+        self.wait_online('veth-peer:degraded')
+
+        for _ in range(20):
+            output = read_networkd_log()
+            if 'veth99: NDISC: Started IPv6 Router Solicitation client' in output:
+                break
+            time.sleep(0.5)
+        else:
+            self.fail('sd-ndisc does not started on veth99.')
+
+        check_output(f'{test_ndisc_send} --interface veth-peer --type ra --lifetime 1hour --mtu 1400')
+        self.check_ndisc_mtu(1400)
+
+        check_output(f'{test_ndisc_send} --interface veth-peer --type ra --lifetime 1hour --mtu 1410')
+        self.check_ndisc_mtu(1410)
+
+        check_output('ip link set dev veth99 mtu 1600')
+        self.check_ndisc_mtu(1410)
+
+        check_output(f'{test_ndisc_send} --interface veth-peer --type ra --lifetime 1hour --mtu 1700')
+        self.check_ndisc_mtu(1600)
+
+        check_output('ip link set dev veth99 mtu 1800')
+        self.check_ndisc_mtu(1700)
+
     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 = networkctl_status('veth99')
+        output = check_output('ip -6 address show dev 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
+        self.assertIn('2002:da8:1:0:b47e:7975:fc7a:7d6e/64', output) # the 1st prefixstable
+        self.assertIn('2002:da8:2:0:1034:56ff:fe78:9abc/64', output) # EUI64
+
+        with open(os.path.join(network_unit_dir, '25-ipv6-prefix-veth-token-prefixstable.network'), mode='a', encoding='utf-8') as f:
+            f.write('\n[IPv6AcceptRA]\nPrefixAllowList=2002:da8:1:0::/64\n')
+
+        networkctl_reload()
+        self.wait_online('veth99:routable')
+
+        output = check_output('ip -6 address show dev veth99')
+        print(output)
+        self.assertIn('2002:da8:1:0:b47e:7975:fc7a:7d6e/64', output) # the 1st prefixstable
+        self.assertNotIn('2002:da8:2:0:1034:56ff:fe78:9abc/64', output) # EUI64
+
+        check_output('ip address del 2002:da8:1:0:b47e:7975:fc7a:7d6e/64 dev veth99')
+        check_output('ip address add 2002:da8:1:0:b47e:7975:fc7a:7d6e/64 dev veth-peer nodad')
+
+        networkctl_reconfigure('veth99')
+        self.wait_online('veth99:routable')
+
+        output = check_output('ip -6 address show dev veth99')
+        print(output)
+        self.assertNotIn('2002:da8:1:0:b47e:7975:fc7a:7d6e/64', output) # the 1st prefixstable
+        self.assertIn('2002:da8:1:0:da5d:e50a:43fd:5d0f/64', output) # the 2nd prefixstable
+
+        check_output('ip address del 2002:da8:1:0:da5d:e50a:43fd:5d0f/64 dev veth99')
+        check_output('ip address add 2002:da8:1:0:da5d:e50a:43fd:5d0f/64 dev veth-peer nodad')
+
+        networkctl_reconfigure('veth99')
+        self.wait_online('veth99:routable')
+
+        output = check_output('ip -6 address show dev veth99')
+        print(output)
+        self.assertNotIn('2002:da8:1:0:b47e:7975:fc7a:7d6e/64', output) # the 1st prefixstable
+        self.assertNotIn('2002:da8:1:0:da5d:e50a:43fd:5d0f/64', output) # the 2nd prefixstable
+        self.assertIn('2002:da8:1:0:c7e4:77ec:eb31:1b0d/64', output) # the 3rd prefixstable
 
     def test_ipv6_token_prefixstable_without_address(self):
         copy_network_unit('25-veth.netdev', '25-ipv6-prefix.network', '25-ipv6-prefix-veth-token-prefixstable-without-address.network')
@@ -5549,15 +5689,6 @@ class NetworkdRATests(unittest.TestCase, Utilities):
         self.assertIn('2002:da8:1:0:b47e:7975:fc7a:7d6e', output)
         self.assertIn('2002:da8:2:0:f689:561a:8eda:7443', output)
 
-    def check_router_hop_limit(self, hop_limit):
-        self.wait_route('client', rf'default via fe80::1034:56ff:fe78:9a99 proto ra .* hoplimit {hop_limit}', ipv='-6', timeout_sec=10)
-
-        output = check_output('ip -6 route show dev client default via fe80::1034:56ff:fe78:9a99')
-        print(output)
-        self.assertIn(f'hoplimit {hop_limit}', output)
-
-        self.check_ipv6_sysctl_attr('client', 'hop_limit', f'{hop_limit}')
-
     def test_router_hop_limit(self):
         copy_network_unit('25-veth-client.netdev',
                           '25-veth-router.netdev',
@@ -5567,18 +5698,24 @@ class NetworkdRATests(unittest.TestCase, Utilities):
                           '25-veth-router-hop-limit.network',
                           '25-bridge99.network')
         start_networkd()
-        self.wait_online('client-p:enslaved',
+        self.wait_online('client:routable', 'client-p:enslaved',
                          'router:degraded', 'router-p:enslaved',
                          'bridge99:routable')
 
-        self.check_router_hop_limit(42)
+        self.check_ipv6_sysctl_attr('client', 'hop_limit', '42')
 
         with open(os.path.join(network_unit_dir, '25-veth-router-hop-limit.network'), mode='a', encoding='utf-8') as f:
             f.write('\n[IPv6SendRA]\nHopLimit=43\n')
 
         networkctl_reload()
 
-        self.check_router_hop_limit(43)
+        for _ in range(20):
+            output = read_ipv6_sysctl_attr('client', 'hop_limit')
+            if output == '43':
+                break
+            time.sleep(0.5)
+
+        self.check_ipv6_sysctl_attr('client', 'hop_limit', '43')
 
     def test_router_preference(self):
         copy_network_unit('25-veth-client.netdev',
@@ -5721,11 +5858,7 @@ class NetworkdDHCPServerTests(unittest.TestCase, Utilities):
     def tearDown(self):
         tear_down_common()
 
-    def test_dhcp_server(self):
-        copy_network_unit('25-veth.netdev', '25-dhcp-client.network', '25-dhcp-server.network')
-        start_networkd()
-        self.wait_online('veth99:routable', 'veth-peer:routable')
-
+    def check_dhcp_server(self, persist_leases=True):
         output = networkctl_status('veth99')
         print(output)
         self.assertRegex(output, r'Address: 192.168.5.[0-9]* \(DHCP4 via 192.168.5.1\)')
@@ -5737,6 +5870,19 @@ class NetworkdDHCPServerTests(unittest.TestCase, Utilities):
         print(output)
         self.assertRegex(output, "Offered DHCP leases: 192.168.5.[0-9]*")
 
+        if persist_leases:
+            with open('/var/lib/systemd/network/dhcp-server-lease/veth-peer', encoding='utf-8') as f:
+                check_json(f.read())
+        else:
+            self.assertFalse(os.path.exists('/var/lib/systemd/network/dhcp-server-lease/veth-peer'))
+
+    def test_dhcp_server(self):
+        copy_network_unit('25-veth.netdev', '25-dhcp-client.network', '25-dhcp-server.network')
+        start_networkd()
+        self.wait_online('veth99:routable', 'veth-peer:routable')
+
+        self.check_dhcp_server()
+
         networkctl_reconfigure('veth-peer')
         self.wait_online('veth-peer:routable')
 
@@ -5748,6 +5894,22 @@ class NetworkdDHCPServerTests(unittest.TestCase, Utilities):
         else:
             self.fail()
 
+    def test_dhcp_server_persist_leases_no(self):
+        copy_networkd_conf_dropin('persist-leases-no.conf')
+        copy_network_unit('25-veth.netdev', '25-dhcp-client.network', '25-dhcp-server.network')
+        start_networkd()
+        self.wait_online('veth99:routable', 'veth-peer:routable')
+
+        self.check_dhcp_server(persist_leases=False)
+
+        remove_networkd_conf_dropin('persist-leases-no.conf')
+        with open(os.path.join(network_unit_dir, '25-dhcp-server.network'), mode='a', encoding='utf-8') as f:
+            f.write('[DHCPServer]\nPersistLeases=no')
+        restart_networkd()
+        self.wait_online('veth99:routable', 'veth-peer:routable')
+
+        self.check_dhcp_server(persist_leases=False)
+
     def test_dhcp_server_null_server_address(self):
         copy_network_unit('25-veth.netdev', '25-dhcp-client.network', '25-dhcp-server-null-server-address.network')
         start_networkd()
@@ -6771,6 +6933,50 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
         check(self, True, False)
         check(self, False, True)
         check(self, False, False)
+    
+    def test_dhcp_client_default_use_domains(self):        
+        def check(self, ipv4, ipv6):
+            mkdir_p(networkd_conf_dropin_dir)
+            with open(os.path.join(networkd_conf_dropin_dir, 'default_use_domains.conf'), mode='w', encoding='utf-8') as f:
+                f.write('[DHCPv4]\nUseDomains=')
+                f.write('yes\n' if ipv4 else 'no\n')
+                f.write('[DHCPv6]\nUseDomains=')
+                f.write('yes\n' if ipv6 else 'no\n')
+            
+            restart_networkd()
+            self.wait_online('veth-peer:carrier')
+            start_dnsmasq('--dhcp-option=option:dns-server,192.168.5.1',
+                          '--dhcp-option=option6:dns-server,[2600::1]',
+                          '--dhcp-option=option:domain-search,example.com',
+                          '--dhcp-option=option6:domain-search,example.com')
+
+            self.wait_online('veth99:routable')
+
+            # link becomes 'routable' when at least one protocol provide an valid address. Hence, we need to explicitly wait for both addresses.
+            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')
+
+            for _ in range(20):
+                output = resolvectl('domain', 'veth99')
+                if ipv4 or ipv6:
+                    if 'example.com' in output:
+                        break
+                else:
+                    if 'example.com' not in output:
+                        break
+                time.sleep(0.5)
+            else:
+                print(output)
+                self.fail('unexpected domain setting in resolved...')
+            
+            stop_dnsmasq()
+            remove_networkd_conf_dropin('default_use_domains.conf')
+
+        copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client.network', copy_dropins=False)
+        check(self, True, True)
+        check(self, True, False)
+        check(self, False, True)
+        check(self, False, False)
 
     def test_dhcp_client_use_captive_portal(self):
         def check(self, ipv4, ipv6):
@@ -7587,6 +7793,11 @@ if __name__ == '__main__':
     udevadm_cmd = valgrind_cmd.split() + [udevadm_bin]
     wait_online_cmd = valgrind_cmd.split() + [wait_online_bin]
 
+    if build_dir:
+        test_ndisc_send = os.path.normpath(os.path.join(build_dir, 'test-ndisc-send'))
+    else:
+        test_ndisc_send = '/usr/lib/tests/test-ndisc-send'
+
     if asan_options:
         env.update({'ASAN_OPTIONS': asan_options})
     if lsan_options:
index f496122f80ce5f546f51522ca100a6109d9a2609..d19a03742c246f6c5cd2f2b40da28be601f38d21 100755 (executable)
@@ -12,13 +12,19 @@ import pexpect
 
 
 def run(args):
-
     ret = 1
     logger = logging.getLogger("test-shutdown")
+    logfile = None
+
+    if args.logfile:
+        logger.debug("Logging pexpect IOs to %s", args.logfile)
+        logfile = open(args.logfile, 'w')
+    elif args.verbose:
+        logfile = sys.stdout
 
     logger.info("spawning test")
-    console = pexpect.spawn(args.command, args.arg, env={
-            "TERM": "linux",
+    console = pexpect.spawn(args.command, args.arg, logfile=logfile, env={
+            "TERM": "dumb",
         }, encoding='utf-8', timeout=60)
 
     logger.debug("child pid %d", console.pid)
@@ -27,12 +33,6 @@ def run(args):
         logger.info("waiting for login prompt")
         console.expect('H login: ', 10)
 
-        if args.logfile:
-            logger.debug("Logging pexpect IOs to %s", args.logfile)
-            console.logfile = open(args.logfile, 'w')
-        elif args.verbose:
-            console.logfile = sys.stdout
-
         logger.info("log in and start screen")
         console.sendline('root')
         console.expect('bash.*# ', 10)
@@ -42,6 +42,10 @@ def run(args):
         console.send('c')
         console.expect('screen1 ', 10)
 
+        logger.info('wait for the machine to fully boot')
+        console.sendline('systemctl is-system-running --wait')
+        console.expect(r'\b(running|degraded)\b', 60)
+
 #        console.interact()
 
         console.sendline('tty')
similarity index 56%
rename from test/units/testsuite-77-server.socket
rename to test/testsuite-23.units/testsuite-23-openfile-server.socket
index 4305077501057cede6554b5f5bb7ad78bc1248c3..c8f759584accaf4bc66c8b1c30d0126c27708cc3 100644 (file)
@@ -1,5 +1,5 @@
 [Unit]
-Description=TEST-77-OPENFILE server socket
+Description=OpenFile= test server socket
 
 [Socket]
 ListenStream=/tmp/test.sock
similarity index 66%
rename from test/units/testsuite-77-server@.service
rename to test/testsuite-23.units/testsuite-23-openfile-server@.service
index 8e99ac83340e9bd34a68529d1e82106ce560f0bc..a57c804c43d84707554f5d3c3c6455f930e761b2 100644 (file)
@@ -1,5 +1,5 @@
 [Unit]
-Description=TEST-77-OPENFILE server
+Description=OpenFile= test server service
 
 [Service]
 ExecStart=echo "Socket"
index 4c6c0afa0e9768369bb75177c330614c4f26f098..5e2943758a1152b9d785a4d7ba164c348f1ed3e8 100755 (executable)
@@ -9,5 +9,9 @@ set -o pipefail
 # Here, the redundant '[ ]' in the pattern is required in order not to match the logged command itself.
 (! journalctl -q -o short-monotonic --grep 'Warning: cannot close sd-bus connection[ ].*after fork' >>/failed)
 
+# Check if sd-executor doesn't complain about not being able to (de)serialize stuff
+(! journalctl -q -o short-monotonic --grep "[F]ailed to parse serialized line" >>/failed)
+(! journalctl -q -o short-monotonic --grep "[F]ailed to (de)?serialize \w+" >>/failed)
+
 systemctl poweroff --no-block
 exit 0
index a58d76e2ee72a249219a03cdbfa39c8d090daf7c..9c7a3d05bb3dc197be220b152e8c0e0f3014575a 100755 (executable)
@@ -1,13 +1,28 @@
 #!/usr/bin/env bash
 # SPDX-License-Identifier: LGPL-2.1-or-later
 set -eux
-# pipefail is disabled intentionally, as `curl | grep -q` is very SIGPIPE happy
+set -o pipefail
 
 if [[ ! -x /usr/lib/systemd/systemd-journal-gatewayd ]]; then
     echo "Built without systemd-journal-gatewayd support, skipping the test"
     exit 0
 fi
 
+LOG_FILE="$(mktemp)"
+
+at_exit() {
+    if [[ $? -ne 0 ]]; then
+        # The $LOG_FILE is potentially huge (as it might be a full copy of the current journal), so let's
+        # dump it at debug level under a specific syslog tag, so it's clearly separated from the actual test
+        # journal; things get very confusing otherwise.
+        systemd-cat -t log-file-dump -p debug cat "$LOG_FILE"
+    fi
+
+    rm -f "$LOG_FILE"
+}
+
+trap at_exit EXIT
+
 TEST_MESSAGE="-= This is a test message $RANDOM =-"
 TEST_TAG="$(systemd-id128 new)"
 
@@ -27,38 +42,56 @@ systemctl start systemd-journal-gatewayd.socket
 
 # /browse
 # We should get redirected to /browse by default
-curl -Lfs http://localhost:19531 | grep -qF "<title>Journal</title>"
-curl -Lfs http://localhost:19531/browse | grep -qF "<title>Journal</title>"
-(! curl -Lfs http://localhost:19531/foo/bar/baz)
-(! curl -Lfs http://localhost:19531/foo/../../../bar/../baz)
+curl -LSfs http://localhost:19531 >"$LOG_FILE"
+grep -qF "<title>Journal</title>" "$LOG_FILE"
+curl -LSfs http://localhost:19531/browse >"$LOG_FILE"
+grep -qF "<title>Journal</title>" "$LOG_FILE"
+(! curl -LSfs http://localhost:19531/foo/bar/baz)
+(! curl -LSfs http://localhost:19531/foo/../../../bar/../baz)
 
 # /entries
 # Accept: text/plain should be the default
-curl -Lfs http://localhost:19531/entries | \
-    grep -qE " $TEST_TAG\[[0-9]+\]: $TEST_MESSAGE"
-curl -Lfs --header "Accept: text/plain" http://localhost:19531/entries | \
-    grep -qE " $TEST_TAG\[[0-9]+\]: $TEST_MESSAGE"
-curl -Lfs --header "Accept: application/json" http://localhost:19531/entries | \
-    jq -se ".[] | select(.MESSAGE == \"$TEST_MESSAGE\")"
-curl -Lfs --header "Accept: application/json" http://localhost:19531/entries?boot | \
-    jq -se ".[] | select(.MESSAGE == \"$TEST_MESSAGE\")"
-curl -Lfs --header "Accept: application/json" http://localhost:19531/entries?SYSLOG_IDENTIFIER="$TEST_TAG" | \
-    jq -se "length == 1 and select(.[].MESSAGE == \"$TEST_MESSAGE\")"
+curl -LSfs http://localhost:19531/entries >"$LOG_FILE"
+grep -qE " $TEST_TAG\[[0-9]+\]: $TEST_MESSAGE" "$LOG_FILE"
+curl -LSfs --header "Accept: text/plain" http://localhost:19531/entries >"$LOG_FILE"
+grep -qE " $TEST_TAG\[[0-9]+\]: $TEST_MESSAGE" "$LOG_FILE"
+curl -LSfs --header "Accept: application/json" http://localhost:19531/entries >"$LOG_FILE"
+jq -se ".[] | select(.MESSAGE == \"$TEST_MESSAGE\")" "$LOG_FILE"
+curl -LSfs --header "Accept: application/json" http://localhost:19531/entries?boot >"$LOG_FILE"
+jq -se ".[] | select(.MESSAGE == \"$TEST_MESSAGE\")" "$LOG_FILE"
+curl -LSfs --header "Accept: application/json" http://localhost:19531/entries?SYSLOG_IDENTIFIER="$TEST_TAG" >"$LOG_FILE"
+jq -se "length == 1 and select(.[].MESSAGE == \"$TEST_MESSAGE\")" "$LOG_FILE"
 # Show 10 entries starting from $BOOT_CURSOR, skip the first 5
-curl -Lfs --header "Accept: application/json" --header "Range: entries=$BOOT_CURSOR:5:10" http://localhost:19531/entries | \
-    jq -se "length == 10"
+curl -LSfs \
+     --header "Accept: application/json" \
+     --header "Range: entries=$BOOT_CURSOR:5:10" \
+     http://localhost:19531/entries >"$LOG_FILE"
+jq -se "length == 10" "$LOG_FILE"
 # Check if the specified cursor refers to an existing entry and return just that entry
-curl -Lfs --header "Accept: application/json" --header "Range: entries=$TEST_CURSOR" http://localhost:19531/entries?discrete | \
-    jq -se "length == 1 and select(.[].MESSAGE == \"$TEST_MESSAGE\")"
+curl -LSfs \
+     --header "Accept: application/json" \
+     --header "Range: entries=$TEST_CURSOR" \
+     http://localhost:19531/entries?discrete >"$LOG_FILE"
+jq -se "length == 1 and select(.[].MESSAGE == \"$TEST_MESSAGE\")" "$LOG_FILE"
 # Check entry is present (resp. absent) when filtering by timestamp
-curl -Lfs --header "Range: realtime=$BEFORE_TIMESTAMP:" http://localhost:19531/entries?SYSLOG_IDENTIFIER="$TEST_TAG" | \
-    grep -qE " $TEST_TAG\[[0-9]+\]: $TEST_MESSAGE"
-curl -Lfs --header "Range: realtime=:$AFTER_TIMESTAMP" http://localhost:19531/entries?SYSLOG_IDENTIFIER="$TEST_TAG" | \
-    grep -qE " $TEST_TAG\[[0-9]+\]: $TEST_MESSAGE"
-curl -Lfs --header "Accept: application/json" --header "Range: realtime=:$BEFORE_TIMESTAMP" http://localhost:19531/entries?SYSLOG_IDENTIFIER="$TEST_TAG" | \
-    jq -se "length == 0"
-curl -Lfs --header "Accept: application/json" --header "Range: realtime=$AFTER_TIMESTAMP:" http://localhost:19531/entries?SYSLOG_IDENTIFIER="$TEST_TAG" | \
-    jq -se "length == 0"
+curl -LSfs \
+     --header "Range: realtime=$BEFORE_TIMESTAMP:" \
+     http://localhost:19531/entries?SYSLOG_IDENTIFIER="$TEST_TAG" >"$LOG_FILE"
+grep -qE " $TEST_TAG\[[0-9]+\]: $TEST_MESSAGE" "$LOG_FILE"
+curl -LSfs \
+     --header "Range: realtime=:$AFTER_TIMESTAMP" \
+     http://localhost:19531/entries?SYSLOG_IDENTIFIER="$TEST_TAG" >"$LOG_FILE"
+grep -qE " $TEST_TAG\[[0-9]+\]: $TEST_MESSAGE" "$LOG_FILE"
+curl -LSfs \
+     --header "Accept: application/json" \
+     --header "Range: realtime=:$BEFORE_TIMESTAMP" \
+     http://localhost:19531/entries?SYSLOG_IDENTIFIER="$TEST_TAG" >"$LOG_FILE"
+jq -se "length == 0" "$LOG_FILE"
+curl -LSfs \
+     --header "Accept: application/json" \
+     --header "Range: realtime=$AFTER_TIMESTAMP:" \
+     http://localhost:19531/entries?SYSLOG_IDENTIFIER="$TEST_TAG" >"$LOG_FILE"
+jq -se "length == 0" "$LOG_FILE"
 # Check positive and negative skip when filtering by timestamp
 echo "-= This is a second test message =-" | systemd-cat -t "$TEST_TAG"
 journalctl --sync
@@ -67,19 +100,25 @@ echo "-= This is a third test message =-" | systemd-cat -t "$TEST_TAG"
 journalctl --sync
 sleep 1
 END_TIMESTAMP="$(date +%s)"
-curl -Lfs --header "Accept: application/json" --header "Range: realtime=$BEFORE_TIMESTAMP::1:1" http://localhost:19531/entries?SYSLOG_IDENTIFIER="$TEST_TAG" | \
-    jq -se "length == 1 and select(.[].__CURSOR == \"$TEST2_CURSOR\")"
-curl -Lfs --header "Accept: application/json" --header "Range: realtime=$END_TIMESTAMP::-1:1" http://localhost:19531/entries?SYSLOG_IDENTIFIER="$TEST_TAG" | \
-    jq -se "length == 1 and select(.[].__CURSOR == \"$TEST2_CURSOR\")"
+curl -LSfs \
+     --header "Accept: application/json" \
+     --header "Range: realtime=$BEFORE_TIMESTAMP::1:1" \
+     http://localhost:19531/entries?SYSLOG_IDENTIFIER="$TEST_TAG" >"$LOG_FILE"
+jq -se "length == 1 and select(.[].__CURSOR == \"$TEST2_CURSOR\")" "$LOG_FILE"
+curl -LSfs \
+     --header "Accept: application/json" \
+     --header "Range: realtime=$END_TIMESTAMP::-1:1" \
+     http://localhost:19531/entries?SYSLOG_IDENTIFIER="$TEST_TAG" >"$LOG_FILE"
+jq -se "length == 1 and select(.[].__CURSOR == \"$TEST2_CURSOR\")" "$LOG_FILE"
 
 # No idea how to properly parse this (jq won't cut it), so let's at least do some sanity checks that every
 # line is either empty or begins with data:
-curl -Lfs --header "Accept: text/event-stream" http://localhost:19531/entries | \
-    awk '!/^(data: \{.+\}|)$/ { exit 1; }'
+curl -LSfs --header "Accept: text/event-stream" http://localhost:19531/entries >"$LOG_FILE"
+awk '!/^(data: \{.+\}|)$/ { exit 1; }' "$LOG_FILE"
 # Same thing as journalctl --output=export
 mkdir /tmp/remote-journal
-curl -Lfs --header "Accept: application/vnd.fdo.journal" http://localhost:19531/entries | \
-    /usr/lib/systemd/systemd-journal-remote --output=/tmp/remote-journal/system.journal --split-mode=none -
+curl -LSfs --header "Accept: application/vnd.fdo.journal" http://localhost:19531/entries >"$LOG_FILE"
+/usr/lib/systemd/systemd-journal-remote --output=/tmp/remote-journal/system.journal --split-mode=none "$LOG_FILE"
 journalctl --directory=/tmp/remote-journal -t "$TEST_TAG" --grep "$TEST_MESSAGE"
 rm -rf /tmp/remote-journal/*
 # Let's do the same thing again, but let systemd-journal-remote spawn curl itself
@@ -90,13 +129,15 @@ journalctl --directory=/tmp/remote-journal -t "$TEST_TAG" --grep "$TEST_MESSAGE"
 rm -rf /tmp/remote-journal
 
 # /machine
-curl -Lfs http://localhost:19531/machine | jq
+curl -LSfs http://localhost:19531/machine >"$LOG_FILE"
+jq . "$LOG_FILE"
 
 # /fields
-curl -Lfs http://localhost:19531/fields/MESSAGE | grep -qE -- "$TEST_MESSAGE"
-curl -Lfs http://localhost:19531/fields/_TRANSPORT
-(! curl -Lfs http://localhost:19531/fields)
-(! curl -Lfs http://localhost:19531/fields/foo-bar-baz)
+curl -LSfs http://localhost:19531/fields/MESSAGE >"$LOG_FILE"
+grep -qE -- "$TEST_MESSAGE" "$LOG_FILE"
+curl -LSfs http://localhost:19531/fields/_TRANSPORT
+(! curl -LSfs http://localhost:19531/fields)
+(! curl -LSfs http://localhost:19531/fields/foo-bar-baz)
 
 systemctl stop systemd-journal-gatewayd.{socket,service}
 
@@ -133,13 +174,15 @@ GATEWAYD_PID=$!
 sleep 1
 
 # Do a limited set of tests, since the underlying code should be the same past the HTTPS transport
-curl -Lfsk https://localhost:19531 | grep -qF "<title>Journal</title>"
-curl -Lfsk https://localhost:19531/entries | \
-    grep -qE " $TEST_TAG\[[0-9]+\]: $TEST_MESSAGE"
-curl -Lfsk --header "Accept: application/json" https://localhost:19531/entries | \
-    jq -se ".[] | select(.MESSAGE == \"$TEST_MESSAGE\")"
-curl -Lfsk https://localhost:19531/machine | jq
-curl -Lfsk https://localhost:19531/fields/_TRANSPORT
+curl -LSfsk https://localhost:19531 >"$LOG_FILE"
+grep -qF "<title>Journal</title>" "$LOG_FILE"
+curl -LSfsk https://localhost:19531/entries >"$LOG_FILE"
+grep -qE " $TEST_TAG\[[0-9]+\]: $TEST_MESSAGE" "$LOG_FILE"
+curl -LSfsk --header "Accept: application/json" https://localhost:19531/entries >"$LOG_FILE"
+jq -se ".[] | select(.MESSAGE == \"$TEST_MESSAGE\")" "$LOG_FILE"
+curl -LSfsk https://localhost:19531/machine >"$LOG_FILE"
+jq . "$LOG_FILE"
+curl -LSfsk https://localhost:19531/fields/_TRANSPORT
 
 kill "$GATEWAYD_PID"
 
@@ -154,25 +197,27 @@ systemd-run --unit="test-gatewayd.service" --socket-property="ListenStream=19531
 # See: https://github.com/systemd/systemd/issues/9858
 OUT="$(mktemp)"
 for _ in {0..4}; do
-    curl --fail-with-body -d "please process this🐱 $RANDOM" -L http://localhost:19531/upload | tee "$OUT"
+    (! curl --fail-with-body -d "please process this🐱 $RANDOM" -L http://localhost:19531/upload | tee "$OUT")
     (! grep '[^[:print:]]' "$OUT")
 done
-curl --fail-with-body --upload-file "$GATEWAYD_FILE" -L http://localhost:19531/upload | tee "$OUT"
+(! curl --fail-with-body --upload-file "$GATEWAYD_FILE" -L http://localhost:19531/upload | tee "$OUT")
 (! grep '[^[:print:]]' "$OUT")
 rm -rf "$OUT"
 
-curl -Lfs http://localhost:19531/browse | grep -qF "<title>Journal</title>"
+curl -LSfs http://localhost:19531/browse >"$LOG_FILE"
+grep -qF "<title>Journal</title>" "$LOG_FILE"
 # Nuke the file behind the /browse endpoint
 mv /usr/share/systemd/gatewayd/browse.html /usr/share/systemd/gatewayd/browse.html.bak
 (! curl --fail-with-body -L http://localhost:19531/browse)
 mv /usr/share/systemd/gatewayd/browse.html.bak /usr/share/systemd/gatewayd/browse.html
-curl -Lfs http://localhost:19531/browse | grep -qF "<title>Journal</title>"
+curl -LSfs http://localhost:19531/browse >"$LOG_FILE"
+grep -qF "<title>Journal</title>" "$LOG_FILE"
 
 # Nuke the journal file
 mv "$GATEWAYD_FILE" "$GATEWAYD_FILE.bak"
 (! curl --fail-with-body -L http://localhost:19531/fields/_PID)
 mv "$GATEWAYD_FILE.bak" "$GATEWAYD_FILE"
-curl -Lfs http://localhost:19531/fields/_PID
+curl -LSfs http://localhost:19531/fields/_PID
 
 systemctl stop test-gatewayd.{socket,service}
 rm -f "$GATEWAYD_FILE"
index 5c6b4cee7908e124dcabc2d172390cc4110d5961..58d9bea195f5ac2ff2995bb9b00fb74d7d86f3bf 100755 (executable)
@@ -13,7 +13,7 @@ fi
 if [[ "$(systemctl show -P InitRDTimestampMonotonic)" -eq 0 ]]; then
     echo "systemd didn't run in the initrd, skipping the test"
     touch /skipped
-    exit 0
+    exit 77
 fi
 
 # We should've created a mount under /run in initrd (see the other half of the test)
index f229dcf25f080bbdad323efb47a566d727830025..a2b8b14d0c2a564c05a85a3e4b7e97869351bf22 100755 (executable)
@@ -45,8 +45,8 @@ udevadm control -S
 udevadm control -R
 udevadm control -p HELLO=world
 udevadm control -m 42
-udevadm control --ping
-udevadm control -t 5
+udevadm control --ping -t 5
+udevadm control --load-credentials
 udevadm control -h
 
 udevadm info /dev/null
diff --git a/test/units/testsuite-17.credentials.sh b/test/units/testsuite-17.credentials.sh
new file mode 100755 (executable)
index 0000000..cbdc570
--- /dev/null
@@ -0,0 +1,33 @@
+#!/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/udev.*
+    rm -f /run/udev/udev.conf.d/*
+    rm -f /run/udev/rules.d/*
+    rm -rf /run/systemd/system/systemd-udev-load-credentials.service.d
+}
+
+trap at_exit EXIT
+
+mkdir -p /run/credstore
+cat > /run/credstore/udev.conf.50-testme <<EOF
+udev_log=debug
+EOF
+cat > /run/credstore/udev.rules.50-testme <<EOF
+SUBSYSTEM=="net", OPTIONS="log_level=debug"
+EOF
+
+systemctl edit systemd-udev-load-credentials.service --stdin --drop-in=50-testme.conf <<EOF
+[Service]
+LoadCredential=udev.conf.50-testme
+LoadCredential=udev.rules.50-testme
+EOF
+
+systemctl restart systemd-udev-load-credentials.service
+
+diff /run/credstore/udev.conf.50-testme /run/udev/udev.conf.d/50-testme.conf
+diff /run/credstore/udev.rules.50-testme /run/udev/rules.d/50-testme.rules
index a43ad22b4342d7d397a5bdfa66049c3e0d1cfc7d..517cc3f7d213e9fc60cdbb684c1efe9def822e8d 100755 (executable)
@@ -122,12 +122,12 @@ 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.
+# check that test command does not 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 "HOGE2=" "$output"
 assert_not_in "BAR=" "$output"
-assert_in "BAR2=baz2" "$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"
@@ -165,6 +165,7 @@ udevadm control --reload
 output=$(udevadm test --action add /sys/class/net/test1)
 assert_in "LINK_VERSION=$(uname -r)" "$output"
 
+udevadm trigger --settle --action add /sys/class/net/test1
 output=$(udevadm info --query property /sys/class/net/test1)
 assert_in "LINK_VERSION=$(uname -r)" "$output"
 
@@ -185,6 +186,7 @@ assert_in "IFINDEX=" "$output"
 assert_not_in "IFINDEX=bar" "$output"
 assert_in "DEVPATH=" "$output"
 
+udevadm trigger --settle --action add /sys/class/net/test1
 output=$(udevadm info --query property /sys/class/net/test1)
 assert_not_in "ACTION=foo" "$output"
 assert_in "IFINDEX=" "$output"
diff --git a/test/units/testsuite-23-openfile-child.sh b/test/units/testsuite-23-openfile-child.sh
new file mode 100755 (executable)
index 0000000..4828b9d
--- /dev/null
@@ -0,0 +1,15 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+assert_eq "$LISTEN_FDS" "$1"
+assert_eq "$LISTEN_FDNAMES" "$2"
+
+for ((i = 3; i < 3 + LISTEN_FDS; i++)); do
+    read -r -u "$i" text
+    assert_eq "$text" "${!i}" # Dereference $i to get i'th arg
+done
diff --git a/test/units/testsuite-23.openfile.sh b/test/units/testsuite-23.openfile.sh
new file mode 100755 (executable)
index 0000000..6108161
--- /dev/null
@@ -0,0 +1,55 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+at_exit() {
+    set +e
+
+    rm -rf /tmp/test-open-file/
+}
+
+trap at_exit EXIT
+
+systemctl log-level debug
+
+# Existing files
+
+mkdir /tmp/test-open-file
+echo "Open" >'/tmp/test-open-file/open.txt'
+echo "File" >'/tmp/test-open-file/file:colon.txt'
+
+systemd-run -p DynamicUser=yes -p EnvironmentFile=-/usr/lib/systemd/systemd-asan-env \
+            -p OpenFile='/tmp/test-open-file/open.txt::read-only' \
+            -p OpenFile='/tmp/test-open-file/file\x3Acolon.txt:colon' \
+            -p RemainAfterExit=yes \
+            --unit=test-23-openfile-existing.service \
+            --service-type=oneshot \
+            /usr/lib/systemd/tests/testdata/units/testsuite-23-openfile-child.sh 2 "open.txt:colon" "Open" "File"
+
+cmp <(systemctl show -p OpenFile test-23-openfile-existing.service) <<EOF
+OpenFile=/tmp/test-open-file/open.txt::read-only
+OpenFile=/tmp/test-open-file/file\\x3acolon.txt:colon
+EOF
+
+systemctl stop test-23-openfile-existing.service
+
+# Sockets
+
+systemctl start testsuite-23-openfile-server.socket
+
+systemd-run -p OpenFile=/tmp/test.sock:socket:read-only \
+            --wait \
+            /usr/lib/systemd/tests/testdata/units/testsuite-23-openfile-child.sh 1 "socket" "Socket"
+
+systemctl stop testsuite-23-openfile-server.socket
+
+# Ignore when missing
+
+assert_rc 202 systemd-run -p OpenFile=/run/missing/foo:missing-file:read-only --wait true
+assert_rc 0 systemd-run -p OpenFile=/run/missing/foo:missing-file:read-only,graceful --wait true
+
+systemctl log-level info
index d40cef2551a2407396fb02a22e33bcb617a3558d..7af0ed45786bbe4d6c7f4d0f06372ad9c8e9dc74 100755 (executable)
@@ -139,6 +139,19 @@ grep -q -F "LogExtraFields=PORTABLE_EXTENSION_NAME_AND_VERSION=app" /run/systemd
 
 portablectl detach --now --runtime --extension /usr/share/app0.raw /usr/share/minimal_1.raw app0
 
+# Ensure versioned images are accepted without needing to use --force to override the extension-release
+# matching
+
+cp /usr/share/app0.raw /tmp/app0_1.0.raw
+portablectl "${ARGS[@]}" attach --now --runtime --extension /tmp/app0_1.0.raw /usr/share/minimal_0.raw app0
+
+systemctl is-active app0.service
+status="$(portablectl is-attached --extension app0_1 minimal_0)"
+[[ "${status}" == "running-runtime" ]]
+
+portablectl detach --now --runtime --extension /tmp/app0_1.0.raw /usr/share/minimal_1.raw app0
+rm -f /tmp/app0_1.0.raw
+
 portablectl "${ARGS[@]}" attach --now --runtime --extension /usr/share/app1.raw /usr/share/minimal_0.raw app1
 
 systemctl is-active app1.service
@@ -211,6 +224,36 @@ test -f /run/systemd/system.attached/app0.service
 test -L /run/systemd/system.attached/app0.service.d/10-profile.conf
 portablectl detach --runtime --extension /usr/share/app0.raw /usr/share/minimal_0.raw app0
 
+# Ensure that when two portables share the same base image, removing one doesn't remove the other too
+
+portablectl "${ARGS[@]}" attach --runtime --extension /usr/share/app0.raw /usr/share/minimal_0.raw app0
+portablectl "${ARGS[@]}" attach --runtime --extension /usr/share/app1.raw /usr/share/minimal_0.raw app1
+
+status="$(portablectl is-attached --extension app0 minimal_0)"
+[[ "${status}" == "attached-runtime" ]]
+status="$(portablectl is-attached --extension app1 minimal_0)"
+[[ "${status}" == "attached-runtime" ]]
+
+(! portablectl detach --runtime /usr/share/minimal_0.raw app)
+
+status="$(portablectl is-attached --extension app0 minimal_0)"
+[[ "${status}" == "attached-runtime" ]]
+status="$(portablectl is-attached --extension app1 minimal_0)"
+[[ "${status}" == "attached-runtime" ]]
+
+# Ensure 'portablectl list' shows the correct status for both images
+portablectl list
+portablectl list | grep -F "minimal_0" | grep -q -F "attached-runtime"
+portablectl list | grep -F "app0" | grep -q -F "attached-runtime"
+portablectl list | grep -F "app1" | grep -q -F "attached-runtime"
+
+portablectl detach --runtime --extension /usr/share/app0.raw /usr/share/minimal_0.raw app
+
+status="$(portablectl is-attached --extension app1 minimal_0)"
+[[ "${status}" == "attached-runtime" ]]
+
+portablectl detach --runtime --extension /usr/share/app1.raw /usr/share/minimal_0.raw app
+
 # portablectl also works with directory paths rather than images
 
 mkdir /tmp/rootdir /tmp/app0 /tmp/app1 /tmp/overlay /tmp/os-release-fix /tmp/os-release-fix/etc
index 4f31a33c343a52ba9c7e24da45b592798b9d0eac..c1204685938ca6b1dfd1f92a824300fdc4c92ab6 100755 (executable)
@@ -8,7 +8,7 @@ set -o pipefail
 
 if [[ "$(sysctl -ne kernel.apparmor_restrict_unprivileged_userns)" -eq 1 ]]; then
     echo "Cannot create unprivileged user namespaces" >/skipped
-    exit 0
+    exit 77
 fi
 
 systemd-analyze log-level debug
index b20b39d762b97fce1ed9c7999982838e3430a073..d9ba83aa8255d379cfb82929998e045c2360234e 100755 (executable)
@@ -6,7 +6,7 @@ set -o pipefail
 # Check if homectl is installed, and if it isn't bail out early instead of failing
 if ! test -x /usr/bin/homectl ; then
         echo "no homed" >/skipped
-        exit 0
+        exit 77
 fi
 
 inspect() {
@@ -207,6 +207,10 @@ PASSWORD=xEhErW0ndafV4s homectl with test-user -- rm /home/test-user/xyz
 PASSWORD=xEhErW0ndafV4s homectl with test-user -- test ! -f /home/test-user/xyz
 (! PASSWORD=xEhErW0ndafV4s homectl with test-user -- test -f /home/test-user/xyz)
 
+# Regression tests
+wait_for_state test-user inactive
+/usr/lib/systemd/tests/unit-tests/manual/test-homed-regression-31896 test-user
+
 wait_for_state test-user inactive
 homectl remove test-user
 
diff --git a/test/units/testsuite-50.DDI.sh b/test/units/testsuite-50.DDI.sh
new file mode 100755 (executable)
index 0000000..42c9a43
--- /dev/null
@@ -0,0 +1,70 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# Check that the /sbin/mount.ddi helper works
+dir="/tmp/mounthelper.$RANDOM"
+mount -t ddi "$MINIMAL_IMAGE.gpt" "$dir" -o ro,X-mount.mkdir,discard
+umount -R "$dir"
+
+# Test systemd-repart --make-ddi=:
+if [[ -z "${OPENSSL_CONFIG:?}" ]] || ! command -v mksquashfs &>/dev/null; then
+    echo "Skipping --make-ddi= tests"
+    exit 0
+fi
+
+openssl req -config "$OPENSSL_CONFIG" -subj="/CN=waldo" \
+            -x509 -sha256 -nodes -days 365 -newkey rsa:4096 \
+            -keyout /tmp/test-50-privkey.key -out /tmp/test-50-cert.crt
+mkdir -p /tmp/test-50-confext/etc/extension-release.d/
+echo "foobar50" >/tmp/test-50-confext/etc/waldo
+{
+    grep -e '^\(ID\|VERSION_ID\)=' /etc/os-release
+    echo IMAGE_ID=waldo
+    echo IMAGE_VERSION=7
+} >/tmp/test-50-confext/etc/extension-release.d/extension-release.waldo
+mkdir -p /run/confexts
+
+SYSTEMD_REPART_OVERRIDE_FSTYPE=squashfs \
+    systemd-repart -C \
+                   -s /tmp/test-50-confext \
+                   --certificate=/tmp/test-50-cert.crt \
+                   --private-key=/tmp/test-50-privkey.key \
+                   /run/confexts/waldo.confext.raw
+rm -rf /tmp/test-50-confext
+
+mkdir -p /run/verity.d
+cp /tmp/test-50-cert.crt /run/verity.d/
+systemd-dissect --mtree /run/confexts/waldo.confext.raw
+
+systemd-confext refresh
+test "$(</etc/waldo)" = foobar50
+rm /run/confexts/waldo.confext.raw
+systemd-confext refresh
+test ! -f /etc/waldo
+
+mkdir -p /tmp/test-50-sysext/usr/lib/extension-release.d/
+# Make sure the sysext is big enough to not fit in the minimum partition size of repart so we know the
+# Minimize= logic is working.
+truncate --size=50M /tmp/test-50-sysext/usr/waldo
+{
+    grep -e '^\(ID\|VERSION_ID\)=' /etc/os-release
+    echo IMAGE_ID=waldo
+    echo IMAGE_VERSION=7
+} >/tmp/test-50-sysext/usr/lib/extension-release.d/extension-release.waldo
+mkdir -p /run/extensions
+
+SYSTEMD_REPART_OVERRIDE_FSTYPE=squashfs \
+    systemd-repart -S \
+                   -s /tmp/test-50-sysext \
+                   --certificate=/tmp/test-50-cert.crt \
+                   --private-key=/tmp/test-50-privkey.key \
+                   /run/extensions/waldo.sysext.raw
+
+systemd-dissect --mtree /run/extensions/waldo.sysext.raw
+systemd-sysext refresh
+test -f /usr/waldo
+rm /run/verity.d/test-50-cert.crt /run/extensions/waldo.sysext.raw /tmp/test-50-cert.crt /tmp/test-50-privkey.key
+systemd-sysext refresh
+test ! -f /usr/waldo
diff --git a/test/units/testsuite-50.dissect.sh b/test/units/testsuite-50.dissect.sh
new file mode 100755 (executable)
index 0000000..34e60aa
--- /dev/null
@@ -0,0 +1,689 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
+# ex: ts=8 sw=4 sts=4 et filetype=sh
+# shellcheck disable=SC2233,SC2235
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+systemd-dissect --json=short "$MINIMAL_IMAGE.raw" | \
+    grep -q -F '{"rw":"ro","designator":"root","partition_uuid":null,"partition_label":null,"fstype":"squashfs","architecture":null,"verity":"external"'
+systemd-dissect "$MINIMAL_IMAGE.raw" | grep -q -F "MARKER=1"
+systemd-dissect "$MINIMAL_IMAGE.raw" | grep -q -F -f <(sed 's/"//g' "$OS_RELEASE")
+
+systemd-dissect --list "$MINIMAL_IMAGE.raw" | grep -q '^etc/os-release$'
+systemd-dissect --mtree "$MINIMAL_IMAGE.raw" --mtree-hash yes | \
+    grep -qe "^./usr/bin/cat type=file mode=0755 uid=0 gid=0 size=[0-9]* sha256sum=[a-z0-9]*$"
+systemd-dissect --mtree "$MINIMAL_IMAGE.raw" --mtree-hash no  | \
+    grep -qe "^./usr/bin/cat type=file mode=0755 uid=0 gid=0 size=[0-9]*$"
+
+read -r SHA256SUM1 _ < <(systemd-dissect --copy-from "$MINIMAL_IMAGE.raw" etc/os-release | sha256sum)
+test "$SHA256SUM1" != ""
+read -r SHA256SUM2 _ < <(systemd-dissect --read-only --with "$MINIMAL_IMAGE.raw" sha256sum etc/os-release)
+test "$SHA256SUM2" != ""
+test "$SHA256SUM1" = "$SHA256SUM2"
+
+if systemctl --version | grep -qF -- "+LIBARCHIVE" ; then
+    # Make sure tarballs are reproducible
+    read -r SHA256SUM1 _ < <(systemd-dissect --make-archive "$MINIMAL_IMAGE.raw" | sha256sum)
+    test "$SHA256SUM1" != ""
+    read -r SHA256SUM2 _ < <(systemd-dissect --make-archive "$MINIMAL_IMAGE.raw" | sha256sum)
+    test "$SHA256SUM2" != ""
+    test "$SHA256SUM1" = "$SHA256SUM2"
+    # Also check that a file we expect to be there is there
+    systemd-dissect --make-archive "$MINIMAL_IMAGE.raw" | tar t | grep etc/os-release
+fi
+
+mv "$MINIMAL_IMAGE.verity" "$MINIMAL_IMAGE.fooverity"
+mv "$MINIMAL_IMAGE.roothash" "$MINIMAL_IMAGE.foohash"
+systemd-dissect "$MINIMAL_IMAGE.raw" \
+                --json=short \
+                --root-hash="$MINIMAL_IMAGE_ROOTHASH" \
+                --verity-data="$MINIMAL_IMAGE.fooverity" | \
+                grep -q -F '{"rw":"ro","designator":"root","partition_uuid":null,"partition_label":null,"fstype":"squashfs","architecture":null,"verity":"external"'
+systemd-dissect "$MINIMAL_IMAGE.raw" \
+                --root-hash="$MINIMAL_IMAGE_ROOTHASH" \
+                --verity-data="$MINIMAL_IMAGE.fooverity" | \
+                grep -q -F "MARKER=1"
+systemd-dissect "$MINIMAL_IMAGE.raw" \
+                --root-hash="$MINIMAL_IMAGE_ROOTHASH" \
+                --verity-data="$MINIMAL_IMAGE.fooverity" | \
+                grep -q -F -f <(sed 's/"//g' "$OS_RELEASE")
+mv "$MINIMAL_IMAGE.fooverity" "$MINIMAL_IMAGE.verity"
+mv "$MINIMAL_IMAGE.foohash" "$MINIMAL_IMAGE.roothash"
+
+mkdir -p "$IMAGE_DIR/mount" "$IMAGE_DIR/mount2"
+systemd-dissect --mount "$MINIMAL_IMAGE.raw" "$IMAGE_DIR/mount"
+grep -q -F -f "$OS_RELEASE" "$IMAGE_DIR/mount/usr/lib/os-release"
+grep -q -F -f "$OS_RELEASE" "$IMAGE_DIR/mount/etc/os-release"
+grep -q -F "MARKER=1" "$IMAGE_DIR/mount/usr/lib/os-release"
+# Verity volume should be shared (opened only once)
+systemd-dissect --mount "$MINIMAL_IMAGE.raw" "$IMAGE_DIR/mount2"
+verity_count=$(find /dev/mapper/ -name "*verity*" | wc -l)
+# In theory we should check that count is exactly one. In practice, libdevmapper
+# randomly and unpredictably fails with an unhelpful EINVAL when a device is open
+# (and even mounted and in use), so best-effort is the most we can do for now
+if [[ "$verity_count" -lt 1 ]]; then
+    echo "Verity device $MINIMAL_IMAGE.raw not found in /dev/mapper/"
+    exit 1
+fi
+systemd-dissect --umount "$IMAGE_DIR/mount"
+systemd-dissect --umount "$IMAGE_DIR/mount2"
+
+systemd-run -P -p RootImage="$MINIMAL_IMAGE.raw" cat /usr/lib/os-release | grep -q -F "MARKER=1"
+mv "$MINIMAL_IMAGE.verity" "$MINIMAL_IMAGE.fooverity"
+mv "$MINIMAL_IMAGE.roothash" "$MINIMAL_IMAGE.foohash"
+systemd-run -P \
+            -p RootImage="$MINIMAL_IMAGE.raw" \
+            -p RootHash="$MINIMAL_IMAGE.foohash" \
+            -p RootVerity="$MINIMAL_IMAGE.fooverity" \
+            cat /usr/lib/os-release | grep -q -F "MARKER=1"
+# Let's use the long option name just here as a test
+systemd-run -P \
+            --property RootImage="$MINIMAL_IMAGE.raw" \
+            --property RootHash="$MINIMAL_IMAGE_ROOTHASH" \
+            --property RootVerity="$MINIMAL_IMAGE.fooverity" \
+            cat /usr/lib/os-release | grep -q -F "MARKER=1"
+mv "$MINIMAL_IMAGE.fooverity" "$MINIMAL_IMAGE.verity"
+mv "$MINIMAL_IMAGE.foohash" "$MINIMAL_IMAGE.roothash"
+
+# Derive partition UUIDs from root hash, in UUID syntax
+ROOT_UUID="$(systemd-id128 -u show "$(head -c 32 "$MINIMAL_IMAGE.roothash")" -u | tail -n 1 | cut -b 6-)"
+VERITY_UUID="$(systemd-id128 -u show "$(tail -c 32 "$MINIMAL_IMAGE.roothash")" -u | tail -n 1 | cut -b 6-)"
+
+systemd-dissect --json=short \
+                --root-hash "$MINIMAL_IMAGE_ROOTHASH" \
+                "$MINIMAL_IMAGE.gpt" | \
+                grep -q '{"rw":"ro","designator":"root","partition_uuid":"'"$ROOT_UUID"'","partition_label":"Root Partition","fstype":"squashfs","architecture":"'"$ARCHITECTURE"'","verity":"signed",'
+systemd-dissect --json=short \
+                --root-hash "$MINIMAL_IMAGE_ROOTHASH" \
+                "$MINIMAL_IMAGE.gpt" | \
+                grep -q '{"rw":"ro","designator":"root-verity","partition_uuid":"'"$VERITY_UUID"'","partition_label":"Verity Partition","fstype":"DM_verity_hash","architecture":"'"$ARCHITECTURE"'","verity":null,'
+if [[ -n "${OPENSSL_CONFIG:-}" ]]; then
+    systemd-dissect --json=short \
+                    --root-hash "$MINIMAL_IMAGE_ROOTHASH" \
+                    "$MINIMAL_IMAGE.gpt" | \
+                    grep -qE '{"rw":"ro","designator":"root-verity-sig","partition_uuid":"'".*"'","partition_label":"Signature Partition","fstype":"verity_hash_signature","architecture":"'"$ARCHITECTURE"'","verity":null,'
+fi
+systemd-dissect --root-hash "$MINIMAL_IMAGE_ROOTHASH" "$MINIMAL_IMAGE.gpt" | grep -q -F "MARKER=1"
+systemd-dissect --root-hash "$MINIMAL_IMAGE_ROOTHASH" "$MINIMAL_IMAGE.gpt" | grep -q -F -f <(sed 's/"//g' "$OS_RELEASE")
+
+# Test image policies
+systemd-dissect --validate "$MINIMAL_IMAGE.gpt"
+systemd-dissect --validate "$MINIMAL_IMAGE.gpt" --image-policy='*'
+(! systemd-dissect --validate "$MINIMAL_IMAGE.gpt" --image-policy='~')
+(! systemd-dissect --validate "$MINIMAL_IMAGE.gpt" --image-policy='-')
+(! systemd-dissect --validate "$MINIMAL_IMAGE.gpt" --image-policy=root=absent)
+(! systemd-dissect --validate "$MINIMAL_IMAGE.gpt" --image-policy=swap=unprotected+encrypted+verity)
+systemd-dissect --validate "$MINIMAL_IMAGE.gpt" --image-policy=root=unprotected
+systemd-dissect --validate "$MINIMAL_IMAGE.gpt" --image-policy=root=verity
+systemd-dissect --validate "$MINIMAL_IMAGE.gpt" --image-policy=root=verity:root-verity-sig=unused+absent
+systemd-dissect --validate "$MINIMAL_IMAGE.gpt" --image-policy=root=verity:swap=absent
+systemd-dissect --validate "$MINIMAL_IMAGE.gpt" --image-policy=root=verity:swap=absent+unprotected
+(! systemd-dissect --validate "$MINIMAL_IMAGE.gpt" --image-policy=root=verity:root-verity=unused+absent)
+systemd-dissect --validate "$MINIMAL_IMAGE.gpt" --image-policy=root=signed
+(! systemd-dissect --validate "$MINIMAL_IMAGE.gpt" --image-policy=root=signed:root-verity-sig=unused+absent)
+(! systemd-dissect --validate "$MINIMAL_IMAGE.gpt" --image-policy=root=signed:root-verity=unused+absent)
+
+# Test RootImagePolicy= unit file setting
+systemd-run --wait -P \
+            -p RootImage="$MINIMAL_IMAGE.gpt" \
+            -p RootHash="$MINIMAL_IMAGE_ROOTHASH" \
+            -p MountAPIVFS=yes \
+            cat /usr/lib/os-release | grep -q -F "MARKER=1"
+systemd-run --wait -P \
+            -p RootImage="$MINIMAL_IMAGE.gpt" \
+            -p RootHash="$MINIMAL_IMAGE_ROOTHASH" \
+            -p RootImagePolicy='*' \
+            -p MountAPIVFS=yes \
+            cat /usr/lib/os-release | grep -q -F "MARKER=1"
+(! systemd-run --wait -P \
+               -p RootImage="$MINIMAL_IMAGE.gpt" \
+               -p RootHash="$MINIMAL_IMAGE_ROOTHASH" \
+               -p RootImagePolicy='~' \
+               -p MountAPIVFS=yes \
+               cat /usr/lib/os-release | grep -q -F "MARKER=1")
+(! systemd-run --wait -P \
+               -p RootImage="$MINIMAL_IMAGE.gpt" \
+               -p RootHash="$MINIMAL_IMAGE_ROOTHASH" \
+               -p RootImagePolicy='-' \
+               -p MountAPIVFS=yes \
+               cat /usr/lib/os-release | grep -q -F "MARKER=1")
+(! systemd-run --wait -P \
+               -p RootImage="$MINIMAL_IMAGE.gpt" \
+               -p RootHash="$MINIMAL_IMAGE_ROOTHASH" \
+               -p RootImagePolicy='root=absent' \
+               -p MountAPIVFS=yes \
+               cat /usr/lib/os-release | grep -q -F "MARKER=1")
+systemd-run --wait -P \
+            -p RootImage="$MINIMAL_IMAGE.gpt" \
+            -p RootHash="$MINIMAL_IMAGE_ROOTHASH" \
+            -p RootImagePolicy='root=verity' \
+            -p MountAPIVFS=yes \
+            cat /usr/lib/os-release | grep -q -F "MARKER=1"
+systemd-run --wait -P \
+            -p RootImage="$MINIMAL_IMAGE.gpt" \
+            -p RootHash="$MINIMAL_IMAGE_ROOTHASH" \
+            -p RootImagePolicy='root=signed' \
+            -p MountAPIVFS=yes \
+            cat /usr/lib/os-release | grep -q -F "MARKER=1"
+(! systemd-run --wait -P \
+               -p RootImage="$MINIMAL_IMAGE.gpt" \
+               -p RootHash="$MINIMAL_IMAGE_ROOTHASH" \
+               -p RootImagePolicy='root=encrypted' \
+               -p MountAPIVFS=yes \
+               cat /usr/lib/os-release | grep -q -F "MARKER=1")
+
+systemd-dissect --root-hash "$MINIMAL_IMAGE_ROOTHASH" --mount "$MINIMAL_IMAGE.gpt" "$IMAGE_DIR/mount"
+grep -q -F -f "$OS_RELEASE" "$IMAGE_DIR/mount/usr/lib/os-release"
+grep -q -F -f "$OS_RELEASE" "$IMAGE_DIR/mount/etc/os-release"
+grep -q -F "MARKER=1" "$IMAGE_DIR/mount/usr/lib/os-release"
+systemd-dissect --umount "$IMAGE_DIR/mount"
+
+systemd-dissect --root-hash "$MINIMAL_IMAGE_ROOTHASH" --mount "$MINIMAL_IMAGE.gpt" --in-memory "$IMAGE_DIR/mount"
+grep -q -F -f "$OS_RELEASE" "$IMAGE_DIR/mount/usr/lib/os-release"
+grep -q -F -f "$OS_RELEASE" "$IMAGE_DIR/mount/etc/os-release"
+grep -q -F "MARKER=1" "$IMAGE_DIR/mount/usr/lib/os-release"
+systemd-dissect --umount "$IMAGE_DIR/mount"
+
+# add explicit -p MountAPIVFS=yes once to test the parser
+systemd-run -P \
+            -p RootImage="$MINIMAL_IMAGE.gpt" \
+            -p RootHash="$MINIMAL_IMAGE_ROOTHASH" \
+            -p MountAPIVFS=yes \
+            cat /usr/lib/os-release | grep -q -F "MARKER=1"
+systemd-run -P \
+            -p RootImage="$MINIMAL_IMAGE.raw" \
+            -p RootImageOptions="root:nosuid,dev home:ro,dev ro,noatime" \
+            mount | grep -F "squashfs" | grep -q -F "nosuid"
+systemd-run -P \
+            -p RootImage="$MINIMAL_IMAGE.gpt" \
+            -p RootImageOptions="root:ro,noatime root:ro,dev" \
+            mount | grep -F "squashfs" | grep -q -F "noatime"
+
+mkdir -p "$IMAGE_DIR/result"
+cat >/run/systemd/system/testservice-50a.service <<EOF
+[Service]
+Type=oneshot
+ExecStart=bash -c "mount >/run/result/a"
+BindPaths=$IMAGE_DIR/result:/run/result
+TemporaryFileSystem=/run
+RootImage=$MINIMAL_IMAGE.raw
+RootImageOptions=root:ro,noatime home:ro,dev relatime,dev
+RootImageOptions=nosuid,dev
+EOF
+systemctl start testservice-50a.service
+grep -F "squashfs" "$IMAGE_DIR/result/a" | grep -q -F "noatime"
+grep -F "squashfs" "$IMAGE_DIR/result/a" | grep -q -F -v "nosuid"
+
+cat >/run/systemd/system/testservice-50b.service <<EOF
+[Service]
+Type=oneshot
+ExecStart=bash -c "mount >/run/result/b"
+BindPaths=$IMAGE_DIR/result:/run/result
+TemporaryFileSystem=/run
+RootImage=$MINIMAL_IMAGE.gpt
+RootImageOptions=root:ro,noatime,nosuid home:ro,dev nosuid,dev
+RootImageOptions=home:ro,dev nosuid,dev,%%foo
+# this is the default, but let's specify once to test the parser
+MountAPIVFS=yes
+EOF
+systemctl start testservice-50b.service
+grep -F "squashfs" "$IMAGE_DIR/result/b" | grep -q -F "noatime"
+
+# Check that specifier escape is applied %%foo → %foo
+busctl get-property org.freedesktop.systemd1 \
+                    /org/freedesktop/systemd1/unit/testservice_2d50b_2eservice \
+                    org.freedesktop.systemd1.Service RootImageOptions | grep -F "nosuid,dev,%foo"
+
+# Now do some checks with MountImages, both by itself, with options and in combination with RootImage, and as single FS or GPT image
+systemd-run -P \
+            -p MountImages="$MINIMAL_IMAGE.gpt:/run/img1 $MINIMAL_IMAGE.raw:/run/img2" \
+            cat /run/img1/usr/lib/os-release | grep -q -F "MARKER=1"
+systemd-run -P \
+            -p MountImages="$MINIMAL_IMAGE.gpt:/run/img1 $MINIMAL_IMAGE.raw:/run/img2" \
+            cat /run/img2/usr/lib/os-release | grep -q -F "MARKER=1"
+systemd-run -P \
+            -p MountImages="$MINIMAL_IMAGE.gpt:/run/img1 $MINIMAL_IMAGE.raw:/run/img2:nosuid,dev" \
+            mount | grep -F "squashfs" | grep -q -F "nosuid"
+systemd-run -P \
+            -p MountImages="$MINIMAL_IMAGE.gpt:/run/img1:root:nosuid $MINIMAL_IMAGE.raw:/run/img2:home:suid" \
+            mount | grep -F "squashfs" | grep -q -F "nosuid"
+systemd-run -P \
+            -p MountImages="$MINIMAL_IMAGE.raw:/run/img2\:3" \
+            cat /run/img2:3/usr/lib/os-release | grep -q -F "MARKER=1"
+systemd-run -P \
+            -p MountImages="$MINIMAL_IMAGE.raw:/run/img2\:3:nosuid" \
+            mount | grep -F "squashfs" | grep -q -F "nosuid"
+systemd-run -P \
+            -p TemporaryFileSystem=/run \
+            -p RootImage="$MINIMAL_IMAGE.raw" \
+            -p MountImages="$MINIMAL_IMAGE.gpt:/run/img1 $MINIMAL_IMAGE.raw:/run/img2" \
+            cat /usr/lib/os-release | grep -q -F "MARKER=1"
+systemd-run -P \
+            -p TemporaryFileSystem=/run \
+            -p RootImage="$MINIMAL_IMAGE.raw" \
+            -p MountImages="$MINIMAL_IMAGE.gpt:/run/img1 $MINIMAL_IMAGE.raw:/run/img2" \
+            cat /run/img1/usr/lib/os-release | grep -q -F "MARKER=1"
+systemd-run -P \
+            -p TemporaryFileSystem=/run \
+            -p RootImage="$MINIMAL_IMAGE.gpt" \
+            -p RootHash="$MINIMAL_IMAGE_ROOTHASH" \
+            -p MountImages="$MINIMAL_IMAGE.gpt:/run/img1 $MINIMAL_IMAGE.raw:/run/img2" \
+            cat /run/img2/usr/lib/os-release | grep -q -F "MARKER=1"
+cat >/run/systemd/system/testservice-50c.service <<EOF
+[Service]
+MountAPIVFS=yes
+TemporaryFileSystem=/run
+RootImage=$MINIMAL_IMAGE.raw
+MountImages=$MINIMAL_IMAGE.gpt:/run/img1:root:noatime:home:relatime
+MountImages=$MINIMAL_IMAGE.raw:/run/img2\:3:nosuid
+ExecStart=bash -c "cat /run/img1/usr/lib/os-release >/run/result/c"
+ExecStart=bash -c "cat /run/img2:3/usr/lib/os-release >>/run/result/c"
+ExecStart=bash -c "mount >>/run/result/c"
+BindPaths=$IMAGE_DIR/result:/run/result
+Type=oneshot
+EOF
+systemctl start testservice-50c.service
+grep -q -F "MARKER=1" "$IMAGE_DIR/result/c"
+grep -F "squashfs" "$IMAGE_DIR/result/c" | grep -q -F "noatime"
+grep -F "squashfs" "$IMAGE_DIR/result/c" | grep -q -F -v "nosuid"
+
+# Adding a new mounts at runtime works if the unit is in the active state,
+# so use Type=notify to make sure there's no race condition in the test
+cat >/run/systemd/system/testservice-50d.service <<EOF
+[Service]
+RuntimeMaxSec=300
+Type=notify
+RemainAfterExit=yes
+MountAPIVFS=yes
+PrivateTmp=yes
+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; \\
+    done; \\
+    mount; \\
+    mount | grep -F "on /tmp/img type squashfs" | grep -q -F "nosuid"; \\
+'
+EOF
+systemctl start testservice-50d.service
+
+# Mount twice to exercise mount-beneath (on kernel 6.5+, on older kernels it will just overmount)
+mkdir -p /tmp/wrong/foo
+mksquashfs /tmp/wrong/foo /tmp/wrong.raw
+systemctl mount-image --mkdir testservice-50d.service /tmp/wrong.raw /tmp/img
+test "$(systemctl show -P SubState testservice-50d.service)" = "running"
+systemctl mount-image --mkdir testservice-50d.service "$MINIMAL_IMAGE.raw" /tmp/img root:nosuid
+# shellcheck disable=SC2016
+timeout 30s bash -xec 'while [[ $(systemctl show -P SubState testservice-50d.service) == running ]]; do sleep .2; done'
+systemctl is-active testservice-50d.service
+
+# ExtensionImages will set up an overlay
+systemd-run -P \
+            --property ExtensionImages=/usr/share/app0.raw \
+            --property RootImage="$MINIMAL_IMAGE.raw" \
+            cat /opt/script0.sh | grep -q -F "extension-release.app0"
+systemd-run -P \
+            --property ExtensionImages=/usr/share/app0.raw \
+            --property RootImage="$MINIMAL_IMAGE.raw" \
+            cat /usr/lib/systemd/system/some_file | grep -q -F "MARKER=1"
+systemd-run -P \
+            --property ExtensionImages="/usr/share/app0.raw /usr/share/app1.raw" \
+            --property RootImage="$MINIMAL_IMAGE.raw" \
+            cat /opt/script0.sh | grep -q -F "extension-release.app0"
+systemd-run -P \
+            --property ExtensionImages="/usr/share/app0.raw /usr/share/app1.raw" \
+            --property RootImage="$MINIMAL_IMAGE.raw" \
+            cat /usr/lib/systemd/system/some_file | grep -q -F "MARKER=1"
+systemd-run -P \
+            --property ExtensionImages="/usr/share/app0.raw /usr/share/app1.raw" \
+            --property RootImage="$MINIMAL_IMAGE.raw" \
+            cat /opt/script1.sh | grep -q -F "extension-release.app2"
+systemd-run -P \
+            --property ExtensionImages="/usr/share/app0.raw /usr/share/app1.raw" \
+            --property RootImage="$MINIMAL_IMAGE.raw" \
+            cat /usr/lib/systemd/system/other_file | grep -q -F "MARKER=1"
+systemd-run -P \
+            --property ExtensionImages=/usr/share/app-nodistro.raw \
+            --property RootImage="$MINIMAL_IMAGE.raw" \
+            cat /usr/lib/systemd/system/some_file | grep -q -F "MARKER=1"
+systemd-run -P \
+            --property ExtensionImages=/etc/service-scoped-test.raw \
+            --property RootImage="$MINIMAL_IMAGE.raw" \
+            cat /etc/systemd/system/some_file | grep -q -F "MARKER_CONFEXT_123"
+# Check that using a symlink to NAME-VERSION.raw works as long as the symlink has the correct name NAME.raw
+mkdir -p /usr/share/symlink-test/
+cp /usr/share/app-nodistro.raw /usr/share/symlink-test/app-nodistro-v1.raw
+ln -fs /usr/share/symlink-test/app-nodistro-v1.raw /usr/share/symlink-test/app-nodistro.raw
+systemd-run -P \
+            --property ExtensionImages=/usr/share/symlink-test/app-nodistro.raw \
+            --property RootImage="$MINIMAL_IMAGE.raw" \
+            cat /usr/lib/systemd/system/some_file | grep -q -F "MARKER=1"
+
+# Symlink check again but for confext
+mkdir -p /etc/symlink-test/
+cp /etc/service-scoped-test.raw /etc/symlink-test/service-scoped-test-v1.raw
+ln -fs /etc/symlink-test/service-scoped-test-v1.raw /etc/symlink-test/service-scoped-test.raw
+systemd-run -P \
+            --property ExtensionImages=/etc/symlink-test/service-scoped-test.raw \
+            --property RootImage="$MINIMAL_IMAGE.raw" \
+            cat /etc/systemd/system/some_file | grep -q -F "MARKER_CONFEXT_123"
+# And again mixing sysext and confext
+systemd-run -P \
+    --property ExtensionImages=/usr/share/symlink-test/app-nodistro.raw \
+    --property ExtensionImages=/etc/symlink-test/service-scoped-test.raw \
+    --property RootImage="$MINIMAL_IMAGE.raw" \
+    cat /etc/systemd/system/some_file | grep -q -F "MARKER_CONFEXT_123"
+systemd-run -P \
+    --property ExtensionImages=/usr/share/symlink-test/app-nodistro.raw \
+    --property ExtensionImages=/etc/symlink-test/service-scoped-test.raw \
+    --property RootImage="$MINIMAL_IMAGE.raw" \
+    cat /usr/lib/systemd/system/some_file | grep -q -F "MARKER=1"
+
+cat >/run/systemd/system/testservice-50e.service <<EOF
+[Service]
+MountAPIVFS=yes
+TemporaryFileSystem=/run /var/lib
+StateDirectory=app0
+RootImage=$MINIMAL_IMAGE.raw
+ExtensionImages=/usr/share/app0.raw /usr/share/app1.raw:nosuid
+# Relevant only for sanitizer runs
+UnsetEnvironment=LD_PRELOAD
+ExecStart=bash -c '/opt/script0.sh | grep ID'
+ExecStart=bash -c '/opt/script1.sh | grep ID'
+Type=oneshot
+RemainAfterExit=yes
+EOF
+systemctl start testservice-50e.service
+systemctl is-active testservice-50e.service
+
+# Check vpick support in ExtensionImages=
+VBASE="vtest$RANDOM"
+VDIR="/tmp/$VBASE.v"
+mkdir "$VDIR"
+
+ln -s /usr/share/app0.raw "$VDIR/${VBASE}_0.raw"
+ln -s /usr/share/app1.raw "$VDIR/${VBASE}_1.raw"
+
+systemd-run -P -p ExtensionImages="$VDIR" bash -c '/opt/script1.sh | grep ID'
+
+rm -rf "$VDIR"
+
+# ExtensionDirectories will set up an overlay
+mkdir -p "$IMAGE_DIR/app0" "$IMAGE_DIR/app1" "$IMAGE_DIR/app-nodistro" "$IMAGE_DIR/service-scoped-test"
+(! systemd-run -P \
+               --property ExtensionDirectories="$IMAGE_DIR/nonexistent" \
+               --property RootImage="$MINIMAL_IMAGE.raw" \
+               cat /opt/script0.sh)
+(! systemd-run -P \
+               --property ExtensionDirectories="$IMAGE_DIR/app0" \
+               --property RootImage="$MINIMAL_IMAGE.raw" \
+               cat /opt/script0.sh)
+systemd-dissect --mount /usr/share/app0.raw "$IMAGE_DIR/app0"
+systemd-dissect --mount /usr/share/app1.raw "$IMAGE_DIR/app1"
+systemd-dissect --mount /usr/share/app-nodistro.raw "$IMAGE_DIR/app-nodistro"
+systemd-dissect --mount /etc/service-scoped-test.raw "$IMAGE_DIR/service-scoped-test"
+systemd-run -P \
+            --property ExtensionDirectories="$IMAGE_DIR/app0" \
+            --property RootImage="$MINIMAL_IMAGE.raw" \
+            cat /opt/script0.sh | grep -q -F "extension-release.app0"
+systemd-run -P \
+            --property ExtensionDirectories="$IMAGE_DIR/app0" \
+            --property RootImage="$MINIMAL_IMAGE.raw" \
+            cat /usr/lib/systemd/system/some_file | grep -q -F "MARKER=1"
+systemd-run -P \
+            --property ExtensionDirectories="$IMAGE_DIR/app0 $IMAGE_DIR/app1" \
+            --property RootImage="$MINIMAL_IMAGE.raw" \
+            cat /opt/script0.sh | grep -q -F "extension-release.app0"
+systemd-run -P \
+            --property ExtensionDirectories="$IMAGE_DIR/app0 $IMAGE_DIR/app1" \
+            --property RootImage="$MINIMAL_IMAGE.raw" \
+            cat /usr/lib/systemd/system/some_file | grep -q -F "MARKER=1"
+systemd-run -P \
+            --property ExtensionDirectories="$IMAGE_DIR/app0 $IMAGE_DIR/app1" \
+            --property RootImage="$MINIMAL_IMAGE.raw" \
+            cat /opt/script1.sh | grep -q -F "extension-release.app2"
+systemd-run -P \
+            --property ExtensionDirectories="$IMAGE_DIR/app0 $IMAGE_DIR/app1" \
+            --property RootImage="$MINIMAL_IMAGE.raw" \
+            cat /usr/lib/systemd/system/other_file | grep -q -F "MARKER=1"
+systemd-run -P \
+            --property ExtensionDirectories="$IMAGE_DIR/app-nodistro" \
+            --property RootImage="$MINIMAL_IMAGE.raw" \
+            cat /usr/lib/systemd/system/some_file | grep -q -F "MARKER=1"
+systemd-run -P \
+            --property ExtensionDirectories="$IMAGE_DIR/service-scoped-test" \
+            --property RootImage="$MINIMAL_IMAGE.raw" \
+            cat /etc/systemd/system/some_file | grep -q -F "MARKER_CONFEXT_123"
+cat >/run/systemd/system/testservice-50f.service <<EOF
+[Service]
+MountAPIVFS=yes
+TemporaryFileSystem=/run /var/lib
+StateDirectory=app0
+RootImage=$MINIMAL_IMAGE.raw
+ExtensionDirectories=$IMAGE_DIR/app0 $IMAGE_DIR/app1
+# Relevant only for sanitizer runs
+UnsetEnvironment=LD_PRELOAD
+ExecStart=bash -c '/opt/script0.sh | grep ID'
+ExecStart=bash -c '/opt/script1.sh | grep ID'
+Type=oneshot
+RemainAfterExit=yes
+EOF
+systemctl start testservice-50f.service
+systemctl is-active testservice-50f.service
+
+# Check vpick support in ExtensionDirectories=
+VBASE="vtest$RANDOM"
+VDIR="/tmp/$VBASE.v"
+mkdir "$VDIR"
+
+ln -s "$IMAGE_DIR/app0" "$VDIR/${VBASE}_0"
+ln -s "$IMAGE_DIR/app1" "$VDIR/${VBASE}_1"
+
+systemd-run -P --property ExtensionDirectories="$VDIR" cat /opt/script1.sh | grep -q -F "extension-release.app2"
+
+rm -rf "$VDIR"
+
+systemd-dissect --umount "$IMAGE_DIR/app0"
+systemd-dissect --umount "$IMAGE_DIR/app1"
+
+# Test that an extension consisting of an empty directory under /etc/extensions/ takes precedence
+mkdir -p /var/lib/extensions/
+ln -s /usr/share/app-nodistro.raw /var/lib/extensions/app-nodistro.raw
+systemd-sysext merge
+grep -q -F "MARKER=1" /usr/lib/systemd/system/some_file
+systemd-sysext unmerge
+mkdir -p /etc/extensions/app-nodistro
+systemd-sysext merge
+test ! -e /usr/lib/systemd/system/some_file
+systemd-sysext unmerge
+rmdir /etc/extensions/app-nodistro
+
+# Similar, but go via varlink
+varlinkctl call /run/systemd/io.systemd.sysext io.systemd.sysext.List '{}'
+(! grep -q -F "MARKER=1" /usr/lib/systemd/system/some_file )
+varlinkctl call /run/systemd/io.systemd.sysext io.systemd.sysext.Merge '{}'
+grep -q -F "MARKER=1" /usr/lib/systemd/system/some_file
+varlinkctl call /run/systemd/io.systemd.sysext io.systemd.sysext.Refresh '{}'
+grep -q -F "MARKER=1" /usr/lib/systemd/system/some_file
+varlinkctl call /run/systemd/io.systemd.sysext io.systemd.sysext.Unmerge '{}'
+(! grep -q -F "MARKER=1" /usr/lib/systemd/system/some_file )
+
+# Check that extensions cannot contain os-release
+mkdir -p /run/extensions/app-reject/usr/lib/{extension-release.d/,systemd/system}
+echo "ID=_any" >/run/extensions/app-reject/usr/lib/extension-release.d/extension-release.app-reject
+echo "ID=_any" >/run/extensions/app-reject/usr/lib/os-release
+touch /run/extensions/app-reject/usr/lib/systemd/system/other_file
+(! systemd-sysext merge)
+test ! -e /usr/lib/systemd/system/some_file
+test ! -e /usr/lib/systemd/system/other_file
+systemd-sysext unmerge
+rm -rf /run/extensions/app-reject
+rm /var/lib/extensions/app-nodistro.raw
+
+# Some super basic test that RootImage= works with .v/ dirs
+VBASE="vtest$RANDOM"
+VDIR="/tmp/$VBASE.v"
+mkdir "$VDIR"
+
+ln -s "$MINIMAL_IMAGE.raw" "$VDIR/${VBASE}_33.raw"
+ln -s "$MINIMAL_IMAGE.raw" "$VDIR/${VBASE}_34.raw"
+ln -s "$MINIMAL_IMAGE.raw" "$VDIR/${VBASE}_35.raw"
+
+systemd-run -P -p RootImage="$VDIR" cat /usr/lib/os-release | grep -q -F "MARKER=1"
+
+rm "$VDIR/${VBASE}_33.raw" "$VDIR/${VBASE}_34.raw" "$VDIR/${VBASE}_35.raw"
+rmdir "$VDIR"
+
+mkdir -p /run/machines /run/portables /run/extensions
+touch /run/machines/a.raw /run/portables/b.raw /run/extensions/c.raw
+
+systemd-dissect --discover --json=short >/tmp/discover.json
+grep -q -F '{"name":"a","type":"raw","class":"machine","ro":false,"path":"/run/machines/a.raw"' /tmp/discover.json
+grep -q -F '{"name":"b","type":"raw","class":"portable","ro":false,"path":"/run/portables/b.raw"' /tmp/discover.json
+grep -q -F '{"name":"c","type":"raw","class":"sysext","ro":false,"path":"/run/extensions/c.raw"' /tmp/discover.json
+rm /tmp/discover.json /run/machines/a.raw /run/portables/b.raw /run/extensions/c.raw
+
+LOOP="$(systemd-dissect --attach --loop-ref=waldo "$MINIMAL_IMAGE.raw")"
+
+# Wait until the symlinks we want to test are established
+udevadm trigger -w "$LOOP"
+
+# Check if the /dev/loop/* symlinks really reference the right device
+test /dev/disk/by-loop-ref/waldo -ef "$LOOP"
+
+if [ "$(stat -c '%Hd:%Ld' "$MINIMAL_IMAGE.raw")" != '?d:?d' ] ; then
+   # Old stat didn't know the %Hd and %Ld specifiers and turned them into ?d
+   # instead. Let's simply skip the test on such old systems.
+   test "$(stat -c '/dev/disk/by-loop-inode/%Hd:%Ld-%i' "$MINIMAL_IMAGE.raw")" -ef "$LOOP"
+fi
+
+# Detach by loopback device
+systemd-dissect --detach "$LOOP"
+
+# Test long reference name.
+# Note, sizeof_field(struct loop_info64, lo_file_name) == 64,
+# and --loop-ref accepts upto 63 characters, and udev creates symlink
+# based on the name when it has upto _62_ characters.
+name="$(for _ in {1..62}; do echo -n 'x'; done)"
+LOOP="$(systemd-dissect --attach --loop-ref="$name" "$MINIMAL_IMAGE.raw")"
+udevadm trigger -w "$LOOP"
+
+# Check if the /dev/disk/by-loop-ref/$name symlink really references the right device
+test "/dev/disk/by-loop-ref/$name" -ef "$LOOP"
+
+# Detach by the /dev/disk/by-loop-ref symlink
+systemd-dissect --detach "/dev/disk/by-loop-ref/$name"
+
+name="$(for _ in {1..63}; do echo -n 'x'; done)"
+LOOP="$(systemd-dissect --attach --loop-ref="$name" "$MINIMAL_IMAGE.raw")"
+udevadm trigger -w "$LOOP"
+
+# Check if the /dev/disk/by-loop-ref/$name symlink does not exist
+test ! -e "/dev/disk/by-loop-ref/$name"
+
+# Detach by backing inode
+systemd-dissect --detach "$MINIMAL_IMAGE.raw"
+(! systemd-dissect --detach "$MINIMAL_IMAGE.raw")
+
+# check for confext functionality
+mkdir -p /run/confexts/test/etc/extension-release.d
+echo "ID=_any" >/run/confexts/test/etc/extension-release.d/extension-release.test
+echo "ARCHITECTURE=_any" >>/run/confexts/test/etc/extension-release.d/extension-release.test
+echo "MARKER_CONFEXT_123" >/run/confexts/test/etc/testfile
+cat <<EOF >/run/confexts/test/etc/testscript
+#!/bin/bash
+echo "This should not happen"
+EOF
+chmod +x /run/confexts/test/etc/testscript
+systemd-confext merge
+grep -q -F "MARKER_CONFEXT_123" /etc/testfile
+(! /etc/testscript)
+systemd-confext status
+systemd-confext unmerge
+rm -rf /run/confexts/
+
+unsquashfs -no-xattrs -d /tmp/img "$MINIMAL_IMAGE.raw"
+systemd-run --unit=test-root-ephemeral \
+    -p RootDirectory=/tmp/img \
+    -p RootEphemeral=yes \
+    -p Type=exec \
+    bash -c "touch /abc && sleep infinity"
+test -n "$(ls -A /var/lib/systemd/ephemeral-trees)"
+systemctl stop test-root-ephemeral
+# shellcheck disable=SC2016
+timeout 10 bash -c 'until test -z "$(ls -A /var/lib/systemd/ephemeral-trees)"; do sleep .5; done'
+test ! -f /tmp/img/abc
+
+systemd-dissect --mtree /tmp/img
+systemd-dissect --list /tmp/img
+
+read -r SHA256SUM1 _ < <(systemd-dissect --copy-from /tmp/img etc/os-release | sha256sum)
+test "$SHA256SUM1" != ""
+
+echo abc > abc
+systemd-dissect --copy-to /tmp/img abc /abc
+test -f /tmp/img/abc
+
+# Test for dissect tool support with systemd-sysext
+mkdir -p /run/extensions/ testkit/usr/lib/extension-release.d/
+echo "ID=_any" >testkit/usr/lib/extension-release.d/extension-release.testkit
+echo "ARCHITECTURE=_any" >>testkit/usr/lib/extension-release.d/extension-release.testkit
+echo "MARKER_SYSEXT_123" >testkit/usr/lib/testfile
+mksquashfs testkit/ testkit.raw
+cp testkit.raw /run/extensions/
+unsquashfs -l /run/extensions/testkit.raw
+systemd-dissect --no-pager /run/extensions/testkit.raw | grep -q '✓ sysext for portable service'
+systemd-dissect --no-pager /run/extensions/testkit.raw | grep -q '✓ sysext for system'
+systemd-sysext merge
+systemd-sysext status
+grep -q -F "MARKER_SYSEXT_123" /usr/lib/testfile
+systemd-sysext unmerge
+rm -rf /run/extensions/ testkit/
+
+# Test for dissect tool support with systemd-confext
+mkdir -p /run/confexts/ testjob/etc/extension-release.d/
+echo "ID=_any" >testjob/etc/extension-release.d/extension-release.testjob
+echo "ARCHITECTURE=_any" >>testjob/etc/extension-release.d/extension-release.testjob
+echo "MARKER_CONFEXT_123" >testjob/etc/testfile
+mksquashfs testjob/ testjob.raw
+cp testjob.raw /run/confexts/
+unsquashfs -l /run/confexts/testjob.raw
+systemd-dissect --no-pager /run/confexts/testjob.raw | grep -q '✓ confext for system'
+systemd-dissect --no-pager /run/confexts/testjob.raw | grep -q '✓ confext for portable service'
+systemd-confext merge
+systemd-confext status
+grep -q -F "MARKER_CONFEXT_123" /etc/testfile
+systemd-confext unmerge
+rm -rf /run/confexts/ testjob/
+
+systemd-run -P -p RootImage="$MINIMAL_IMAGE.raw" cat /run/host/os-release | cmp "$OS_RELEASE"
+
+# Test that systemd-sysext reloads the daemon.
+mkdir -p /var/lib/extensions/
+ln -s /usr/share/app-reload.raw /var/lib/extensions/app-reload.raw
+systemd-sysext merge --no-reload
+# the service should not be running
+(! systemctl --quiet is-active foo.service)
+systemd-sysext unmerge --no-reload
+systemd-sysext merge
+# shellcheck disable=SC2016
+timeout 30s bash -xec 'until [[ $(journalctl -b -u foo.service _TRANSPORT=stdout -o cat) == foo ]]; do sleep .5; done'
+systemd-sysext unmerge --no-reload
+# Grep on the Warning to find the warning helper mentioning the daemon reload.
+systemctl status foo.service 2>&1 | grep -q -F "Warning"
+systemd-sysext merge
+systemd-sysext unmerge
+systemctl status foo.service 2>&1 | grep -v -q -F "Warning"
+rm /var/lib/extensions/app-reload.raw
+
+# Sneak in a couple of expected-to-fail invocations to cover
+# https://github.com/systemd/systemd/issues/29610
+(! systemd-run -P -p MountImages="/this/should/definitely/not/exist.img:/run/img2\:3:nosuid" false)
+(! systemd-run -P -p ExtensionImages="/this/should/definitely/not/exist.img" false)
+(! systemd-run -P -p RootImage="/this/should/definitely/not/exist.img" false)
+(! systemd-run -P -p ExtensionDirectories="/foo/bar /foo/baz" false)
diff --git a/test/units/testsuite-50.mountnfsd.sh b/test/units/testsuite-50.mountnfsd.sh
new file mode 100755 (executable)
index 0000000..d6b5faf
--- /dev/null
@@ -0,0 +1,81 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+if [[ ! -f /usr/lib/systemd/system/systemd-mountfsd.socket ]] || \
+   [[ ! -f /usr/lib/systemd/system/systemd-nsresourced.socket ]] || \
+   ! command -v mksquashfs || \
+   ! grep -q bpf /sys/kernel/security/lsm ||
+   ! find /usr/lib* -name libbpf.so.1 2>/dev/null | grep .; then
+    echo "Skipping mountnfsd/nsresourced tests"
+    exit 0
+fi
+
+at_exit() {
+    set +e
+
+    umount -R /tmp/unpriv/mount
+    rmdir /tmp/unpriv
+    rm -f /tmp/test-50-unpriv-privkey.key /tmp/test-50-unpriv-cert.crt /run/verity.d/test-50-unpriv-cert.crt
+    rm -f /var/tmp/unpriv.raw /tmp/unpriv.raw.mtree /tmp/unpriv2.raw.mtree
+    rm -f /tmp/unpriv.out /tmp/unpriv.out2 /tmp/unpriv.out3
+}
+
+trap at_exit EXIT
+
+systemctl start systemd-mountfsd.socket systemd-nsresourced.socket
+
+openssl req -config "$OPENSSL_CONFIG" -subj="/CN=waldo" \
+            -x509 -sha256 -nodes -days 365 -newkey rsa:4096 \
+            -keyout /tmp/test-50-unpriv-privkey.key -out /tmp/test-50-unpriv-cert.crt
+
+systemd-dissect --mkdir --mount "$MINIMAL_IMAGE.raw" /tmp/unpriv/mount
+SYSTEMD_REPART_OVERRIDE_FSTYPE=squashfs \
+    systemd-repart -P \
+                   -s /tmp/unpriv/mount \
+                   --certificate=/tmp/test-50-unpriv-cert.crt \
+                   --private-key=/tmp/test-50-unpriv-privkey.key \
+                   /var/tmp/unpriv.raw
+systemd-dissect --rmdir --umount /tmp/unpriv/mount
+
+systemd-dissect --image-policy='root=unprotected:=absent+unused' /var/tmp/unpriv.raw
+systemd-dissect --image-policy='root=unprotected:=absent+unused' --mtree /var/tmp/unpriv.raw | tee /tmp/unpriv.raw.mtree
+
+# Run unpriv, should fail due to lack of privs
+(! runas testuser systemd-dissect /var/tmp/unpriv.raw)
+(! runas testuser systemd-dissect --mtree /var/tmp/unpriv.raw)
+
+# Install key in keychain
+cp /tmp/test-50-unpriv-cert.crt /run/verity.d
+
+# Now run unpriv again, should be OK now.
+runas testuser systemd-dissect /var/tmp/unpriv.raw
+runas testuser systemd-dissect --mtree /var/tmp/unpriv.raw | tee /tmp/unpriv2.raw.mtree
+
+# Check that unpriv and priv run yielded same results
+cmp /tmp/unpriv.raw.mtree /tmp/unpriv2.raw.mtree
+
+# Make sure nspawn works unpriv, too (for now do not nest)
+if ! systemd-detect-virt -c; then
+    systemd-nspawn --pipe -i /var/tmp/unpriv.raw --read-only echo thisisatest > /tmp/unpriv.out
+    echo thisisatest | cmp /tmp/unpriv.out -
+
+    # The unpriv user has no rights to lock the image or write to it. Let's
+    # turn off both for this test, so that we don't have to copy the image
+    # around.
+    systemd-run -M testuser@ --user --pipe \
+                -p Environment=SYSTEMD_NSPAWN_LOCK=0 \
+                -p Delegate=1 \
+                -p DelegateSubgroup=supervisor \
+                -p Environment=SYSTEMD_LOG_LEVEL=debug \
+                --wait -- \
+                systemd-nspawn --keep-unit -i /var/tmp/unpriv.raw --read-only --pipe echo thisisatest >/tmp/unpriv.out2
+    echo thisisatest | cmp /tmp/unpriv.out2 -
+fi
+
+systemd-run -M testuser@ --user --pipe -p RootImage=/var/tmp/unpriv.raw -p PrivateUsers=1 --wait echo thisisatest >/tmp/unpriv.out3
+echo thisisatest | cmp /tmp/unpriv.out3 -
index 7c4a9db96d0d99e68069c503bab2fa4c2652af4b..ab65c86ba8ba8bcdceb3e224e0ee363e7e1e1576 100755 (executable)
 #!/usr/bin/env bash
 # SPDX-License-Identifier: LGPL-2.1-or-later
-# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
-# ex: ts=8 sw=4 sts=4 et filetype=sh
-# shellcheck disable=SC2233,SC2235
 set -eux
 set -o pipefail
 
-export SYSTEMD_LOG_LEVEL=debug
+# shellcheck source=test/units/test-control.sh
+. "$(dirname "$0")"/test-control.sh
 
-cleanup_image_dir() {
-    if [ -z "${image_dir}" ]; then
-        return
-    fi
-    umount "${image_dir}/app0"
-    umount "${image_dir}/app1"
-    umount "${image_dir}/app-nodistro"
-    umount "${image_dir}/service-scoped-test"
-    rm -rf "${image_dir}"
-}
+# Setup shared stuff & run all subtests
 
-fake_roots_dir=/fake-roots
+at_exit() {
+    set +e
 
-cleanup_fake_rootfses() {
-    local tries=10 e
-    local -a lines fake_roots_mounts
+    if [[ -z "${IMAGE_DIR:-}" ]]; then
+        return
+    fi
 
-    while [[ ${tries} -gt 0 ]]; do
-        tries=$((tries - 1))
-        mapfile -t lines < <(mount | awk '{ print $3 }')
-        fake_roots_mounts=()
-        for e in "${lines[@]}"; do
-            if [[ ${e} = "${fake_roots_dir}"/* ]]; then
-                fake_roots_mounts+=( "${e}" )
-            fi
-        done
-        if [[ ${#fake_roots_mounts[@]} -eq 0 ]]; then
-            break
+    while read -r dir; do
+        if mountpoint -q "$dir"; then
+            umount -Rv "$dir"
         fi
-        for e in "${fake_roots_mounts[@]}"; do
-            umount "${e}"
-        done
-    done
-    rm -rf "${fake_roots_dir}"
-}
+    done < <(find "${IMAGE_DIR}" -mindepth 1 -maxdepth 1 -type d)
 
-# shellcheck disable=SC2317
-cleanup() {(
-    set +ex
+    rm -rf "$IMAGE_DIR"
+}
 
-    cleanup_image_dir
-    cleanup_fake_rootfses
-)}
+trap at_exit EXIT
 
-udevadm control --log-level=debug
+: "Setup base images"
 
-cd /tmp
+export SYSTEMD_LOG_LEVEL=debug
+export ARCHITECTURE
+export IMAGE_DIR
+export MACHINE
+export MINIMAL_IMAGE
+export MINIMAL_IMAGE_ROOTHASH
+export OPENSSL_CONFIG
+export OS_RELEASE
+export ROOT_GUID
+export SIGNATURE_GUID
+export VERITY_GUID
 
-image_dir="$(mktemp -d -t -p /tmp tmp.XXXXXX)"
-if [ -z "${image_dir}" ] || [ ! -d "${image_dir}" ]; then
-    echo "mktemp under /tmp failed"
+machine="$(uname -m)"
+if [[ "$machine" == "x86_64" ]]; then
+    ROOT_GUID=4f68bce3-e8cd-4db1-96e7-fbcaf984b709
+    VERITY_GUID=2c7357ed-ebd2-46d9-aec1-23d437ec2bf5
+    SIGNATURE_GUID=41092b05-9fc8-4523-994f-2def0408b176
+    ARCHITECTURE="x86-64"
+elif [[ "$machine" =~ ^(i386|i686|x86)$ ]]; then
+    ROOT_GUID=44479540-f297-41b2-9af7-d131d5f0458a
+    VERITY_GUID=d13c5d3b-b5d1-422a-b29f-9454fdc89d76
+    SIGNATURE_GUID=5996fc05-109c-48de-808b-23fa0830b676
+    ARCHITECTURE="x86"
+elif [[ "$machine" =~ ^(aarch64|aarch64_be|armv8b|armv8l)$ ]]; then
+    ROOT_GUID=b921b045-1df0-41c3-af44-4c6f280d3fae
+    VERITY_GUID=df3300ce-d69f-4c92-978c-9bfb0f38d820
+    SIGNATURE_GUID=6db69de6-29f4-4758-a7a5-962190f00ce3
+    ARCHITECTURE="arm64"
+elif [[ "$machine" == "arm" ]]; then
+    ROOT_GUID=69dad710-2ce4-4e3c-b16c-21a1d49abed3
+    VERITY_GUID=7386cdf2-203c-47a9-a498-f2ecce45a2d6
+    SIGNATURE_GUID=42b0455f-eb11-491d-98d3-56145ba9d037
+    ARCHITECTURE="arm"
+elif [[ "$machine" == "ia64" ]]; then
+    ROOT_GUID=993d8d3d-f80e-4225-855a-9daf8ed7ea97
+    VERITY_GUID=86ed10d5-b607-45bb-8957-d350f23d0571
+    SIGNATURE_GUID=e98b36ee-32ba-4882-9b12-0ce14655f46a
+    ARCHITECTURE="ia64"
+elif [[ "$machine" == "loongarch64" ]]; then
+    ROOT_GUID=77055800-792c-4f94-b39a-98c91b762bb6
+    VERITY_GUID=f3393b22-e9af-4613-a948-9d3bfbd0c535
+    SIGNATURE_GUID=5afb67eb-ecc8-4f85-ae8e-ac1e7c50e7d0
+    ARCHITECTURE="loongarch64"
+elif [[ "$machine" == "s390x" ]]; then
+    ROOT_GUID=5eead9a9-fe09-4a1e-a1d7-520d00531306
+    VERITY_GUID=b325bfbe-c7be-4ab8-8357-139e652d2f6b
+    SIGNATURE_GUID=c80187a5-73a3-491a-901a-017c3fa953e9
+    ARCHITECTURE="s390x"
+elif [[ "$machine" == "ppc64le" ]]; then
+    ROOT_GUID=c31c45e6-3f39-412e-80fb-4809c4980599
+    VERITY_GUID=906bd944-4589-4aae-a4e4-dd983917446a
+    SIGNATURE_GUID=d4a236e7-e873-4c07-bf1d-bf6cf7f1c3c6
+    ARCHITECTURE="ppc64-le"
+elif [[ "$machine" == "riscv64" ]]; then
+    ROOT_GUID=72ec70a6-cf74-40e6-bd49-4bda08e8f224
+    VERITY_GUID=b6ed5582-440b-4209-b8da-5ff7c419ea3d
+    SIGNATURE_GUID=efe0f087-ea8d-4469-821a-4c2a96a8386a
+    ARCHITECTURE="riscv64"
+elif [[ "$machine" == "riscv32" ]]; then
+    ROOT_GUID=60d5a7fe-8e7d-435c-b714-3dd8162144e1
+    VERITY_GUID=ae0253be-1167-4007-ac68-43926c14c5de
+    SIGNATURE_GUID=3a112a75-8729-4380-b4cf-764d79934448
+    ARCHITECTURE="riscv32"
+else
+    echo "Unexpected uname -m: $machine in testsuite-50.sh, please fix me"
     exit 1
 fi
 
-trap cleanup EXIT
-
-cp /usr/share/minimal* "${image_dir}/"
-image="${image_dir}/minimal_0"
-roothash="$(cat "${image}.roothash")"
-
-os_release="$(test -e /etc/os-release && echo /etc/os-release || echo /usr/lib/os-release)"
-
-systemd-dissect --json=short "${image}.raw" | grep -q -F '{"rw":"ro","designator":"root","partition_uuid":null,"partition_label":null,"fstype":"squashfs","architecture":null,"verity":"external"'
-systemd-dissect "${image}.raw" | grep -q -F "MARKER=1"
-systemd-dissect "${image}.raw" | grep -q -F -f <(sed 's/"//g' "$os_release")
-
-systemd-dissect --list "${image}.raw" | grep -q '^etc/os-release$'
-systemd-dissect --mtree "${image}.raw" --mtree-hash yes | grep -qe "^./usr/bin/cat type=file mode=0755 uid=0 gid=0 size=[0-9]* sha256sum=[a-z0-9]*$"
-systemd-dissect --mtree "${image}.raw" --mtree-hash no  | grep -qe "^./usr/bin/cat type=file mode=0755 uid=0 gid=0 size=[0-9]*$"
-
-read -r SHA256SUM1 _ < <(systemd-dissect --copy-from "${image}.raw" etc/os-release | sha256sum)
-test "$SHA256SUM1" != ""
-read -r SHA256SUM2 _ < <(systemd-dissect --read-only --with "${image}.raw" sha256sum etc/os-release)
-test "$SHA256SUM2" != ""
-test "$SHA256SUM1" = "$SHA256SUM2"
-
-if systemctl --version | grep -qF -- "+LIBARCHIVE" ; then
-    # Make sure tarballs are reproducible
-    read -r SHA256SUM1 _ < <(systemd-dissect --make-archive "${image}.raw" | sha256sum)
-    test "$SHA256SUM1" != ""
-    read -r SHA256SUM2 _ < <(systemd-dissect --make-archive "${image}.raw" | sha256sum)
-    test "$SHA256SUM2" != ""
-    test "$SHA256SUM1" = "$SHA256SUM2"
-    # Also check that a file we expect to be there is there
-    systemd-dissect --make-archive "${image}.raw" | tar t | grep etc/os-release
-fi
-
-mv "${image}.verity" "${image}.fooverity"
-mv "${image}.roothash" "${image}.foohash"
-systemd-dissect --json=short "${image}.raw" --root-hash="${roothash}" --verity-data="${image}.fooverity" | grep -q -F '{"rw":"ro","designator":"root","partition_uuid":null,"partition_label":null,"fstype":"squashfs","architecture":null,"verity":"external"'
-systemd-dissect "${image}.raw" --root-hash="${roothash}" --verity-data="${image}.fooverity" | grep -q -F "MARKER=1"
-systemd-dissect "${image}.raw" --root-hash="${roothash}" --verity-data="${image}.fooverity" | grep -q -F -f <(sed 's/"//g' "$os_release")
-mv "${image}.fooverity" "${image}.verity"
-mv "${image}.foohash" "${image}.roothash"
-
-mkdir -p "${image_dir}/mount" "${image_dir}/mount2"
-systemd-dissect --mount "${image}.raw" "${image_dir}/mount"
-grep -q -F -f "$os_release" "${image_dir}/mount/usr/lib/os-release"
-grep -q -F -f "$os_release" "${image_dir}/mount/etc/os-release"
-grep -q -F "MARKER=1" "${image_dir}/mount/usr/lib/os-release"
-# Verity volume should be shared (opened only once)
-systemd-dissect --mount "${image}.raw" "${image_dir}/mount2"
-verity_count=$(find /dev/mapper/ -name "*verity*" | wc -l)
-# In theory we should check that count is exactly one. In practice, libdevmapper
-# randomly and unpredictably fails with an unhelpful EINVAL when a device is open
-# (and even mounted and in use), so best-effort is the most we can do for now
-if [ "${verity_count}" -lt 1 ]; then
-    echo "Verity device ${image}.raw not found in /dev/mapper/"
-    exit 1
-fi
-systemd-dissect --umount "${image_dir}/mount"
-systemd-dissect --umount "${image_dir}/mount2"
+udevadm control --log-level=debug
 
-systemd-run -P -p RootImage="${image}.raw" cat /usr/lib/os-release | grep -q -F "MARKER=1"
-mv "${image}.verity" "${image}.fooverity"
-mv "${image}.roothash" "${image}.foohash"
-systemd-run -P -p RootImage="${image}.raw" -p RootHash="${image}.foohash" -p RootVerity="${image}.fooverity" cat /usr/lib/os-release | grep -q -F "MARKER=1"
-# Let's use the long option name just here as a test
-systemd-run -P --property RootImage="${image}.raw" --property RootHash="${roothash}" --property RootVerity="${image}.fooverity" cat /usr/lib/os-release | grep -q -F "MARKER=1"
-mv "${image}.fooverity" "${image}.verity"
-mv "${image}.foohash" "${image}.roothash"
+IMAGE_DIR="$(mktemp -d --tmpdir="" TEST-50-IMAGES.XXX)"
+cp -v /usr/share/minimal* "$IMAGE_DIR/"
+MINIMAL_IMAGE="$IMAGE_DIR/minimal_0"
+MINIMAL_IMAGE_ROOTHASH="$(<"$MINIMAL_IMAGE.roothash")"
 
-# Make a GPT disk on the fly, with the squashfs as partition 1 and the verity hash tree as partition 2
-machine="$(uname -m)"
-if [ "${machine}" = "x86_64" ]; then
-    root_guid=4f68bce3-e8cd-4db1-96e7-fbcaf984b709
-    verity_guid=2c7357ed-ebd2-46d9-aec1-23d437ec2bf5
-    signature_guid=41092b05-9fc8-4523-994f-2def0408b176
-    architecture="x86-64"
-elif [ "${machine}" = "i386" ] || [ "${machine}" = "i686" ] || [ "${machine}" = "x86" ]; then
-    root_guid=44479540-f297-41b2-9af7-d131d5f0458a
-    verity_guid=d13c5d3b-b5d1-422a-b29f-9454fdc89d76
-    signature_guid=5996fc05-109c-48de-808b-23fa0830b676
-    architecture="x86"
-elif [ "${machine}" = "aarch64" ] || [ "${machine}" = "aarch64_be" ] || [ "${machine}" = "armv8b" ] || [ "${machine}" = "armv8l" ]; then
-    root_guid=b921b045-1df0-41c3-af44-4c6f280d3fae
-    verity_guid=df3300ce-d69f-4c92-978c-9bfb0f38d820
-    signature_guid=6db69de6-29f4-4758-a7a5-962190f00ce3
-    architecture="arm64"
-elif [ "${machine}" = "arm" ]; then
-    root_guid=69dad710-2ce4-4e3c-b16c-21a1d49abed3
-    verity_guid=7386cdf2-203c-47a9-a498-f2ecce45a2d6
-    signature_guid=42b0455f-eb11-491d-98d3-56145ba9d037
-    architecture="arm"
-elif [ "${machine}" = "loongarch64" ]; then
-    root_guid=77055800-792c-4f94-b39a-98c91b762bb6
-    verity_guid=f3393b22-e9af-4613-a948-9d3bfbd0c535
-    signature_guid=5afb67eb-ecc8-4f85-ae8e-ac1e7c50e7d0
-    architecture="loongarch64"
-elif [ "${machine}" = "ia64" ]; then
-    root_guid=993d8d3d-f80e-4225-855a-9daf8ed7ea97
-    verity_guid=86ed10d5-b607-45bb-8957-d350f23d0571
-    signature_guid=e98b36ee-32ba-4882-9b12-0ce14655f46a
-    architecture="ia64"
-elif [ "${machine}" = "s390x" ]; then
-    root_guid=5eead9a9-fe09-4a1e-a1d7-520d00531306
-    verity_guid=b325bfbe-c7be-4ab8-8357-139e652d2f6b
-    signature_guid=c80187a5-73a3-491a-901a-017c3fa953e9
-    architecture="s390x"
-elif [ "${machine}" = "ppc64le" ]; then
-    root_guid=c31c45e6-3f39-412e-80fb-4809c4980599
-    verity_guid=906bd944-4589-4aae-a4e4-dd983917446a
-    signature_guid=d4a236e7-e873-4c07-bf1d-bf6cf7f1c3c6
-    architecture="ppc64-le"
-else
-    echo "Unexpected uname -m: ${machine} in testsuite-50.sh, please fix me"
-    exit 1
-fi
-# du rounds up to block size, which is more helpful for partitioning
-root_size="$(du -k "${image}.raw" | cut -f1)"
-verity_size="$(du -k "${image}.verity" | cut -f1)"
-signature_size=4
-# 4MB seems to be the minimum size blkid will accept, below that probing fails
-dd if=/dev/zero of="${image}.gpt" bs=512 count=$((8192+root_size*2+verity_size*2+signature_size*2))
-# sfdisk seems unhappy if the size overflows into the next unit, eg: 1580KiB will be interpreted as 1MiB
-# so do some basic rounding up if the minimal image is more than 1 MB
-if [ "${root_size}" -ge 1024 ]; then
-    root_size="$((root_size/1024 + 1))MiB"
-else
-    root_size="${root_size}KiB"
-fi
-verity_size="$((verity_size * 2))KiB"
-signature_size="$((signature_size * 2))KiB"
+OS_RELEASE="$(test -e /etc/os-release && echo /etc/os-release || echo /usr/lib/os-release)"
 
-HAVE_OPENSSL=0
 if systemctl --version | grep -q -- +OPENSSL ; then
-    # The openssl binary is installed conditionally.
-    # If we have OpenSSL support enabled and openssl is missing, fail early
-    # with a proper error message.
-    if ! command -v openssl >/dev/null 2>&1; then
-        echo "openssl missing" >/failed
+    # The openssl binary is installed conditionally. If we have OpenSSL support enabled and openssl is
+    # missing, fail early with a proper error message.
+    if ! command -v openssl &>/dev/null; then
+        echo "openssl binary is missing" >/failed
         exit 1
     fi
 
-    HAVE_OPENSSL=1
     OPENSSL_CONFIG="$(mktemp)"
     # Unfortunately OpenSSL insists on reading some config file, hence provide one with mostly placeholder contents
     cat >"${OPENSSL_CONFIG:?}" <<EOF
@@ -219,1703 +129,81 @@ OU = Org Unit Name
 CN = Common Name
 emailAddress = test@email.com
 EOF
+fi
+
+# Make a GPT disk on the fly, with the squashfs as partition 1 and the verity hash tree as partition 2
+#
+# du rounds up to block size, which is more helpful for partitioning
+root_size="$(du -k "$MINIMAL_IMAGE.raw" | cut -f1)"
+verity_size="$(du -k "$MINIMAL_IMAGE.verity" | cut -f1)"
+signature_size=4
+# 4MB seems to be the minimum size blkid will accept, below that probing fails
+dd if=/dev/zero of="$MINIMAL_IMAGE.gpt" bs=512 count=$((8192+root_size*2+verity_size*2+signature_size*2))
+# sfdisk seems unhappy if the size overflows into the next unit, eg: 1580KiB will be interpreted as 1MiB
+# so do some basic rounding up if the minimal image is more than 1 MB
+if [[ "$root_size" -ge 1024 ]]; then
+    root_size="$((root_size/1024 + 1))MiB"
+else
+    root_size="${root_size}KiB"
+fi
+verity_size="$((verity_size * 2))KiB"
+signature_size="$((signature_size * 2))KiB"
 
+if [[ -n "${OPENSSL_CONFIG:-}" ]]; then
     # Create key pair
-    openssl req -config "$OPENSSL_CONFIG" -new -x509 -newkey rsa:1024 -keyout "${image}.key" -out "${image}.crt" -days 365 -nodes
+    openssl req -config "$OPENSSL_CONFIG" -new -x509 -newkey rsa:1024 \
+                -keyout "$MINIMAL_IMAGE.key" -out "$MINIMAL_IMAGE.crt" -days 365 -nodes
     # Sign Verity root hash with it
-    openssl smime -sign -nocerts -noattr -binary -in "${image}.roothash" -inkey "${image}.key" -signer "${image}.crt" -outform der -out "${image}.roothash.p7s"
+    openssl smime -sign -nocerts -noattr -binary \
+                  -in "$MINIMAL_IMAGE.roothash" \
+                  -inkey "$MINIMAL_IMAGE.key" \
+                  -signer "$MINIMAL_IMAGE.crt" \
+                  -outform der \
+                  -out "$MINIMAL_IMAGE.roothash.p7s"
     # Generate signature partition JSON data
-    echo '{"rootHash":"'"${roothash}"'","signature":"'"$(base64 -w 0 <"${image}.roothash.p7s")"'"}' >"${image}.verity-sig"
+    echo '{"rootHash":"'"$MINIMAL_IMAGE_ROOTHASH"'","signature":"'"$(base64 -w 0 <"$MINIMAL_IMAGE.roothash.p7s")"'"}' >"$MINIMAL_IMAGE.verity-sig"
     # Pad it
-    truncate -s "${signature_size}" "${image}.verity-sig"
+    truncate -s "$signature_size" "$MINIMAL_IMAGE.verity-sig"
     # Register certificate in the (userspace) verity key ring
     mkdir -p /run/verity.d
-    ln -s "${image}.crt" /run/verity.d/ok.crt
+    ln -s "$MINIMAL_IMAGE.crt" /run/verity.d/ok.crt
 fi
 
 # Construct a UUID from hash
 # input:  11111111222233334444555566667777
 # output: 11111111-2222-3333-4444-555566667777
-uuid="$(head -c 32 "${image}.roothash" | sed -r 's/(.{8})(.{4})(.{4})(.{4})(.+)/\1-\2-\3-\4-\5/')"
-echo -e "label: gpt\nsize=${root_size}, type=${root_guid}, uuid=${uuid}" | sfdisk "${image}.gpt"
-uuid="$(tail -c 32 "${image}.roothash" | sed -r 's/(.{8})(.{4})(.{4})(.{4})(.+)/\1-\2-\3-\4-\5/')"
-echo -e "size=${verity_size}, type=${verity_guid}, uuid=${uuid}" | sfdisk "${image}.gpt" --append
-if [ "${HAVE_OPENSSL}" -eq 1 ]; then
-    echo -e "size=${signature_size}, type=${signature_guid}" | sfdisk "${image}.gpt" --append
+uuid="$(head -c 32 "$MINIMAL_IMAGE.roothash" | sed -r 's/(.{8})(.{4})(.{4})(.{4})(.+)/\1-\2-\3-\4-\5/')"
+echo -e "label: gpt\nsize=$root_size, type=$ROOT_GUID, uuid=$uuid" | sfdisk "$MINIMAL_IMAGE.gpt"
+uuid="$(tail -c 32 "$MINIMAL_IMAGE.roothash" | sed -r 's/(.{8})(.{4})(.{4})(.{4})(.+)/\1-\2-\3-\4-\5/')"
+echo -e "size=$verity_size, type=$VERITY_GUID, uuid=$uuid" | sfdisk "$MINIMAL_IMAGE.gpt" --append
+if [[ -n "${OPENSSL_CONFIG:-}" ]]; then
+    echo -e "size=$signature_size, type=$SIGNATURE_GUID" | sfdisk "$MINIMAL_IMAGE.gpt" --append
 fi
-sfdisk --part-label "${image}.gpt" 1 "Root Partition"
-sfdisk --part-label "${image}.gpt" 2 "Verity Partition"
-if [ "${HAVE_OPENSSL}" -eq 1 ]; then
-    sfdisk --part-label "${image}.gpt" 3 "Signature Partition"
+sfdisk --part-label "$MINIMAL_IMAGE.gpt" 1 "Root Partition"
+sfdisk --part-label "$MINIMAL_IMAGE.gpt" 2 "Verity Partition"
+if [[ -n "${OPENSSL_CONFIG:-}" ]]; then
+    sfdisk --part-label "$MINIMAL_IMAGE.gpt" 3 "Signature Partition"
 fi
-loop="$(losetup --show -P -f "${image}.gpt")"
+loop="$(losetup --show -P -f "$MINIMAL_IMAGE.gpt")"
 partitions=(
     "${loop:?}p1"
     "${loop:?}p2"
 )
-if [ "${HAVE_OPENSSL}" -eq 1 ]; then
-    partitions+=( "${loop:?}p3" )
+if [[ -n "${OPENSSL_CONFIG:-}" ]]; then
+    partitions+=("${loop:?}p3")
 fi
 # The kernel sometimes(?) does not emit "add" uevent for loop block partition devices.
 # Let's not expect the devices to be initialized.
 udevadm wait --timeout 60 --settle --initialized=no "${partitions[@]}"
-udevadm lock --device="${loop}p1" dd if="${image}.raw" of="${loop}p1"
-udevadm lock --device="${loop}p2" dd if="${image}.verity" of="${loop}p2"
-if [ "${HAVE_OPENSSL}" -eq 1 ]; then
-    udevadm lock --device="${loop}p3" dd if="${image}.verity-sig" of="${loop}p3"
-fi
-losetup -d "${loop}"
-
-# Derive partition UUIDs from root hash, in UUID syntax
-ROOT_UUID="$(systemd-id128 -u show "$(head -c 32 "${image}.roothash")" -u | tail -n 1 | cut -b 6-)"
-VERITY_UUID="$(systemd-id128 -u show "$(tail -c 32 "${image}.roothash")" -u | tail -n 1 | cut -b 6-)"
-
-systemd-dissect --json=short --root-hash "${roothash}" "${image}.gpt" | grep -q '{"rw":"ro","designator":"root","partition_uuid":"'"$ROOT_UUID"'","partition_label":"Root Partition","fstype":"squashfs","architecture":"'"$architecture"'","verity":"signed",'
-systemd-dissect --json=short --root-hash "${roothash}" "${image}.gpt" | grep -q '{"rw":"ro","designator":"root-verity","partition_uuid":"'"$VERITY_UUID"'","partition_label":"Verity Partition","fstype":"DM_verity_hash","architecture":"'"$architecture"'","verity":null,'
-if [ "${HAVE_OPENSSL}" -eq 1 ]; then
-    systemd-dissect --json=short --root-hash "${roothash}" "${image}.gpt" | grep -q -E '{"rw":"ro","designator":"root-verity-sig","partition_uuid":"'".*"'","partition_label":"Signature Partition","fstype":"verity_hash_signature","architecture":"'"$architecture"'","verity":null,'
-fi
-systemd-dissect --root-hash "${roothash}" "${image}.gpt" | grep -q -F "MARKER=1"
-systemd-dissect --root-hash "${roothash}" "${image}.gpt" | grep -q -F -f <(sed 's/"//g' "$os_release")
-
-# Test image policies
-systemd-dissect --validate "${image}.gpt"
-systemd-dissect --validate "${image}.gpt" --image-policy='*'
-(! systemd-dissect --validate "${image}.gpt" --image-policy='~')
-(! systemd-dissect --validate "${image}.gpt" --image-policy='-')
-(! systemd-dissect --validate "${image}.gpt" --image-policy=root=absent)
-(! systemd-dissect --validate "${image}.gpt" --image-policy=swap=unprotected+encrypted+verity)
-systemd-dissect --validate "${image}.gpt" --image-policy=root=unprotected
-systemd-dissect --validate "${image}.gpt" --image-policy=root=verity
-systemd-dissect --validate "${image}.gpt" --image-policy=root=verity:root-verity-sig=unused+absent
-systemd-dissect --validate "${image}.gpt" --image-policy=root=verity:swap=absent
-systemd-dissect --validate "${image}.gpt" --image-policy=root=verity:swap=absent+unprotected
-(! systemd-dissect --validate "${image}.gpt" --image-policy=root=verity:root-verity=unused+absent)
-systemd-dissect --validate "${image}.gpt" --image-policy=root=signed
-(! systemd-dissect --validate "${image}.gpt" --image-policy=root=signed:root-verity-sig=unused+absent)
-(! systemd-dissect --validate "${image}.gpt" --image-policy=root=signed:root-verity=unused+absent)
-
-# Test RootImagePolicy= unit file setting
-systemd-run --wait -P -p RootImage="${image}.gpt" -p RootHash="${roothash}" -p MountAPIVFS=yes cat /usr/lib/os-release | grep -q -F "MARKER=1"
-systemd-run --wait -P -p RootImage="${image}.gpt" -p RootHash="${roothash}" -p RootImagePolicy='*' -p MountAPIVFS=yes cat /usr/lib/os-release | grep -q -F "MARKER=1"
-(! systemd-run --wait -P -p RootImage="${image}.gpt" -p RootHash="${roothash}" -p RootImagePolicy='~' -p MountAPIVFS=yes cat /usr/lib/os-release | grep -q -F "MARKER=1")
-(! systemd-run --wait -P -p RootImage="${image}.gpt" -p RootHash="${roothash}" -p RootImagePolicy='-' -p MountAPIVFS=yes cat /usr/lib/os-release | grep -q -F "MARKER=1")
-(! systemd-run --wait -P -p RootImage="${image}.gpt" -p RootHash="${roothash}" -p RootImagePolicy='root=absent' -p MountAPIVFS=yes cat /usr/lib/os-release | grep -q -F "MARKER=1")
-systemd-run --wait -P -p RootImage="${image}.gpt" -p RootHash="${roothash}" -p RootImagePolicy='root=verity' -p MountAPIVFS=yes cat /usr/lib/os-release | grep -q -F "MARKER=1"
-systemd-run --wait -P -p RootImage="${image}.gpt" -p RootHash="${roothash}" -p RootImagePolicy='root=signed' -p MountAPIVFS=yes cat /usr/lib/os-release | grep -q -F "MARKER=1"
-(! systemd-run --wait -P -p RootImage="${image}.gpt" -p RootHash="${roothash}" -p RootImagePolicy='root=encrypted' -p MountAPIVFS=yes cat /usr/lib/os-release | grep -q -F "MARKER=1")
-
-systemd-dissect --root-hash "${roothash}" --mount "${image}.gpt" "${image_dir}/mount"
-grep -q -F -f "$os_release" "${image_dir}/mount/usr/lib/os-release"
-grep -q -F -f "$os_release" "${image_dir}/mount/etc/os-release"
-grep -q -F "MARKER=1" "${image_dir}/mount/usr/lib/os-release"
-systemd-dissect --umount "${image_dir}/mount"
-
-systemd-dissect --root-hash "${roothash}" --mount "${image}.gpt" --in-memory "${image_dir}/mount"
-grep -q -F -f "$os_release" "${image_dir}/mount/usr/lib/os-release"
-grep -q -F -f "$os_release" "${image_dir}/mount/etc/os-release"
-grep -q -F "MARKER=1" "${image_dir}/mount/usr/lib/os-release"
-systemd-dissect --umount "${image_dir}/mount"
-
-# add explicit -p MountAPIVFS=yes once to test the parser
-systemd-run -P -p RootImage="${image}.gpt" -p RootHash="${roothash}" -p MountAPIVFS=yes cat /usr/lib/os-release | grep -q -F "MARKER=1"
-
-systemd-run -P -p RootImage="${image}.raw" -p RootImageOptions="root:nosuid,dev home:ro,dev ro,noatime" mount | grep -F "squashfs" | grep -q -F "nosuid"
-systemd-run -P -p RootImage="${image}.gpt" -p RootImageOptions="root:ro,noatime root:ro,dev" mount | grep -F "squashfs" | grep -q -F "noatime"
-
-mkdir -p "${image_dir}/result"
-cat >/run/systemd/system/testservice-50a.service <<EOF
-[Service]
-Type=oneshot
-ExecStart=bash -c "mount >/run/result/a"
-BindPaths=${image_dir}/result:/run/result
-TemporaryFileSystem=/run
-RootImage=${image}.raw
-RootImageOptions=root:ro,noatime home:ro,dev relatime,dev
-RootImageOptions=nosuid,dev
-EOF
-systemctl start testservice-50a.service
-grep -F "squashfs" "${image_dir}/result/a" | grep -q -F "noatime"
-grep -F "squashfs" "${image_dir}/result/a" | grep -q -F -v "nosuid"
-
-cat >/run/systemd/system/testservice-50b.service <<EOF
-[Service]
-Type=oneshot
-ExecStart=bash -c "mount >/run/result/b"
-BindPaths=${image_dir}/result:/run/result
-TemporaryFileSystem=/run
-RootImage=${image}.gpt
-RootImageOptions=root:ro,noatime,nosuid home:ro,dev nosuid,dev
-RootImageOptions=home:ro,dev nosuid,dev,%%foo
-# this is the default, but let's specify once to test the parser
-MountAPIVFS=yes
-EOF
-systemctl start testservice-50b.service
-grep -F "squashfs" "${image_dir}/result/b" | grep -q -F "noatime"
-
-# Check that specifier escape is applied %%foo → %foo
-busctl get-property org.freedesktop.systemd1 /org/freedesktop/systemd1/unit/testservice_2d50b_2eservice org.freedesktop.systemd1.Service RootImageOptions | grep -F "nosuid,dev,%foo"
-
-# Now do some checks with MountImages, both by itself, with options and in combination with RootImage, and as single FS or GPT image
-systemd-run -P -p MountImages="${image}.gpt:/run/img1 ${image}.raw:/run/img2" cat /run/img1/usr/lib/os-release | grep -q -F "MARKER=1"
-systemd-run -P -p MountImages="${image}.gpt:/run/img1 ${image}.raw:/run/img2" cat /run/img2/usr/lib/os-release | grep -q -F "MARKER=1"
-systemd-run -P -p MountImages="${image}.gpt:/run/img1 ${image}.raw:/run/img2:nosuid,dev" mount | grep -F "squashfs" | grep -q -F "nosuid"
-systemd-run -P -p MountImages="${image}.gpt:/run/img1:root:nosuid ${image}.raw:/run/img2:home:suid" mount | grep -F "squashfs" | grep -q -F "nosuid"
-systemd-run -P -p MountImages="${image}.raw:/run/img2\:3" cat /run/img2:3/usr/lib/os-release | grep -q -F "MARKER=1"
-systemd-run -P -p MountImages="${image}.raw:/run/img2\:3:nosuid" mount | grep -F "squashfs" | grep -q -F "nosuid"
-systemd-run -P -p TemporaryFileSystem=/run -p RootImage="${image}.raw" -p MountImages="${image}.gpt:/run/img1 ${image}.raw:/run/img2" cat /usr/lib/os-release | grep -q -F "MARKER=1"
-systemd-run -P -p TemporaryFileSystem=/run -p RootImage="${image}.raw" -p MountImages="${image}.gpt:/run/img1 ${image}.raw:/run/img2" cat /run/img1/usr/lib/os-release | grep -q -F "MARKER=1"
-systemd-run -P -p TemporaryFileSystem=/run -p RootImage="${image}.gpt" -p RootHash="${roothash}" -p MountImages="${image}.gpt:/run/img1 ${image}.raw:/run/img2" cat /run/img2/usr/lib/os-release | grep -q -F "MARKER=1"
-cat >/run/systemd/system/testservice-50c.service <<EOF
-[Service]
-MountAPIVFS=yes
-TemporaryFileSystem=/run
-RootImage=${image}.raw
-MountImages=${image}.gpt:/run/img1:root:noatime:home:relatime
-MountImages=${image}.raw:/run/img2\:3:nosuid
-ExecStart=bash -c "cat /run/img1/usr/lib/os-release >/run/result/c"
-ExecStart=bash -c "cat /run/img2:3/usr/lib/os-release >>/run/result/c"
-ExecStart=bash -c "mount >>/run/result/c"
-BindPaths=${image_dir}/result:/run/result
-Type=oneshot
-EOF
-systemctl start testservice-50c.service
-grep -q -F "MARKER=1" "${image_dir}/result/c"
-grep -F "squashfs" "${image_dir}/result/c" | grep -q -F "noatime"
-grep -F "squashfs" "${image_dir}/result/c" | grep -q -F -v "nosuid"
-
-# Adding a new mounts at runtime works if the unit is in the active state,
-# so use Type=notify to make sure there's no race condition in the test
-cat >/run/systemd/system/testservice-50d.service <<EOF
-[Service]
-RuntimeMaxSec=300
-Type=notify
-RemainAfterExit=yes
-MountAPIVFS=yes
-PrivateTmp=yes
-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; \\
-    done; \\
-    mount; \\
-    mount | grep -F "on /tmp/img type squashfs" | grep -q -F "nosuid"; \\
-'
-EOF
-systemctl start testservice-50d.service
-
-# Mount twice to exercise mount-beneath (on kernel 6.5+, on older kernels it will just overmount)
-mkdir -p /tmp/wrong/foo
-mksquashfs /tmp/wrong/foo /tmp/wrong.raw
-systemctl mount-image --mkdir testservice-50d.service /tmp/wrong.raw /tmp/img
-test "$(systemctl show -P SubState testservice-50d.service)" = "running"
-systemctl mount-image --mkdir testservice-50d.service "${image}.raw" /tmp/img root:nosuid
-
-while systemctl show -P SubState testservice-50d.service | grep -q running
-do
-    sleep 0.1
-done
-
-systemctl is-active testservice-50d.service
-
-# ExtensionImages will set up an overlay
-systemd-run -P --property ExtensionImages=/usr/share/app0.raw --property RootImage="${image}.raw" cat /opt/script0.sh | grep -q -F "extension-release.app0"
-systemd-run -P --property ExtensionImages=/usr/share/app0.raw --property RootImage="${image}.raw" cat /usr/lib/systemd/system/some_file | grep -q -F "MARKER=1"
-systemd-run -P --property ExtensionImages="/usr/share/app0.raw /usr/share/app1.raw" --property RootImage="${image}.raw" cat /opt/script0.sh | grep -q -F "extension-release.app0"
-systemd-run -P --property ExtensionImages="/usr/share/app0.raw /usr/share/app1.raw" --property RootImage="${image}.raw" cat /usr/lib/systemd/system/some_file | grep -q -F "MARKER=1"
-systemd-run -P --property ExtensionImages="/usr/share/app0.raw /usr/share/app1.raw" --property RootImage="${image}.raw" cat /opt/script1.sh | grep -q -F "extension-release.app2"
-systemd-run -P --property ExtensionImages="/usr/share/app0.raw /usr/share/app1.raw" --property RootImage="${image}.raw" cat /usr/lib/systemd/system/other_file | grep -q -F "MARKER=1"
-systemd-run -P --property ExtensionImages=/usr/share/app-nodistro.raw --property RootImage="${image}.raw" cat /usr/lib/systemd/system/some_file | grep -q -F "MARKER=1"
-systemd-run -P --property ExtensionImages=/etc/service-scoped-test.raw --property RootImage="${image}.raw" cat /etc/systemd/system/some_file | grep -q -F "MARKER_CONFEXT_123"
-# Check that using a symlink to NAME-VERSION.raw works as long as the symlink has the correct name NAME.raw
-mkdir -p /usr/share/symlink-test/
-cp /usr/share/app-nodistro.raw /usr/share/symlink-test/app-nodistro-v1.raw
-ln -fs /usr/share/symlink-test/app-nodistro-v1.raw /usr/share/symlink-test/app-nodistro.raw
-systemd-run -P --property ExtensionImages=/usr/share/symlink-test/app-nodistro.raw --property RootImage="${image}.raw" cat /usr/lib/systemd/system/some_file | grep -q -F "MARKER=1"
-
-# Symlink check again but for confext
-mkdir -p /etc/symlink-test/
-cp /etc/service-scoped-test.raw /etc/symlink-test/service-scoped-test-v1.raw
-ln -fs /etc/symlink-test/service-scoped-test-v1.raw /etc/symlink-test/service-scoped-test.raw
-systemd-run -P --property ExtensionImages=/etc/symlink-test/service-scoped-test.raw --property RootImage="${image}.raw" cat /etc/systemd/system/some_file | grep -q -F "MARKER_CONFEXT_123"
-# And again mixing sysext and confext
-systemd-run -P \
-    --property ExtensionImages=/usr/share/symlink-test/app-nodistro.raw \
-    --property ExtensionImages=/etc/symlink-test/service-scoped-test.raw \
-    --property RootImage="${image}.raw" cat /etc/systemd/system/some_file | grep -q -F "MARKER_CONFEXT_123"
-systemd-run -P \
-    --property ExtensionImages=/usr/share/symlink-test/app-nodistro.raw \
-    --property ExtensionImages=/etc/symlink-test/service-scoped-test.raw \
-    --property RootImage="${image}.raw" cat /usr/lib/systemd/system/some_file | grep -q -F "MARKER=1"
-
-cat >/run/systemd/system/testservice-50e.service <<EOF
-[Service]
-MountAPIVFS=yes
-TemporaryFileSystem=/run /var/lib
-StateDirectory=app0
-RootImage=${image}.raw
-ExtensionImages=/usr/share/app0.raw /usr/share/app1.raw:nosuid
-# Relevant only for sanitizer runs
-UnsetEnvironment=LD_PRELOAD
-ExecStart=bash -c '/opt/script0.sh | grep ID'
-ExecStart=bash -c '/opt/script1.sh | grep ID'
-Type=oneshot
-RemainAfterExit=yes
-EOF
-systemctl start testservice-50e.service
-systemctl is-active testservice-50e.service
-
-# Check vpick support in ExtensionImages=
-VBASE="vtest$RANDOM"
-VDIR="/tmp/${VBASE}.v"
-mkdir "$VDIR"
-
-ln -s /usr/share/app0.raw "$VDIR/${VBASE}_0.raw"
-ln -s /usr/share/app1.raw "$VDIR/${VBASE}_1.raw"
-
-systemd-run -P -p ExtensionImages="$VDIR" bash -c '/opt/script1.sh | grep ID'
-
-rm -rf "$VDIR"
-
-# ExtensionDirectories will set up an overlay
-mkdir -p "${image_dir}/app0" "${image_dir}/app1" "${image_dir}/app-nodistro" "${image_dir}/service-scoped-test"
-(! systemd-run -P --property ExtensionDirectories="${image_dir}/nonexistent" --property RootImage="${image}.raw" cat /opt/script0.sh)
-(! systemd-run -P --property ExtensionDirectories="${image_dir}/app0" --property RootImage="${image}.raw" cat /opt/script0.sh)
-systemd-dissect --mount /usr/share/app0.raw "${image_dir}/app0"
-systemd-dissect --mount /usr/share/app1.raw "${image_dir}/app1"
-systemd-dissect --mount /usr/share/app-nodistro.raw "${image_dir}/app-nodistro"
-systemd-dissect --mount /etc/service-scoped-test.raw "${image_dir}/service-scoped-test"
-systemd-run -P --property ExtensionDirectories="${image_dir}/app0" --property RootImage="${image}.raw" cat /opt/script0.sh | grep -q -F "extension-release.app0"
-systemd-run -P --property ExtensionDirectories="${image_dir}/app0" --property RootImage="${image}.raw" cat /usr/lib/systemd/system/some_file | grep -q -F "MARKER=1"
-systemd-run -P --property ExtensionDirectories="${image_dir}/app0 ${image_dir}/app1" --property RootImage="${image}.raw" cat /opt/script0.sh | grep -q -F "extension-release.app0"
-systemd-run -P --property ExtensionDirectories="${image_dir}/app0 ${image_dir}/app1" --property RootImage="${image}.raw" cat /usr/lib/systemd/system/some_file | grep -q -F "MARKER=1"
-systemd-run -P --property ExtensionDirectories="${image_dir}/app0 ${image_dir}/app1" --property RootImage="${image}.raw" cat /opt/script1.sh | grep -q -F "extension-release.app2"
-systemd-run -P --property ExtensionDirectories="${image_dir}/app0 ${image_dir}/app1" --property RootImage="${image}.raw" cat /usr/lib/systemd/system/other_file | grep -q -F "MARKER=1"
-systemd-run -P --property ExtensionDirectories="${image_dir}/app-nodistro" --property RootImage="${image}.raw" cat /usr/lib/systemd/system/some_file | grep -q -F "MARKER=1"
-systemd-run -P --property ExtensionDirectories="${image_dir}/service-scoped-test" --property RootImage="${image}.raw" cat /etc/systemd/system/some_file | grep -q -F "MARKER_CONFEXT_123"
-cat >/run/systemd/system/testservice-50f.service <<EOF
-[Service]
-MountAPIVFS=yes
-TemporaryFileSystem=/run /var/lib
-StateDirectory=app0
-RootImage=${image}.raw
-ExtensionDirectories=${image_dir}/app0 ${image_dir}/app1
-# Relevant only for sanitizer runs
-UnsetEnvironment=LD_PRELOAD
-ExecStart=bash -c '/opt/script0.sh | grep ID'
-ExecStart=bash -c '/opt/script1.sh | grep ID'
-Type=oneshot
-RemainAfterExit=yes
-EOF
-systemctl start testservice-50f.service
-systemctl is-active testservice-50f.service
-
-# Check vpick support in ExtensionDirectories=
-VBASE="vtest$RANDOM"
-VDIR="/tmp/${VBASE}.v"
-mkdir "$VDIR"
-
-ln -s "${image_dir}/app0" "$VDIR/${VBASE}_0"
-ln -s "${image_dir}/app1" "$VDIR/${VBASE}_1"
-
-systemd-run -P --property ExtensionDirectories="$VDIR" cat /opt/script1.sh | grep -q -F "extension-release.app2"
-
-rm -rf "$VDIR"
-
-systemd-dissect --umount "${image_dir}/app0"
-systemd-dissect --umount "${image_dir}/app1"
-
-# Test that an extension consisting of an empty directory under /etc/extensions/ takes precedence
-mkdir -p /var/lib/extensions/
-ln -s /usr/share/app-nodistro.raw /var/lib/extensions/app-nodistro.raw
-systemd-sysext merge
-grep -q -F "MARKER=1" /usr/lib/systemd/system/some_file
-systemd-sysext unmerge
-mkdir -p /etc/extensions/app-nodistro
-systemd-sysext merge
-test ! -e /usr/lib/systemd/system/some_file
-systemd-sysext unmerge
-rmdir /etc/extensions/app-nodistro
-
-# Similar, but go via varlink
-varlinkctl call /run/systemd/io.systemd.sysext io.systemd.sysext.List '{}'
-(! grep -q -F "MARKER=1" /usr/lib/systemd/system/some_file )
-varlinkctl call /run/systemd/io.systemd.sysext io.systemd.sysext.Merge '{}'
-grep -q -F "MARKER=1" /usr/lib/systemd/system/some_file
-varlinkctl call /run/systemd/io.systemd.sysext io.systemd.sysext.Refresh '{}'
-grep -q -F "MARKER=1" /usr/lib/systemd/system/some_file
-varlinkctl call /run/systemd/io.systemd.sysext io.systemd.sysext.Unmerge '{}'
-(! grep -q -F "MARKER=1" /usr/lib/systemd/system/some_file )
-
-# Check that extensions cannot contain os-release
-mkdir -p /run/extensions/app-reject/usr/lib/{extension-release.d/,systemd/system}
-echo "ID=_any" >/run/extensions/app-reject/usr/lib/extension-release.d/extension-release.app-reject
-echo "ID=_any" >/run/extensions/app-reject/usr/lib/os-release
-touch /run/extensions/app-reject/usr/lib/systemd/system/other_file
-(! systemd-sysext merge)
-test ! -e /usr/lib/systemd/system/some_file
-test ! -e /usr/lib/systemd/system/other_file
-systemd-sysext unmerge
-rm -rf /run/extensions/app-reject
-rm /var/lib/extensions/app-nodistro.raw
-
-# Some super basic test that RootImage= works with .v/ dirs
-VBASE="vtest$RANDOM"
-VDIR="/tmp/${VBASE}.v"
-mkdir "$VDIR"
-
-ln -s "${image}.raw" "$VDIR/${VBASE}_33.raw"
-ln -s "${image}.raw" "$VDIR/${VBASE}_34.raw"
-ln -s "${image}.raw" "$VDIR/${VBASE}_35.raw"
-
-systemd-run -P -p RootImage="$VDIR" cat /usr/lib/os-release | grep -q -F "MARKER=1"
-
-rm "$VDIR/${VBASE}_33.raw" "$VDIR/${VBASE}_34.raw" "$VDIR/${VBASE}_35.raw"
-rmdir "$VDIR"
-
-mkdir -p /run/machines /run/portables /run/extensions
-touch /run/machines/a.raw /run/portables/b.raw /run/extensions/c.raw
-
-systemd-dissect --discover --json=short >/tmp/discover.json
-grep -q -F '{"name":"a","type":"raw","class":"machine","ro":false,"path":"/run/machines/a.raw"' /tmp/discover.json
-grep -q -F '{"name":"b","type":"raw","class":"portable","ro":false,"path":"/run/portables/b.raw"' /tmp/discover.json
-grep -q -F '{"name":"c","type":"raw","class":"sysext","ro":false,"path":"/run/extensions/c.raw"' /tmp/discover.json
-rm /tmp/discover.json /run/machines/a.raw /run/portables/b.raw /run/extensions/c.raw
-
-# Check that the /sbin/mount.ddi helper works
-T="/tmp/mounthelper.$RANDOM"
-mount -t ddi "${image}.gpt" "$T" -o ro,X-mount.mkdir,discard
-umount -R "$T"
-rmdir "$T"
-
-LOOP="$(systemd-dissect --attach --loop-ref=waldo "${image}.raw")"
-
-# Wait until the symlinks we want to test are established
-udevadm trigger -w "$LOOP"
-
-# Check if the /dev/loop/* symlinks really reference the right device
-test /dev/disk/by-loop-ref/waldo -ef "$LOOP"
-
-if [ "$(stat -c '%Hd:%Ld' "${image}.raw")" != '?d:?d' ] ; then
-   # Old stat didn't know the %Hd and %Ld specifiers and turned them into ?d
-   # instead. Let's simply skip the test on such old systems.
-   test "$(stat -c '/dev/disk/by-loop-inode/%Hd:%Ld-%i' "${image}.raw")" -ef "$LOOP"
-fi
-
-# Detach by loopback device
-systemd-dissect --detach "$LOOP"
-
-# Test long reference name.
-# Note, sizeof_field(struct loop_info64, lo_file_name) == 64,
-# and --loop-ref accepts upto 63 characters, and udev creates symlink
-# based on the name when it has upto _62_ characters.
-name="$(for _ in {1..62}; do echo -n 'x'; done)"
-LOOP="$(systemd-dissect --attach --loop-ref="$name" "${image}.raw")"
-udevadm trigger -w "$LOOP"
-
-# Check if the /dev/disk/by-loop-ref/$name symlink really references the right device
-test "/dev/disk/by-loop-ref/$name" -ef "$LOOP"
-
-# Detach by the /dev/disk/by-loop-ref symlink
-systemd-dissect --detach "/dev/disk/by-loop-ref/$name"
-
-name="$(for _ in {1..63}; do echo -n 'x'; done)"
-LOOP="$(systemd-dissect --attach --loop-ref="$name" "${image}.raw")"
-udevadm trigger -w "$LOOP"
-
-# Check if the /dev/disk/by-loop-ref/$name symlink does not exist
-test ! -e "/dev/disk/by-loop-ref/$name"
-
-# Detach by backing inode
-systemd-dissect --detach "${image}.raw"
-(! systemd-dissect --detach "${image}.raw")
-
-# check for confext functionality
-mkdir -p /run/confexts/test/etc/extension-release.d
-echo "ID=_any" >/run/confexts/test/etc/extension-release.d/extension-release.test
-echo "ARCHITECTURE=_any" >>/run/confexts/test/etc/extension-release.d/extension-release.test
-echo "MARKER_CONFEXT_123" >/run/confexts/test/etc/testfile
-cat <<EOF >/run/confexts/test/etc/testscript
-#!/bin/bash
-echo "This should not happen"
-EOF
-chmod +x /run/confexts/test/etc/testscript
-systemd-confext merge
-grep -q -F "MARKER_CONFEXT_123" /etc/testfile
-(! /etc/testscript)
-systemd-confext status
-systemd-confext unmerge
-rm -rf /run/confexts/
-
-unsquashfs -no-xattrs -d /tmp/img "${image}.raw"
-systemd-run --unit=test-root-ephemeral \
-    -p RootDirectory=/tmp/img \
-    -p RootEphemeral=yes \
-    -p Type=exec \
-    bash -c "touch /abc && sleep infinity"
-test -n "$(ls -A /var/lib/systemd/ephemeral-trees)"
-systemctl stop test-root-ephemeral
-# shellcheck disable=SC2016
-timeout 10 bash -c 'until test -z "$(ls -A /var/lib/systemd/ephemeral-trees)"; do sleep .5; done'
-test ! -f /tmp/img/abc
-
-systemd-dissect --mtree /tmp/img
-systemd-dissect --list /tmp/img
-
-read -r SHA256SUM1 _ < <(systemd-dissect --copy-from /tmp/img etc/os-release | sha256sum)
-test "$SHA256SUM1" != ""
-
-echo abc > abc
-systemd-dissect --copy-to /tmp/img abc /abc
-test -f /tmp/img/abc
-
-# Test for dissect tool support with systemd-sysext
-mkdir -p /run/extensions/ testkit/usr/lib/extension-release.d/
-echo "ID=_any" >testkit/usr/lib/extension-release.d/extension-release.testkit
-echo "ARCHITECTURE=_any" >>testkit/usr/lib/extension-release.d/extension-release.testkit
-echo "MARKER_SYSEXT_123" >testkit/usr/lib/testfile
-mksquashfs testkit/ testkit.raw
-cp testkit.raw /run/extensions/
-unsquashfs -l /run/extensions/testkit.raw
-systemd-dissect --no-pager /run/extensions/testkit.raw | grep -q '✓ sysext for portable service'
-systemd-dissect --no-pager /run/extensions/testkit.raw | grep -q '✓ sysext for system'
-systemd-sysext merge
-systemd-sysext status
-grep -q -F "MARKER_SYSEXT_123" /usr/lib/testfile
-systemd-sysext unmerge
-rm -rf /run/extensions/ testkit/
-
-# Test for dissect tool support with systemd-confext
-mkdir -p /run/confexts/ testjob/etc/extension-release.d/
-echo "ID=_any" >testjob/etc/extension-release.d/extension-release.testjob
-echo "ARCHITECTURE=_any" >>testjob/etc/extension-release.d/extension-release.testjob
-echo "MARKER_CONFEXT_123" >testjob/etc/testfile
-mksquashfs testjob/ testjob.raw
-cp testjob.raw /run/confexts/
-unsquashfs -l /run/confexts/testjob.raw
-systemd-dissect --no-pager /run/confexts/testjob.raw | grep -q '✓ confext for system'
-systemd-dissect --no-pager /run/confexts/testjob.raw | grep -q '✓ confext for portable service'
-systemd-confext merge
-systemd-confext status
-grep -q -F "MARKER_CONFEXT_123" /etc/testfile
-systemd-confext unmerge
-rm -rf /run/confexts/ testjob/
-
-systemd-run -P -p RootImage="${image}.raw" cat /run/host/os-release | cmp "${os_release}"
-
-# Test that systemd-sysext reloads the daemon.
-mkdir -p /var/lib/extensions/
-ln -s /usr/share/app-reload.raw /var/lib/extensions/app-reload.raw
-systemd-sysext merge --no-reload
-# the service should not be running
-if systemctl --quiet is-active foo.service; then
-    echo "foo.service should not be active"
-    exit 1
-fi
-systemd-sysext unmerge --no-reload
-systemd-sysext merge
-for RETRY in $(seq 60) LAST; do
-  if journalctl --boot --unit foo.service | grep -q -P 'echo\[[0-9]+\]: foo'; then
-    break
-  fi
-  if [ "${RETRY}" = LAST ]; then
-    echo "Output of foo.service not found"
-    exit 1
-  fi
-  sleep 0.5
-done
-systemd-sysext unmerge --no-reload
-# Grep on the Warning to find the warning helper mentioning the daemon reload.
-systemctl status foo.service 2>&1 | grep -q -F "Warning"
-systemd-sysext merge
-systemd-sysext unmerge
-systemctl status foo.service 2>&1 | grep -v -q -F "Warning"
-rm /var/lib/extensions/app-reload.raw
-
-# Test systemd-repart --make-ddi=:
-if command -v mksquashfs >/dev/null 2>&1; then
-
-    openssl req -config "$OPENSSL_CONFIG" -subj="/CN=waldo" -x509 -sha256 -nodes -days 365 -newkey rsa:4096 -keyout /tmp/test-50-privkey.key -out /tmp/test-50-cert.crt
-
-    mkdir -p /tmp/test-50-confext/etc/extension-release.d/
-
-    echo "foobar50" > /tmp/test-50-confext/etc/waldo
-
-    ( grep -e '^\(ID\|VERSION_ID\)=' /etc/os-release ; echo IMAGE_ID=waldo ; echo IMAGE_VERSION=7 ) > /tmp/test-50-confext/etc/extension-release.d/extension-release.waldo
-
-    mkdir -p /run/confexts
-
-    SYSTEMD_REPART_OVERRIDE_FSTYPE=squashfs systemd-repart -C -s /tmp/test-50-confext --certificate=/tmp/test-50-cert.crt --private-key=/tmp/test-50-privkey.key /run/confexts/waldo.confext.raw
-    rm -rf /tmp/test-50-confext
-
-    mkdir -p /run/verity.d
-    cp /tmp/test-50-cert.crt /run/verity.d/
-    systemd-dissect --mtree /run/confexts/waldo.confext.raw
-
-    systemd-confext refresh
-
-    read -r X < /etc/waldo
-    test "$X" = foobar50
-
-    rm /run/confexts/waldo.confext.raw
-
-    systemd-confext refresh
-
-    (! test -f /etc/waldo )
-
-    mkdir -p /tmp/test-50-sysext/usr/lib/extension-release.d/
-
-    # Make sure the sysext is big enough to not fit in the minimum partition size of repart so we know the
-    # Minimize= logic is working.
-    truncate --size=50M /tmp/test-50-sysext/usr/waldo
-
-    ( grep -e '^\(ID\|VERSION_ID\)=' /etc/os-release ; echo IMAGE_ID=waldo ; echo IMAGE_VERSION=7 ) > /tmp/test-50-sysext/usr/lib/extension-release.d/extension-release.waldo
-
-    mkdir -p /run/extensions
-
-    SYSTEMD_REPART_OVERRIDE_FSTYPE=squashfs systemd-repart -S -s /tmp/test-50-sysext --certificate=/tmp/test-50-cert.crt --private-key=/tmp/test-50-privkey.key /run/extensions/waldo.sysext.raw
-
-    systemd-dissect --mtree /run/extensions/waldo.sysext.raw
-
-    systemd-sysext refresh
-
-    test -f /usr/waldo
-
-    rm /run/verity.d/test-50-cert.crt /run/extensions/waldo.sysext.raw /tmp/test-50-cert.crt /tmp/test-50-privkey.key
-
-    systemd-sysext refresh
-
-    (! test -f /usr/waldo)
+udevadm lock --device="${loop}p1" dd if="$MINIMAL_IMAGE.raw" of="${loop}p1"
+udevadm lock --device="${loop}p2" dd if="$MINIMAL_IMAGE.verity" of="${loop}p2"
+if [[ -n "${OPENSSL_CONFIG:-}" ]]; then
+    udevadm lock --device="${loop}p3" dd if="$MINIMAL_IMAGE.verity-sig" of="${loop}p3"
 fi
+losetup -d "$loop"
 
-# Sneak in a couple of expected-to-fail invocations to cover
-# https://github.com/systemd/systemd/issues/29610
-(! systemd-run -P -p MountImages="/this/should/definitely/not/exist.img:/run/img2\:3:nosuid" false)
-(! systemd-run -P -p ExtensionImages="/this/should/definitely/not/exist.img" false)
-(! systemd-run -P -p RootImage="/this/should/definitely/not/exist.img" false)
-(! systemd-run -P -p ExtensionDirectories="/foo/bar /foo/baz" false)
-
-# general systemd-sysext tests
-
-shopt -s extglob
-
-die() {
-    echo "${*}"
-    exit 1
-}
-
-prep_root() {
-    local r=${1}; shift
-    local h=${1}; shift
-
-    if [[ -d ${r} ]]; then
-        die "${r@Q} is being reused as a root, possibly a result of copy-pasting some test case and forgetting to rename the root directory name"
-    fi
-
-    mkdir -p "${r}${h}" "${r}/usr/lib" "${r}/var/lib/extensions" "${r}/var/lib/extensions.mutable"
-}
-
-prep_env() {
-    local mode=${1}; shift
-
-    export SYSTEMD_SYSEXT_MUTABLE_MODE=${mode}
-}
-
-drop_env() {
-    unset -v SYSTEMD_SYSEXT_MUTABLE_MODE
-}
-
-gen_os_release() {
-    local r=${1}; shift
-
-    {
-        echo "ID=testtest"
-        echo "VERSION=1.2.3"
-    } >"${r}/usr/lib/os-release"
-}
-
-gen_test_ext_image() {
-    local r=${1}; shift
-    local h=${1}; shift
-
-    local n d f
-
-    n='test-extension'
-    d="${r}/var/lib/extensions/${n}"
-    f="${d}/usr/lib/extension-release.d/extension-release.${n}"
-    mkdir -p "$(dirname "${f}")"
-    echo "ID=_any" >"${f}"
-    mkdir -p "${d}/${h}"
-    touch "${d}${h}/preexisting-file-in-extension-image"
-}
-
-hierarchy_ext_mut_path() {
-    local r=${1}; shift
-    local h=${1}; shift
-
-    # /a/b/c -> a.b.c
-    local n=${h}
-    n="${n##+(/)}"
-    n="${n%%+(/)}"
-    n="${n//\//.}"
-
-    printf '%s' "${r}/var/lib/extensions.mutable/${n}"
-}
-
-prep_ext_mut() {
-    local p=${1}; shift
-
-    mkdir -p "${p}"
-    touch "${p}/preexisting-file-in-extensions-mutable"
-}
-
-make_ro() {
-    local r=${1}; shift
-    local h=${1}; shift
-
-    mount -o bind "${r}${h}" "${r}${h}"
-    mount -o bind,remount,ro "${r}${h}"
-}
-
-prep_hierarchy() {
-    local r=${1}; shift
-    local h=${1}; shift
-
-    touch "${r}${h}/preexisting-file-in-hierarchy"
-}
-
-prep_ro_hierarchy() {
-    local r=${1}; shift
-    local h=${1}; shift
-
-    prep_hierarchy "${r}" "${h}"
-    make_ro "${r}" "${h}"
-}
-
-# extra args:
-# "e" for checking for the preexisting file in extension
-# "h" for checking for the preexisting file in hierarchy
-# "u" for checking for the preexisting file in upperdir
-check_usual_suspects() {
-    local root=${1}; shift
-    local hierarchy=${1}; shift
-    local message=${1}; shift
-
-    local arg
-    # shellcheck disable=SC2034 # the variables below are used indirectly
-    local e='' h='' u=''
-
-    for arg; do
-        case ${arg} in
-            e|h|u)
-                local -n v=${arg}
-                v=x
-                unset -n v
-                ;;
-            *)
-                die "invalid arg to ${0}: ${arg@Q}"
-                ;;
-        esac
-    done
-
-    # var name, file name
-    local pairs=(
-        e:preexisting-file-in-extension-image
-        h:preexisting-file-in-hierarchy
-        u:preexisting-file-in-extensions-mutable
-    )
-    local pair name file desc full_path
-    for pair in "${pairs[@]}"; do
-        name=${pair%%:*}
-        file=${pair#*:}
-        desc=${file//-/ }
-        full_path="${root}${hierarchy}/${file}"
-        local -n v=${name}
-        if [[ -n ${v} ]]; then
-            test -f "${full_path}" || {
-                ls -la "$(dirname "${full_path}")"
-                die "${desc} is missing ${message}"
-            }
-        else
-            test ! -f "${full_path}" || {
-                ls -la "$(dirname "${full_path}")"
-                die "${desc} unexpectedly exists ${message}"
-            }
-        fi
-        unset -n v
-    done
-}
-
-check_usual_suspects_after_merge() {
-    local r=${1}; shift
-    local h=${1}; shift
-
-    check_usual_suspects "${r}" "${h}" "after merge" "${@}"
-}
-
-check_usual_suspects_after_unmerge() {
-    local r=${1}; shift
-    local h=${1}; shift
-
-    check_usual_suspects "${r}" "${h}" "after unmerge" "${@}"
-}
-
-drop_env
-
-#
-# no extension data in /var/lib/extensions.mutable/…, read-only hierarchy,
-# mutability disabled by default
-#
-# read-only merged
-#
-
-
-fake_root=${fake_roots_dir}/simple-read-only-with-read-only-hierarchy
-hierarchy=/usr
-
-prep_root "${fake_root}" "${hierarchy}"
-gen_os_release "${fake_root}"
-gen_test_ext_image "${fake_root}" "${hierarchy}"
-
-prep_ro_hierarchy "${fake_root}" "${hierarchy}"
-
-touch "${fake_root}${hierarchy}/should-fail-on-read-only-fs" && die "${fake_root}${hierarchy} is not read-only"
-
-# run systemd-sysext
-SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" merge
-
-touch "${fake_root}${hierarchy}/should-still-fail-on-read-only-fs" && die "${fake_root}${hierarchy} is not read-only"
-check_usual_suspects_after_merge "${fake_root}" "${hierarchy}" e h
-
-SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" unmerge
-
-check_usual_suspects_after_unmerge "${fake_root}" "${hierarchy}" h
-
-touch "${fake_root}${hierarchy}/should-still-fail-on-read-only-fs" && die "${fake_root}${hierarchy} is not read-only after unmerge"
-
-
-#
-# no extension data in /var/lib/extensions.mutable/…, mutable hierarchy,
-# mutability disabled by default
-#
-# read-only merged
-#
-
-
-fake_root=${fake_roots_dir}/simple-read-only-with-mutable-hierarchy
-hierarchy=/usr
-
-prep_root "${fake_root}" "${hierarchy}"
-gen_os_release "${fake_root}"
-gen_test_ext_image "${fake_root}" "${hierarchy}"
-
-prep_hierarchy "${fake_root}" "${hierarchy}"
-
-touch "${fake_root}${hierarchy}/should-succeed-on-mutable-fs" || die "${fake_root}${hierarchy} is not mutable"
-
-# run systemd-sysext
-SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" merge
-
-touch "${fake_root}${hierarchy}/should-fail-on-read-only-fs" && die "${fake_root}${hierarchy} is not read-only"
-check_usual_suspects_after_merge "${fake_root}" "${hierarchy}" e h
-
-SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" unmerge
-
-check_usual_suspects_after_unmerge "${fake_root}" "${hierarchy}" h
-
-touch "${fake_root}${hierarchy}/should-succeed-on-mutable-fs-again" || die "${fake_root}${hierarchy} is not mutable after unmerge"
-
-
-#
-# no extension data in /var/lib/extensions.mutable/…, no hierarchy either,
-# mutability disabled by default
-#
-# read-only merged
-#
-
-
-fake_root=${fake_roots_dir}/simple-read-only-with-missing-hierarchy
-hierarchy=/opt
-
-prep_root "${fake_root}" "${hierarchy}"
-rmdir "${fake_root}/${hierarchy}"
-gen_os_release "${fake_root}"
-gen_test_ext_image "${fake_root}" "${hierarchy}"
-
-# run systemd-sysext
-SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" merge
-
-touch "${fake_root}${hierarchy}/should-still-fail-on-read-only-fs" && die "${fake_root}${hierarchy} is not read-only"
-check_usual_suspects_after_merge "${fake_root}" "${hierarchy}" e
-
-SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" unmerge
-
-check_usual_suspects_after_unmerge "${fake_root}" "${hierarchy}"
-
-
-#
-# no extension data in /var/lib/extensions.mutable/…, an empty hierarchy,
-# mutability disabled by default
-#
-# read-only merged
-#
-
-
-fake_root=${fake_roots_dir}/simple-read-only-with-empty-hierarchy
-hierarchy=/opt
-
-prep_root "${fake_root}" "${hierarchy}"
-gen_os_release "${fake_root}"
-gen_test_ext_image "${fake_root}" "${hierarchy}"
-
-make_ro "${fake_root}" "${hierarchy}"
-
-touch "${fake_root}${hierarchy}/should-fail-on-read-only-fs" && die "${fake_root}${hierarchy} is not read-only"
-
-# run systemd-sysext
-SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" merge
-
-touch "${fake_root}${hierarchy}/should-still-fail-on-read-only-fs" && die "${fake_root}${hierarchy} is not read-only"
-check_usual_suspects_after_merge "${fake_root}" "${hierarchy}" e
-
-SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" unmerge
-
-check_usual_suspects_after_unmerge "${fake_root}" "${hierarchy}"
-
-
-#
-# extension data in /var/lib/extensions.mutable/…, read-only hierarchy, mutability disabled-by-default
-#
-# read-only merged
-#
-
-
-fake_root=${fake_roots_dir}/simple-mutable-with-read-only-hierarchy-disabled
-hierarchy=/usr
-
-prep_root "${fake_root}" "${hierarchy}"
-gen_os_release "${fake_root}"
-gen_test_ext_image "${fake_root}" "${hierarchy}"
-
-ext_data_path=$(hierarchy_ext_mut_path "${fake_root}" "${hierarchy}")
-prep_ext_mut "${ext_data_path}"
-
-prep_ro_hierarchy "${fake_root}" "${hierarchy}"
-
-touch "${fake_root}${hierarchy}/should-fail-on-read-only-fs" && die "${fake_root}${hierarchy} is not read-only"
-
-# run systemd-sysext
-SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" merge
-
-touch "${fake_root}${hierarchy}/should-be-read-only" && die "${fake_root}${hierarchy} is not read-only"
-check_usual_suspects_after_merge "${fake_root}" "${hierarchy}" e h
-
-SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" unmerge
-
-check_usual_suspects_after_unmerge "${fake_root}" "${hierarchy}" h
-
-
-#
-# extension data in /var/lib/extensions.mutable/…, read-only hierarchy, auto-mutability
-#
-# mutable merged
-#
-
-
-fake_root=${fake_roots_dir}/simple-mutable-with-read-only-hierarchy
-hierarchy=/usr
-
-prep_root "${fake_root}" "${hierarchy}"
-gen_os_release "${fake_root}"
-gen_test_ext_image "${fake_root}" "${hierarchy}"
-
-ext_data_path=$(hierarchy_ext_mut_path "${fake_root}" "${hierarchy}")
-prep_ext_mut "${ext_data_path}"
-
-prep_ro_hierarchy "${fake_root}" "${hierarchy}"
-
-touch "${fake_root}${hierarchy}/should-fail-on-read-only-fs" && die "${fake_root}${hierarchy} is not read-only"
-
-# run systemd-sysext
-SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" --mutable=auto merge
-
-touch "${fake_root}${hierarchy}/now-is-mutable" || die "${fake_root}${hierarchy} is not mutable"
-check_usual_suspects_after_merge "${fake_root}" "${hierarchy}" e h u
-test -f "${ext_data_path}/now-is-mutable" || die "now-is-mutable is not stored in expected location"
-
-SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" unmerge
-
-check_usual_suspects_after_unmerge "${fake_root}" "${hierarchy}" h
-test -f "${ext_data_path}/now-is-mutable" || die "now-is-mutable disappeared from writable storage after unmerge"
-test ! -f "${fake_root}${hierarchy}/now-is-mutable" || die "now-is-mutable did not disappear from hierarchy after unmerge"
-
-
-#
-# extension data in /var/lib/extensions.mutable/…, missing hierarchy,
-# auto-mutability
-#
-# mutable merged
-#
-
-
-fake_root=${fake_roots_dir}/simple-mutable-with-missing-hierarchy
-hierarchy=/opt
-
-prep_root "${fake_root}" "${hierarchy}"
-rmdir "${fake_root}/${hierarchy}"
-gen_os_release "${fake_root}"
-gen_test_ext_image "${fake_root}" "${hierarchy}"
-
-ext_data_path=$(hierarchy_ext_mut_path "${fake_root}" "${hierarchy}")
-prep_ext_mut "${ext_data_path}"
-
-# run systemd-sysext
-SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" --mutable=auto merge
-
-touch "${fake_root}${hierarchy}/now-is-mutable" || die "${fake_root}${hierarchy} is not mutable"
-check_usual_suspects_after_merge "${fake_root}" "${hierarchy}" e u
-test -f "${ext_data_path}/now-is-mutable" || die "now-is-mutable is not stored in expected location"
-
-SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" unmerge
-
-check_usual_suspects_after_unmerge "${fake_root}" "${hierarchy}"
-test -f "${ext_data_path}/now-is-mutable" || die "now-is-mutable disappeared from writable storage after unmerge"
-test ! -f "${fake_root}${hierarchy}/now-is-mutable" || die "now-is-mutable did not disappear from hierarchy after unmerge"
-
-
-#
-# extension data in /var/lib/extensions.mutable/…, empty hierarchy, auto-mutability
-#
-# mutable merged
-#
-
-
-fake_root=${fake_roots_dir}/simple-mutable-with-empty-hierarchy
-hierarchy=/opt
-
-prep_root "${fake_root}" "${hierarchy}"
-gen_os_release "${fake_root}"
-gen_test_ext_image "${fake_root}" "${hierarchy}"
-
-ext_data_path=$(hierarchy_ext_mut_path "${fake_root}" "${hierarchy}")
-prep_ext_mut "${ext_data_path}"
-
-make_ro "${fake_root}" "${hierarchy}"
-
-touch "${fake_root}${hierarchy}/should-fail-on-read-only-fs" && die "${fake_root}${hierarchy} is not read-only"
-
-# run systemd-sysext
-SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" --mutable=auto merge
-
-touch "${fake_root}${hierarchy}/now-is-mutable" || die "${fake_root}${hierarchy} is not mutable"
-check_usual_suspects_after_merge "${fake_root}" "${hierarchy}" e u
-test -f "${ext_data_path}/now-is-mutable" || die "now-is-mutable is not stored in expected location"
-
-SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" unmerge
-
-check_usual_suspects_after_unmerge "${fake_root}" "${hierarchy}"
-test -f "${ext_data_path}/now-is-mutable" || die "now-is-mutable disappeared from writable storage after unmerge"
-test ! -f "${fake_root}${hierarchy}/now-is-mutable" || die "now-is-mutable did not disappear from hierarchy after unmerge"
-
-
-#
-# /var/lib/extensions.mutable/… is a symlink to /some/other/dir, read-only
-# hierarchy, auto-mutability
-#
-# mutable merged
-#
-
-
-fake_root=${fake_roots_dir}/mutable-symlink-with-read-only-hierarchy
-hierarchy=/usr
-
-prep_root "${fake_root}" "${hierarchy}"
-gen_os_release "${fake_root}"
-gen_test_ext_image "${fake_root}" "${hierarchy}"
-
-# generate extension writable data
-ext_data_path=$(hierarchy_ext_mut_path "${fake_root}" "${hierarchy}")
-real_ext_dir="${fake_root}/upperdir"
-prep_ext_mut "${real_ext_dir}"
-ln -sfTr "${real_ext_dir}" "${ext_data_path}"
-
-prep_ro_hierarchy "${fake_root}" "${hierarchy}"
-
-touch "${fake_root}${hierarchy}/should-fail-on-read-only-fs" && die "${fake_root}${hierarchy} is not read-only"
-
-# run systemd-sysext
-SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" --mutable=auto merge
-
-touch "${fake_root}${hierarchy}/now-is-mutable" || die "${fake_root}${hierarchy} is not mutable"
-check_usual_suspects_after_merge "${fake_root}" "${hierarchy}" e h u
-test -f "${ext_data_path}/now-is-mutable" || die "now-is-mutable is not stored in expected location"
-test -f "${real_ext_dir}/now-is-mutable" || die "now-is-mutable is not stored in expected location"
-
-SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" unmerge
-
-check_usual_suspects_after_unmerge "${fake_root}" "${hierarchy}" h
-test -f "${ext_data_path}/now-is-mutable" || die "now-is-mutable disappeared from writable storage after unmerge"
-test -f "${real_ext_dir}/now-is-mutable" || die "now-is-mutable disappeared from writable storage after unmerge"
-test ! -f "${fake_root}${hierarchy}/now-is-mutable" || die "now-is-mutable did not disappear from hierarchy after unmerge"
-
-
-#
-# /var/lib/extensions.mutable/… is a symlink to the hierarchy itself, auto-mutability
-#
-# for this to work, hierarchy must be mutable
-#
-# mutable merged
-#
-
-
-fake_root=${fake_roots_dir}/mutable-self-upper
-hierarchy=/usr
-
-prep_root "${fake_root}" "${hierarchy}"
-gen_os_release "${fake_root}"
-gen_test_ext_image "${fake_root}" "${hierarchy}"
-
-# generate extension writable data
-ext_data_path=$(hierarchy_ext_mut_path "${fake_root}" "${hierarchy}")
-real_ext_dir="${fake_root}${hierarchy}"
-prep_ext_mut "${real_ext_dir}"
-ln -sfTr "${real_ext_dir}" "${ext_data_path}"
-
-# prepare writable hierarchy
-touch "${fake_root}${hierarchy}/preexisting-file-in-hierarchy"
-
-# run systemd-sysext
-SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" --mutable=auto merge
-
-touch "${fake_root}${hierarchy}/now-is-mutable" || die "${fake_root}${hierarchy} is not mutable"
-check_usual_suspects_after_merge "${fake_root}" "${hierarchy}" e h u
-test -f "${ext_data_path}/now-is-mutable" || die "now-is-mutable is not stored in expected location"
-test -f "${real_ext_dir}/now-is-mutable" || die "now-is-mutable is not stored in expected location"
-
-SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" unmerge
-
-check_usual_suspects_after_unmerge "${fake_root}" "${hierarchy}" h u
-test -f "${ext_data_path}/now-is-mutable" || die "now-is-mutable disappeared from writable storage after unmerge"
-test -f "${real_ext_dir}/now-is-mutable" || die "now-is-mutable disappeared from writable storage after unmerge"
-
-
-#
-# /var/lib/extensions.mutable/… is a symlink to the hierarchy itself, which is
-# read-only, auto-mutability
-#
-# expecting a failure here
-#
-
-
-fake_root=${fake_roots_dir}/failure-self-upper-ro
-hierarchy=/usr
-
-prep_root "${fake_root}" "${hierarchy}"
-gen_os_release "${fake_root}"
-gen_test_ext_image "${fake_root}" "${hierarchy}"
-
-# generate extension writable data
-ext_data_path=$(hierarchy_ext_mut_path "${fake_root}" "${hierarchy}")
-real_ext_dir="${fake_root}${hierarchy}"
-prep_ext_mut "${real_ext_dir}"
-ln -sfTr "${real_ext_dir}" "${ext_data_path}"
-
-prep_ro_hierarchy "${fake_root}" "${hierarchy}"
-
-# run systemd-sysext
-SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" --mutable=auto merge && die "expected merge to fail"
-
-
-#
-# /var/lib/extensions.mutable/… is a dangling symlink, auto-mutability
-#
-# read-only merged
-#
-
-
-fake_root=${fake_roots_dir}/read-only-mutable-dangling-symlink
-hierarchy=/usr
-
-prep_root "${fake_root}" "${hierarchy}"
-gen_os_release "${fake_root}"
-gen_test_ext_image "${fake_root}" "${hierarchy}"
-
-ext_data_path=$(hierarchy_ext_mut_path "${fake_root}" "${hierarchy}")
-ln -sfTr "/should/not/exist/" "${ext_data_path}"
-
-prep_ro_hierarchy "${fake_root}" "${hierarchy}"
-
-touch "${fake_root}${hierarchy}/should-fail-on-read-only-fs" && die "${fake_root}${hierarchy} is not read-only"
-
-# run systemd-sysext
-SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" --mutable=auto merge
-
-touch "${fake_root}${hierarchy}/should-still-fail-on-read-only-fs" && die "${fake_root}${hierarchy} is not read-only"
-
-check_usual_suspects_after_merge "${fake_root}" "${hierarchy}" e h
-
-SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" unmerge
-
-check_usual_suspects_after_unmerge "${fake_root}" "${hierarchy}" h
-
-
-#
-# /var/lib/extensions.mutable/… exists, but it's ignored, mutability disabled explicitly
-#
-# read-only merged
-#
-
-
-fake_root=${fake_roots_dir}/disabled
-hierarchy=/usr
-
-prep_root "${fake_root}" "${hierarchy}"
-gen_os_release "${fake_root}"
-gen_test_ext_image "${fake_root}" "${hierarchy}"
-
-ext_data_path=$(hierarchy_ext_mut_path "${fake_root}" "${hierarchy}")
-prep_ext_mut "${ext_data_path}"
-
-prep_ro_hierarchy "${fake_root}" "${hierarchy}"
-
-touch "${fake_root}${hierarchy}/should-fail-on-read-only-fs" && die "${fake_root}${hierarchy} is not read-only"
-
-# run systemd-sysext
-SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" --mutable=no merge
-
-touch "${fake_root}${hierarchy}/should-still-fail-on-read-only-fs" && die "${fake_root}${hierarchy} is not read-only"
-
-check_usual_suspects_after_merge "${fake_root}" "${hierarchy}" e h
-
-SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" unmerge
-
-check_usual_suspects_after_unmerge "${fake_root}" "${hierarchy}" h
-
-
-#
-# /var/lib/extensions.mutable/… exists, but it's imported instead
-#
-# read-only merged
-#
-
-
-fake_root=${fake_roots_dir}/imported
-hierarchy=/usr
-
-prep_root "${fake_root}" "${hierarchy}"
-gen_os_release "${fake_root}"
-gen_test_ext_image "${fake_root}" "${hierarchy}"
-
-ext_data_path=$(hierarchy_ext_mut_path "${fake_root}" "${hierarchy}")
-prep_ext_mut "${ext_data_path}"
-
-prep_ro_hierarchy "${fake_root}" "${hierarchy}"
-
-touch "${fake_root}${hierarchy}/should-fail-on-read-only-fs" && die "${fake_root}${hierarchy} is not read-only"
-
-# run systemd-sysext
-SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" --mutable=import merge
-
-touch "${fake_root}${hierarchy}/should-still-fail-on-read-only-fs" && die "${fake_root}${hierarchy} is not read-only"
-
-check_usual_suspects_after_merge "${fake_root}" "${hierarchy}" e h u
-
-SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" unmerge
-
-check_usual_suspects_after_unmerge "${fake_root}" "${hierarchy}" h
-
-
-#
-# /var/lib/extensions.mutable/… does not exist, but mutability is enabled
-# explicitly
-#
-# mutable merged
-#
-
-
-fake_root=${fake_roots_dir}/enabled
-hierarchy=/usr
-
-prep_root "${fake_root}" "${hierarchy}"
-gen_os_release "${fake_root}"
-gen_test_ext_image "${fake_root}" "${hierarchy}"
-
-ext_data_path=$(hierarchy_ext_mut_path "${fake_root}" "${hierarchy}")
-
-prep_ro_hierarchy "${fake_root}" "${hierarchy}"
-
-touch "${fake_root}${hierarchy}/should-fail-on-read-only-fs" && die "${fake_root}${hierarchy} is not read-only"
-
-test ! -d "${ext_data_path}" || die "extensions.mutable should not exist"
-
-# run systemd-sysext
-SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" --mutable=yes merge
-
-test -d "${ext_data_path}" || die "extensions.mutable should exist now"
-touch "${fake_root}${hierarchy}/now-is-mutable" || die "${fake_root}${hierarchy} is not mutable"
-check_usual_suspects_after_merge "${fake_root}" "${hierarchy}" e h
-test -f "${ext_data_path}/now-is-mutable" || die "now-is-mutable is not stored in expected location"
-
-SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" unmerge
-
-check_usual_suspects_after_unmerge "${fake_root}" "${hierarchy}" h
-test -f "${ext_data_path}/now-is-mutable" || die "now-is-mutable disappeared from writable storage after unmerge"
-test ! -f "${fake_root}${hierarchy}/now-is-mutable" || die "now-is-mutable did not disappear from hierarchy after unmerge"
-
-
-#
-# /var/lib/extensions.mutable/… does not exist, auto-mutability
-#
-# read-only merged
-#
-
-
-fake_root=${fake_roots_dir}/simple-read-only-explicit
-hierarchy=/usr
-
-prep_root "${fake_root}" "${hierarchy}"
-gen_os_release "${fake_root}"
-gen_test_ext_image "${fake_root}" "${hierarchy}"
-
-prep_ro_hierarchy "${fake_root}" "${hierarchy}"
-
-touch "${fake_root}${hierarchy}/should-fail-on-read-only-fs" && die "${fake_root}${hierarchy} is not read-only"
-
-# run systemd-sysext
-SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" --mutable=auto merge
-
-touch "${fake_root}${hierarchy}/should-still-fail-on-read-only-fs" && die "${fake_root}${hierarchy} is not read-only"
-check_usual_suspects_after_merge "${fake_root}" "${hierarchy}" e h
-
-SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" unmerge
-
-check_usual_suspects_after_unmerge "${fake_root}" "${hierarchy}" h
-
-
-#
-# /var/lib/extensions.mutable/… does not exist, but mutability is enabled
-# through an env var
-#
-# mutable merged
-#
-
-
-fake_root=${fake_roots_dir}/enabled-env-var
-hierarchy=/usr
-
-prep_env "yes"
-prep_root "${fake_root}" "${hierarchy}"
-gen_os_release "${fake_root}"
-gen_test_ext_image "${fake_root}" "${hierarchy}"
-
-ext_data_path=$(hierarchy_ext_mut_path "${fake_root}" "${hierarchy}")
-
-prep_ro_hierarchy "${fake_root}" "${hierarchy}"
-
-touch "${fake_root}${hierarchy}/should-fail-on-read-only-fs" && die "${fake_root}${hierarchy} is not read-only"
-
-test ! -d "${ext_data_path}" || die "extensions.mutable should not exist"
-
-# run systemd-sysext
-SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" merge
-
-test -d "${ext_data_path}" || die "extensions.mutable should exist now"
-touch "${fake_root}${hierarchy}/now-is-mutable" || die "${fake_root}${hierarchy} is not mutable"
-check_usual_suspects_after_merge "${fake_root}" "${hierarchy}" e h
-test -f "${ext_data_path}/now-is-mutable" || die "now-is-mutable is not stored in expected location"
-
-SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" unmerge
-
-check_usual_suspects_after_unmerge "${fake_root}" "${hierarchy}" h
-test -f "${ext_data_path}/now-is-mutable" || die "now-is-mutable disappeared from writable storage after unmerge"
-test ! -f "${fake_root}${hierarchy}/now-is-mutable" || die "now-is-mutable did not disappear from hierarchy after unmerge"
-drop_env
-
-
-#
-# /var/lib/extensions.mutable/… does not exist, auto-mutability through an env
-# var
-#
-# read-only merged
-#
-
-
-fake_root=${fake_roots_dir}/read-only-auto-env-var
-hierarchy=/usr
-
-prep_env "auto"
-prep_root "${fake_root}" "${hierarchy}"
-gen_os_release "${fake_root}"
-gen_test_ext_image "${fake_root}" "${hierarchy}"
-
-prep_ro_hierarchy "${fake_root}" "${hierarchy}"
-
-touch "${fake_root}${hierarchy}/should-fail-on-read-only-fs" && die "${fake_root}${hierarchy} is not read-only"
-
-# run systemd-sysext
-SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" --mutable=auto merge
-
-touch "${fake_root}${hierarchy}/should-still-fail-on-read-only-fs" && die "${fake_root}${hierarchy} is not read-only"
-check_usual_suspects_after_merge "${fake_root}" "${hierarchy}" e h
-
-SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" unmerge
-
-check_usual_suspects_after_unmerge "${fake_root}" "${hierarchy}" h
-drop_env
-
-
-#
-# extension data in /var/lib/extensions.mutable/…, read-only hierarchy,
-# auto-mutability through an env var
-#
-# mutable merged
-#
-
-
-fake_root=${fake_roots_dir}/auto-mutable-env-var
-hierarchy=/usr
-
-prep_env "auto"
-prep_root "${fake_root}" "${hierarchy}"
-gen_os_release "${fake_root}"
-gen_test_ext_image "${fake_root}" "${hierarchy}"
-
-ext_data_path=$(hierarchy_ext_mut_path "${fake_root}" "${hierarchy}")
-prep_ext_mut "${ext_data_path}"
-
-prep_ro_hierarchy "${fake_root}" "${hierarchy}"
-
-touch "${fake_root}${hierarchy}/should-fail-on-read-only-fs" && die "${fake_root}${hierarchy} is not read-only"
-
-# run systemd-sysext
-SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" merge
-
-touch "${fake_root}${hierarchy}/now-is-mutable" || die "${fake_root}${hierarchy} is not mutable"
-check_usual_suspects_after_merge "${fake_root}" "${hierarchy}" e h u
-test -f "${ext_data_path}/now-is-mutable" || die "now-is-mutable is not stored in expected location"
-
-SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" unmerge
-
-check_usual_suspects_after_unmerge "${fake_root}" "${hierarchy}" h
-test -f "${ext_data_path}/now-is-mutable" || die "now-is-mutable disappeared from writable storage after unmerge"
-test ! -f "${fake_root}${hierarchy}/now-is-mutable" || die "now-is-mutable did not disappear from hierarchy after unmerge"
-drop_env
-
-
-#
-# extension data in /var/lib/extensions.mutable/…, read-only hierarchy,
-# mutability disabled through an env var
-#
-# read-only merged
-#
-
-
-fake_root=${fake_roots_dir}/env-var-disabled
-hierarchy=/usr
-
-prep_env "no"
-prep_root "${fake_root}" "${hierarchy}"
-gen_os_release "${fake_root}"
-gen_test_ext_image "${fake_root}" "${hierarchy}"
-
-ext_data_path=$(hierarchy_ext_mut_path "${fake_root}" "${hierarchy}")
-prep_ext_mut "${ext_data_path}"
-
-prep_ro_hierarchy "${fake_root}" "${hierarchy}"
-
-touch "${fake_root}${hierarchy}/should-fail-on-read-only-fs" && die "${fake_root}${hierarchy} is not read-only"
-
-# run systemd-sysext
-SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" merge
-
-touch "${fake_root}${hierarchy}/should-be-read-only" && die "${fake_root}${hierarchy} is not read-only"
-check_usual_suspects_after_merge "${fake_root}" "${hierarchy}" e h
-
-SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" unmerge
-
-check_usual_suspects_after_unmerge "${fake_root}" "${hierarchy}" h
-drop_env
-
-
-#
-# /var/lib/extensions.mutable/… exists, but it's imported instead through an
-# env var
-#
-# read-only merged
-#
-
-
-fake_root=${fake_roots_dir}/imported-env-var
-hierarchy=/usr
-
-prep_env "import"
-prep_root "${fake_root}" "${hierarchy}"
-gen_os_release "${fake_root}"
-gen_test_ext_image "${fake_root}" "${hierarchy}"
-
-ext_data_path=$(hierarchy_ext_mut_path "${fake_root}" "${hierarchy}")
-prep_ext_mut "${ext_data_path}"
-
-prep_ro_hierarchy "${fake_root}" "${hierarchy}"
-
-touch "${fake_root}${hierarchy}/should-fail-on-read-only-fs" && die "${fake_root}${hierarchy} is not read-only"
-
-# run systemd-sysext
-SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" merge
-
-touch "${fake_root}${hierarchy}/should-still-fail-on-read-only-fs" && die "${fake_root}${hierarchy} is not read-only"
-
-check_usual_suspects_after_merge "${fake_root}" "${hierarchy}" e h u
-
-SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" unmerge
-
-check_usual_suspects_after_unmerge "${fake_root}" "${hierarchy}" h
-drop_env
-
-
-#
-# extension data in /var/lib/extensions.mutable/…, read-only hierarchy,
-# mutability enabled through an env var, but overridden with a command-line
-# option
-#
-# read-only merged
-#
-
-
-fake_root=${fake_roots_dir}/env-var-overridden
-hierarchy=/usr
-
-prep_env "yes"
-prep_root "${fake_root}" "${hierarchy}"
-gen_os_release "${fake_root}"
-gen_test_ext_image "${fake_root}" "${hierarchy}"
-
-ext_data_path=$(hierarchy_ext_mut_path "${fake_root}" "${hierarchy}")
-prep_ext_mut "${ext_data_path}"
-
-prep_ro_hierarchy "${fake_root}" "${hierarchy}"
-
-touch "${fake_root}${hierarchy}/should-fail-on-read-only-fs" && die "${fake_root}${hierarchy} is not read-only"
-
-# run systemd-sysext
-SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" --mutable=no merge
-
-touch "${fake_root}${hierarchy}/should-be-read-only" && die "${fake_root}${hierarchy} is not read-only"
-check_usual_suspects_after_merge "${fake_root}" "${hierarchy}" e h
-
-SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" unmerge
-
-check_usual_suspects_after_unmerge "${fake_root}" "${hierarchy}" h
-drop_env
-
-
-#
-# extension data in /var/lib/extensions.mutable/…, read-only hierarchy,
-# ephemeral mutability, so extension data contents are ignored
-#
-# mutable merged
-#
-
-
-fake_root=${fake_roots_dir}/ephemeral
-hierarchy=/usr
-
-prep_root "${fake_root}" "${hierarchy}"
-gen_os_release "${fake_root}"
-gen_test_ext_image "${fake_root}" "${hierarchy}"
-
-ext_data_path=$(hierarchy_ext_mut_path "${fake_root}" "${hierarchy}")
-prep_ext_mut "${ext_data_path}"
-
-prep_ro_hierarchy "${fake_root}" "${hierarchy}"
-
-touch "${fake_root}${hierarchy}/should-fail-on-read-only-fs" && die "${fake_root}${hierarchy} is not read-only"
-
-# run systemd-sysext
-SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" --mutable=ephemeral merge
-
-touch "${fake_root}${hierarchy}/now-is-mutable" || die "${fake_root}${hierarchy} is not mutable"
-check_usual_suspects_after_merge "${fake_root}" "${hierarchy}" e h
-test ! -f "${ext_data_path}/now-is-mutable" || die "now-is-mutable should not be stored in extension data"
-
-SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" unmerge
-
-check_usual_suspects_after_unmerge "${fake_root}" "${hierarchy}" h
-test ! -f "${ext_data_path}/now-is-mutable" || die "now-is-mutable should not appear in writable storage after unmerge"
-test ! -f "${fake_root}${hierarchy}/now-is-mutable" || die "now-is-mutable did not disappear from hierarchy after unmerge"
-
-
-#
-# extension data in /var/lib/extensions.mutable/…, read-only hierarchy,
-# ephemeral mutability through an env var, so extension data contents are
-# ignored
-#
-# mutable merged
-#
-
-
-fake_root=${fake_roots_dir}/ephemeral-env-var
-hierarchy=/usr
-
-prep_env "ephemeral"
-prep_root "${fake_root}" "${hierarchy}"
-gen_os_release "${fake_root}"
-gen_test_ext_image "${fake_root}" "${hierarchy}"
-
-ext_data_path=$(hierarchy_ext_mut_path "${fake_root}" "${hierarchy}")
-prep_ext_mut "${ext_data_path}"
-
-prep_ro_hierarchy "${fake_root}" "${hierarchy}"
-
-touch "${fake_root}${hierarchy}/should-fail-on-read-only-fs" && die "${fake_root}${hierarchy} is not read-only"
-
-# run systemd-sysext
-SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" merge
-
-touch "${fake_root}${hierarchy}/now-is-mutable" || die "${fake_root}${hierarchy} is not mutable"
-check_usual_suspects_after_merge "${fake_root}" "${hierarchy}" e h
-test ! -f "${ext_data_path}/now-is-mutable" || die "now-is-mutable should not be stored in extension data"
-
-SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" unmerge
-
-check_usual_suspects_after_unmerge "${fake_root}" "${hierarchy}" h
-test ! -f "${ext_data_path}/now-is-mutable" || die "now-is-mutable should not appear in writable storage after unmerge"
-test ! -f "${fake_root}${hierarchy}/now-is-mutable" || die "now-is-mutable did not disappear from hierarchy after unmerge"
-drop_env
-
-
-#
-# extension data in /var/lib/extensions.mutable/…, read-only hierarchy,
-# ephemeral import mutability, so extension data contents are imported too
-#
-# mutable merged
-#
-
-
-fake_root=${fake_roots_dir}/ephemeral-import
-hierarchy=/usr
-
-prep_root "${fake_root}" "${hierarchy}"
-gen_os_release "${fake_root}"
-gen_test_ext_image "${fake_root}" "${hierarchy}"
-
-ext_data_path=$(hierarchy_ext_mut_path "${fake_root}" "${hierarchy}")
-prep_ext_mut "${ext_data_path}"
-
-prep_ro_hierarchy "${fake_root}" "${hierarchy}"
-
-touch "${fake_root}${hierarchy}/should-fail-on-read-only-fs" && die "${fake_root}${hierarchy} is not read-only"
-
-# run systemd-sysext
-SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" --mutable=ephemeral-import merge
-
-touch "${fake_root}${hierarchy}/now-is-mutable" || die "${fake_root}${hierarchy} is not mutable"
-check_usual_suspects_after_merge "${fake_root}" "${hierarchy}" e h u
-test ! -f "${ext_data_path}/now-is-mutable" || die "now-is-mutable should not be stored in extension data"
-
-SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" unmerge
-
-check_usual_suspects_after_unmerge "${fake_root}" "${hierarchy}" h
-test ! -f "${ext_data_path}/now-is-mutable" || die "now-is-mutable should not appear in writable storage after unmerge"
-test ! -f "${fake_root}${hierarchy}/now-is-mutable" || die "now-is-mutable did not disappear from hierarchy after unmerge"
-
-
-#
-# extension data in /var/lib/extensions.mutable/…, read-only hierarchy,
-# ephemeral mutability through an env var, so extension data contents are
-# imported too
-#
-# mutable merged
-#
-
-
-fake_root=${fake_roots_dir}/ephemeral-import-env-var
-hierarchy=/usr
-
-prep_env "ephemeral-import"
-prep_root "${fake_root}" "${hierarchy}"
-gen_os_release "${fake_root}"
-gen_test_ext_image "${fake_root}" "${hierarchy}"
-
-ext_data_path=$(hierarchy_ext_mut_path "${fake_root}" "${hierarchy}")
-prep_ext_mut "${ext_data_path}"
-
-prep_ro_hierarchy "${fake_root}" "${hierarchy}"
-
-touch "${fake_root}${hierarchy}/should-fail-on-read-only-fs" && die "${fake_root}${hierarchy} is not read-only"
-
-# run systemd-sysext
-SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" merge
-
-touch "${fake_root}${hierarchy}/now-is-mutable" || die "${fake_root}${hierarchy} is not mutable"
-check_usual_suspects_after_merge "${fake_root}" "${hierarchy}" e h u
-test ! -f "${ext_data_path}/now-is-mutable" || die "now-is-mutable should not be stored in extension data"
-
-SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" unmerge
-
-check_usual_suspects_after_unmerge "${fake_root}" "${hierarchy}" h
-test ! -f "${ext_data_path}/now-is-mutable" || die "now-is-mutable should not appear in writable storage after unmerge"
-test ! -f "${fake_root}${hierarchy}/now-is-mutable" || die "now-is-mutable did not disappear from hierarchy after unmerge"
-drop_env
-
-
-#
-# extension data pointing to mutable hierarchy, ephemeral import mutability
-#
-# expecting a failure here
-#
-
-
-fake_root=${fake_roots_dir}/ephemeral-import-self
-hierarchy=/usr
-
-prep_root "${fake_root}" "${hierarchy}"
-gen_os_release "${fake_root}"
-gen_test_ext_image "${fake_root}" "${hierarchy}"
-
-ext_data_path=$(hierarchy_ext_mut_path "${fake_root}" "${hierarchy}")
-real_ext_dir="${fake_root}${hierarchy}"
-prep_ext_mut "${real_ext_dir}"
-ln -sfTr "${real_ext_dir}" "${ext_data_path}"
-
-prep_hierarchy "${fake_root}" "${hierarchy}"
-
-touch "${fake_root}${hierarchy}/should-succeed-on-read-only-fs" || die "${fake_root}${hierarchy} is not mutable"
-
-# run systemd-sysext
-SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" --mutable=ephemeral-import merge && die 'expected merge to fail'
-
-
-#
-# extension data pointing to mutable hierarchy, import mutability
-#
-# expecting a failure here
-#
-
-
-fake_root=${fake_roots_dir}/import-self
-hierarchy=/usr
-
-prep_root "${fake_root}" "${hierarchy}"
-gen_os_release "${fake_root}"
-gen_test_ext_image "${fake_root}" "${hierarchy}"
-
-ext_data_path=$(hierarchy_ext_mut_path "${fake_root}" "${hierarchy}")
-real_ext_dir="${fake_root}${hierarchy}"
-prep_ext_mut "${real_ext_dir}"
-ln -sfTr "${real_ext_dir}" "${ext_data_path}"
-
-prep_hierarchy "${fake_root}" "${hierarchy}"
-
-touch "${fake_root}${hierarchy}/should-succeed-on-read-only-fs" || die "${fake_root}${hierarchy} is not mutable"
-
-# run systemd-sysext
-SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" --mutable=import merge && die 'expected merge to fail'
-
-
-#
-# done
-#
+: "Run subtests"
 
+run_subtests
 
 touch /testok
diff --git a/test/units/testsuite-50.sysext.sh b/test/units/testsuite-50.sysext.sh
new file mode 100755 (executable)
index 0000000..f3077f5
--- /dev/null
@@ -0,0 +1,724 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+FAKE_ROOTS_DIR="$(mktemp -d --tmpdir="" fake-roots-XXX)"
+
+# shellcheck disable=SC2317
+at_exit() {
+    set +ex
+
+    local target
+
+    # Note: `cat` here is used intentionally, so we iterate over our own copy of /proc/mounts. Otherwise
+    #       things get very confusing once we start unmounting things, due to changing file offsets.
+    # shellcheck disable=SC2002
+    cat /proc/mounts | while read -r _ target _ _ _ _; do
+        if [[ "$target" =~ ^"$FAKE_ROOTS_DIR" ]]; then
+            umount -Rv "$target"
+        fi
+    done
+
+    rm -rf "${FAKE_ROOTS_DIR}"
+}
+
+trap at_exit EXIT
+
+prepare_root() {
+    local root=${1:?}
+    local hierarchy=${2:?}
+    local dir
+
+    if [[ -d $root ]]; then
+        echo >&2 "Directory $root already exists, possible copy-paste error?"
+        exit 1
+    fi
+
+    for dir in "$hierarchy" "/usr/lib" "/var/lib/extensions/" "/var/lib/extensions.mutable"; do
+        mkdir -p "$root$dir"
+    done
+
+    {
+        echo "ID=testtest"
+        echo "VERSION=1.2.3"
+    } >"$root/usr/lib/os-release"
+}
+
+prepare_extension_image() {
+    local root="${1:?}"
+    local hierarchy="${2:?}"
+    local ext_dir ext_release name
+
+    name="test-extension"
+    ext_dir="$root/var/lib/extensions/$name"
+    ext_release="$ext_dir/usr/lib/extension-release.d/extension-release.$name"
+    mkdir -p "${ext_release%/*}"
+    echo "ID=_any" >"$ext_release"
+    mkdir -p "$ext_dir/$hierarchy"
+    touch "$ext_dir$hierarchy/preexisting-file-in-extension-image"
+}
+
+prepare_extension_mutable_dir() {
+    local dir=${1:?}
+
+    mkdir -p "$dir"
+    touch "$dir/preexisting-file-in-extensions-mutable"
+}
+
+make_read_only() {
+    local root="${1:?}"
+    local hierarchy="${2:?}"
+
+    mount -o bind,ro "$root$hierarchy" "$root$hierarchy"
+}
+
+prepare_hierarchy() {
+    local root="${1:?}"
+    local hierarchy="${2:?}"
+
+    touch "$root$hierarchy/preexisting-file-in-hierarchy"
+}
+
+prepare_read_only_hierarchy() {
+    local root="${1:?}"
+    local hierarchy="${2:?}"
+
+    prepare_hierarchy "$root" "$hierarchy"
+    make_read_only "$root" "$hierarchy"
+}
+
+# Extra arguments:
+#   -e: check for a preexisting file in extension
+#   -h: check for a preexisting file in hierarchy
+#   -u: check for a preexisting file in upperdir
+extension_verify() {
+    local root="${1:?}"
+    local hierarchy="${2:?}"
+    local message="${3:?}"
+    shift 3
+    # Map each option to a pre-defined file name
+    local -A option_files_map=(
+        [e]="preexisting-file-in-extension-image"
+        [h]="preexisting-file-in-hierarchy"
+        [u]="preexisting-file-in-extensions-mutable"
+    )
+    local -A args=(
+        [e]=0
+        [h]=0
+        [u]=0
+    )
+    local file full_path option
+
+    while getopts "ehu" opt; do
+        case "$opt" in
+            e|h|u)
+                args[$opt]=1
+                ;;
+            *)
+                echo >&2 "Unxexpected option: $opt"
+                exit 1
+        esac
+    done
+
+    echo "${args[@]}"
+
+    for option in "${!option_files_map[@]}"; do
+        file="${option_files_map[$option]}"
+        full_path="$root$hierarchy/$file"
+
+        if [[ ${args[$option]} -ne 0 ]]; then
+            if [[ ! -f "$full_path" ]]; then
+                ls -la "$root$hierarchy"
+                echo >&2 "Expected file '$file' to exist under $root$hierarchy $message"
+                exit 1
+            fi
+        else
+            if [[ -f "$full_path" ]]; then
+                ls -la "$root$hierarchy"
+                echo >&2 "Expected file '$file' to not exist under $root$hierarchy $message"
+                exit 1
+            fi
+        fi
+    done
+}
+
+extension_verify_after_merge() (
+    set +x
+
+    local root="${1:?}"
+    local hierarchy="${2:?}"
+    shift 2
+
+    extension_verify "$root" "$hierarchy" "after merge" "$@"
+)
+
+extension_verify_after_unmerge() (
+    set +x
+
+    local root="${1:?}"
+    local hierarchy="${2:?}"
+    shift 2
+
+    extension_verify "$root" "$hierarchy" "after unmerge" "$@"
+)
+
+# General systemd-sysext tests
+
+: "No extension data in /var/lib/extensions.mutable/…, R/O hierarchy, mutability disabled by default, read-only merged"
+fake_root="$FAKE_ROOTS_DIR/simple-read-only-with-read-only-hierarchy"
+hierarchy=/usr
+
+prepare_root "$fake_root" "$hierarchy"
+prepare_extension_image "$fake_root" "$hierarchy"
+prepare_read_only_hierarchy "$fake_root" "$hierarchy"
+(! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
+
+SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" merge
+(! touch "$fake_root$hierarchy/should-still-fail-on-read-only-fs")
+extension_verify_after_merge "$fake_root" "$hierarchy" -e -h
+
+SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" unmerge
+extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
+(! touch "$fake_root$hierarchy/should-still-fail-on-read-only-fs")
+
+
+: "No extension data in /var/lib/extensions.mutable/…, mutable hierarchy, mutability disabled by default, read-only merged"
+fake_root="$FAKE_ROOTS_DIR/simple-read-only-with-mutable-hierarchy"
+hierarchy=/usr
+
+prepare_root "$fake_root" "$hierarchy"
+prepare_extension_image "$fake_root" "$hierarchy"
+prepare_hierarchy "$fake_root" "$hierarchy"
+touch "$fake_root$hierarchy/should-succeed-on-mutable-fs"
+
+SYSTEMD_SYSEXT_HIERARCHIEe="$hierarchy" systemd-sysext --root="$fake_root" merge
+(! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
+extension_verify_after_merge "$fake_root" "$hierarchy" -e -h
+
+SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" unmerge
+extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
+touch "$fake_root$hierarchy/should-succeed-on-mutable-fs-again"
+
+
+: "No extension data in /var/lib/extensions.mutable/…, no hierarchy either, mutability disabled by default, read-only merged"
+fake_root="$FAKE_ROOTS_DIR/simple-read-only-with-missing-hierarchy"
+hierarchy=/opt
+
+prepare_root "$fake_root" "$hierarchy"
+rmdir "$fake_root/$hierarchy"
+prepare_extension_image "$fake_root" "$hierarchy"
+
+SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" merge
+(! touch "$fake_root$hierarchy/should-still-fail-on-read-only-fs")
+extension_verify_after_merge "$fake_root" "$hierarchy" -e
+
+SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" unmerge
+extension_verify_after_unmerge "$fake_root" "$hierarchy"
+
+
+: "No extension data in /var/lib/extensions.mutable/…, empty hierarchy, mutability disabled by default, read-only merged"
+fake_root="$FAKE_ROOTS_DIR/simple-read-only-with-empty-hierarchy"
+hierarchy=/opt
+
+prepare_root "$fake_root" "$hierarchy"
+prepare_extension_image "$fake_root" "$hierarchy"
+make_read_only "$fake_root" "$hierarchy"
+(! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
+
+SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" merge
+(! touch "$fake_root$hierarchy/should-still-fail-on-read-only-fs")
+extension_verify_after_merge "$fake_root" "$hierarchy" -e
+
+SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" unmerge
+extension_verify_after_unmerge "$fake_root" "$hierarchy"
+
+
+: "Extension data in /var/lib/extensions.mutable/…, R/O hierarchy, mutability disabled by default, read-only merged"
+fake_root="$FAKE_ROOTS_DIR/simple-mutable-with-read-only-hierarchy-disabled"
+hierarchy=/usr
+extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
+
+prepare_root "$fake_root" "$hierarchy"
+prepare_extension_image "$fake_root" "$hierarchy"
+prepare_extension_mutable_dir "$extension_data_dir"
+prepare_read_only_hierarchy "$fake_root" "$hierarchy"
+(! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
+
+SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" merge
+(! touch "$fake_root$hierarchy/should-be-read-only")
+extension_verify_after_merge "$fake_root" "$hierarchy" -e -h
+
+SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" unmerge
+extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
+
+: "Extension data in /var/lib/extensions.mutable/…, R/O hierarchy, auto-mutability, mutable merged"
+fake_root="$FAKE_ROOTS_DIR/simple-mutable-with-read-only-hierarchy"
+hierarchy=/usr
+extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
+
+prepare_root "$fake_root" "$hierarchy"
+prepare_extension_image "$fake_root" "$hierarchy"
+prepare_extension_mutable_dir "$extension_data_dir"
+prepare_read_only_hierarchy "$fake_root" "$hierarchy"
+(! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
+
+SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" --mutable=auto merge
+touch "$fake_root$hierarchy/now-is-mutable"
+extension_verify_after_merge "$fake_root" "$hierarchy" -e -h -u
+test -f "$extension_data_dir/now-is-mutable"
+
+SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" unmerge
+extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
+test -f "$extension_data_dir/now-is-mutable"
+test ! -f "$fake_root$hierarchy/now-is-mutable"
+
+
+: "Extension data in /var/lib/extensions.mutable/…, missing hierarchy, auto-mutability, mutable merged"
+fake_root="$FAKE_ROOTS_DIR/simple-mutable-with-missing-hierarchy"
+hierarchy=/opt
+extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
+
+prepare_root "$fake_root" "$hierarchy"
+rmdir "$fake_root/$hierarchy"
+prepare_extension_image "$fake_root" "$hierarchy"
+prepare_extension_mutable_dir "$extension_data_dir"
+
+SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" --mutable=auto merge
+touch "$fake_root$hierarchy/now-is-mutable"
+extension_verify_after_merge "$fake_root" "$hierarchy" -e -u
+test -f "$extension_data_dir/now-is-mutable"
+
+SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" unmerge
+extension_verify_after_unmerge "$fake_root" "$hierarchy"
+test -f "$extension_data_dir/now-is-mutable"
+test ! -f "$fake_root$hierarchy/now-is-mutable"
+
+
+: "Extension data in /var/lib/extensions.mutable/…, empty hierarchy, auto-mutability, mutable merged"
+fake_root="$FAKE_ROOTS_DIR/simple-mutable-with-empty-hierarchy"
+hierarchy=/opt
+extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
+
+prepare_root "$fake_root" "$hierarchy"
+prepare_extension_image "$fake_root" "$hierarchy"
+prepare_extension_mutable_dir "$extension_data_dir"
+make_read_only "$fake_root" "$hierarchy"
+(! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
+
+SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" --mutable=auto merge
+touch "$fake_root$hierarchy/now-is-mutable"
+extension_verify_after_merge "$fake_root" "$hierarchy" -e -u
+test -f "$extension_data_dir/now-is-mutable"
+
+SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" unmerge
+extension_verify_after_unmerge "$fake_root" "$hierarchy"
+test -f "$extension_data_dir/now-is-mutable"
+test ! -f "$fake_root$hierarchy/now-is-mutable"
+
+
+: "/var/lib/extensions.mutable/… is a symlink to other dir, R/O hierarchy, auto-mutability, mutable merged"
+fake_root="$FAKE_ROOTS_DIR/mutable-symlink-with-read-only-hierarchy"
+hierarchy=/usr
+extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
+extension_real_dir="$fake_root/upperdir"
+
+prepare_root "$fake_root" "$hierarchy"
+prepare_extension_image "$fake_root" "$hierarchy"
+prepare_extension_mutable_dir "$extension_real_dir"
+ln -sfTr "$extension_real_dir" "$extension_data_dir"
+prepare_read_only_hierarchy "$fake_root" "$hierarchy"
+(! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
+
+SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" --mutable=auto merge
+touch "$fake_root$hierarchy/now-is-mutable"
+extension_verify_after_merge "$fake_root" "$hierarchy" -e -h -u
+test -f "$extension_data_dir/now-is-mutable"
+test -f "$extension_real_dir/now-is-mutable"
+
+SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" unmerge
+extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
+test -f "$extension_data_dir/now-is-mutable"
+test -f "$extension_real_dir/now-is-mutable"
+test ! -f "$fake_root$hierarchy/now-is-mutable"
+
+
+: "/var/lib/extensions.mutable/… is a symlink to the hierarchy itself, mutable hierarchy, auto-mutability, mutable merged"
+fake_root="$FAKE_ROOTS_DIR/mutable-self-upper"
+hierarchy=/usr
+extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
+extension_real_dir="$fake_root$hierarchy"
+
+prepare_root "$fake_root" "$hierarchy"
+prepare_extension_image "$fake_root" "$hierarchy"
+prepare_extension_mutable_dir "$extension_real_dir"
+ln -sfTr "$extension_real_dir" "$extension_data_dir"
+touch "$fake_root$hierarchy/preexisting-file-in-hierarchy"
+
+SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" --mutable=auto merge
+touch "$fake_root$hierarchy/now-is-mutable"
+extension_verify_after_merge "$fake_root" "$hierarchy" -e -h -u
+test -f "$extension_data_dir/now-is-mutable"
+test -f "$extension_real_dir/now-is-mutable"
+
+SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" unmerge
+extension_verify_after_unmerge "$fake_root" "$hierarchy" -h -u
+test -f "$extension_data_dir/now-is-mutable"
+test -f "$extension_real_dir/now-is-mutable"
+
+
+: "/var/lib/extensions.mutable/… is a symlink to the hierarchy itself, R/O hierarchy, auto-mutability, expected fail"
+fake_root="$FAKE_ROOTS_DIR/failure-self-upper-ro"
+hierarchy=/usr
+extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
+extension_real_dir="$fake_root$hierarchy"
+
+prepare_root "$fake_root" "$hierarchy"
+prepare_extension_image "$fake_root" "$hierarchy"
+prepare_extension_mutable_dir "$extension_real_dir"
+ln -sfTr "$extension_real_dir" "$extension_data_dir"
+prepare_read_only_hierarchy "$fake_root" "$hierarchy"
+
+(! SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" --mutable=auto merge)
+
+
+: "/var/lib/extensions.mutable/… is a dangling symlink, auto-mutability, read-only merged"
+fake_root="$FAKE_ROOTS_DIR/read-only-mutable-dangling-symlink"
+hierarchy=/usr
+extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
+
+prepare_root "$fake_root" "$hierarchy"
+prepare_extension_image "$fake_root" "$hierarchy"
+ln -sfTr "/should/not/exist/" "$extension_data_dir"
+prepare_read_only_hierarchy "$fake_root" "$hierarchy"
+(! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
+
+# run systemd-sysext
+SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" --mutable=auto merge
+(! touch "$fake_root$hierarchy/should-still-fail-on-read-only-fs")
+extension_verify_after_merge "$fake_root" "$hierarchy" -e -h
+
+SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" unmerge
+extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
+
+
+: "/var/lib/extensions.mutable/… exists but ignored, mutability disabled explicitly, read-only merged"
+fake_root="$FAKE_ROOTS_DIR/disabled"
+hierarchy=/usr
+extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
+
+prepare_root "$fake_root" "$hierarchy"
+prepare_extension_image "$fake_root" "$hierarchy"
+prepare_extension_mutable_dir "$extension_data_dir"
+prepare_read_only_hierarchy "$fake_root" "$hierarchy"
+(! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
+
+# run systemd-sysext
+SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" --mutable=no merge
+(! touch "$fake_root$hierarchy/should-still-fail-on-read-only-fs")
+extension_verify_after_merge "$fake_root" "$hierarchy" -e -h
+
+SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" unmerge
+extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
+
+
+: "/var/lib/extensions.mutable/… exists but is imported instead, read-only merged"
+fake_root="$FAKE_ROOTS_DIR/imported"
+hierarchy=/usr
+extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
+
+prepare_root "$fake_root" "$hierarchy"
+prepare_extension_image "$fake_root" "$hierarchy"
+prepare_extension_mutable_dir "$extension_data_dir"
+prepare_read_only_hierarchy "$fake_root" "$hierarchy"
+(! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
+
+# run systemd-sysext
+SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" --mutable=import merge
+(! touch "$fake_root$hierarchy/should-still-fail-on-read-only-fs")
+extension_verify_after_merge "$fake_root" "$hierarchy" -e -h -u
+
+SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" unmerge
+extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
+
+
+: "/var/lib/extensions.mutable/… doesn't exists, mutability enabled, mutable merged"
+fake_root="$FAKE_ROOTS_DIR/enabled"
+hierarchy=/usr
+extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
+
+prepare_root "$fake_root" "$hierarchy"
+prepare_extension_image "$fake_root" "$hierarchy"
+prepare_read_only_hierarchy "$fake_root" "$hierarchy"
+(! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
+test ! -d "$extension_data_dir"
+
+SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" --mutable=yes merge
+test -d "$extension_data_dir"
+touch "$fake_root$hierarchy/now-is-mutable"
+extension_verify_after_merge "$fake_root" "$hierarchy" -e -h
+test -f "$extension_data_dir/now-is-mutable"
+
+SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" unmerge
+extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
+test -f "$extension_data_dir/now-is-mutable"
+test ! -f "$fake_root$hierarchy/now-is-mutable"
+
+
+: "/var/lib/extensions.mutable/… doesn't exists, auto-mutability, read-only merged"
+fake_root="$FAKE_ROOTS_DIR/simple-read-only-explicit"
+hierarchy=/usr
+
+prepare_root "$fake_root" "$hierarchy"
+prepare_extension_image "$fake_root" "$hierarchy"
+prepare_read_only_hierarchy "$fake_root" "$hierarchy"
+(! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
+
+SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" --mutable=auto merge
+(! touch "$fake_root$hierarchy/should-still-fail-on-read-only-fs")
+extension_verify_after_merge "$fake_root" "$hierarchy" -e -h
+
+SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" unmerge
+extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
+
+
+: "/var/lib/extensions.mutable/… doesn't exists, mutability enabled through env var, mutable merged"
+fake_root="$FAKE_ROOTS_DIR/enabled-env-var"
+hierarchy=/usr
+extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
+
+prepare_root "$fake_root" "$hierarchy"
+prepare_extension_image "$fake_root" "$hierarchy"
+prepare_read_only_hierarchy "$fake_root" "$hierarchy"
+(! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
+test ! -d "$extension_data_dir"
+
+SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" SYSTEMD_SYSEXT_MUTABLE_MODE=yes systemd-sysext --root="$fake_root" merge
+test -d "$extension_data_dir"
+touch "$fake_root$hierarchy/now-is-mutable"
+extension_verify_after_merge "$fake_root" "$hierarchy" -e -h
+test -f "$extension_data_dir/now-is-mutable"
+
+SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" SYSTEMD_SYSEXT_MUTABLE_MODE=yes systemd-sysext --root="$fake_root" unmerge
+extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
+test -f "$extension_data_dir/now-is-mutable"
+test ! -f "$fake_root$hierarchy/now-is-mutable"
+
+
+: "/var/lib/extensions.mutable/… doesn't exists, auto-mutability enabled through env var, read-only merged"
+fake_root="$FAKE_ROOTS_DIR/read-only-auto-env-var"
+hierarchy=/usr
+
+prepare_root "$fake_root" "$hierarchy"
+prepare_extension_image "$fake_root" "$hierarchy"
+prepare_read_only_hierarchy "$fake_root" "$hierarchy"
+(! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
+
+SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" SYSTEMD_SYSEXT_MUTABLE_MODE=auto systemd-sysext --root="$fake_root" --mutable=auto merge
+(! touch "$fake_root$hierarchy/should-still-fail-on-read-only-fs")
+extension_verify_after_merge "$fake_root" "$hierarchy" -e -h
+
+SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" SYSTEMD_SYSEXT_MUTABLE_MODE=auto systemd-sysext --root="$fake_root" unmerge
+extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
+
+
+: "Extension data in /var/lib/extensions.mutable/…, R/O hierarchy, auto-mutability enabled through env var, mutable merged"
+fake_root="$FAKE_ROOTS_DIR/auto-mutable-env-var"
+hierarchy=/usr
+extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
+
+prepare_root "$fake_root" "$hierarchy"
+prepare_extension_image "$fake_root" "$hierarchy"
+prepare_extension_mutable_dir "$extension_data_dir"
+prepare_read_only_hierarchy "$fake_root" "$hierarchy"
+(! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
+
+SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" SYSTEMD_SYSEXT_MUTABLE_MODE=auto systemd-sysext --root="$fake_root" merge
+touch "$fake_root$hierarchy/now-is-mutable"
+extension_verify_after_merge "$fake_root" "$hierarchy" -e -h -u
+test -f "$extension_data_dir/now-is-mutable"
+
+SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" SYSTEMD_SYSEXT_MUTABLE_MODE=auto systemd-sysext --root="$fake_root" unmerge
+extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
+test -f "$extension_data_dir/now-is-mutable"
+test ! -f "$fake_root$hierarchy/now-is-mutable"
+
+
+: "Extension data in /var/lib/extensions.mutable/…, R/O hierarchy, mutability disabled through env var, read-only merged"
+fake_root="$FAKE_ROOTS_DIR/env-var-disabled"
+hierarchy=/usr
+extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
+
+prepare_root "$fake_root" "$hierarchy"
+prepare_extension_image "$fake_root" "$hierarchy"
+prepare_extension_mutable_dir "$extension_data_dir"
+prepare_read_only_hierarchy "$fake_root" "$hierarchy"
+(! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
+
+SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" SYSTEMD_SYSEXT_MUTABLE_MODE=no systemd-sysext --root="$fake_root" merge
+(! touch "$fake_root$hierarchy/should-be-read-only")
+extension_verify_after_merge "$fake_root" "$hierarchy" -e -h
+
+SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" SYSTEMD_SYSEXT_MUTABLE_MODE=no systemd-sysext --root="$fake_root" unmerge
+extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
+
+
+: "/var/lib/extensions.mutable/… exists but is imported through env var, read-only merged"
+fake_root="$FAKE_ROOTS_DIR/imported-env-var"
+hierarchy=/usr
+extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
+
+prepare_root "$fake_root" "$hierarchy"
+prepare_extension_image "$fake_root" "$hierarchy"
+prepare_extension_mutable_dir "$extension_data_dir"
+prepare_read_only_hierarchy "$fake_root" "$hierarchy"
+(! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
+
+SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" SYSTEMD_SYSEXT_MUTABLE_MODE=import systemd-sysext --root="$fake_root" merge
+(! touch "$fake_root$hierarchy/should-still-fail-on-read-only-fs")
+extension_verify_after_merge "$fake_root" "$hierarchy" -e -h -u
+
+SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" SYSTEMD_SYSEXT_MUTABLE_MODE=import systemd-sysext --root="$fake_root" unmerge
+extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
+
+
+: "Extension data in /var/lib/extensions.mutable/…, R/O hierarchy, mutability enabled through env var but overridden via CLI option, read-only merged"
+fake_root="$FAKE_ROOTS_DIR/env-var-overridden"
+hierarchy=/usr
+extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
+
+prepare_root "$fake_root" "$hierarchy"
+prepare_extension_image "$fake_root" "$hierarchy"
+prepare_extension_mutable_dir "$extension_data_dir"
+prepare_read_only_hierarchy "$fake_root" "$hierarchy"
+(! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
+
+SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" SYSTEMD_SYSEXT_MUTABLE_MODE=yes systemd-sysext --root="$fake_root" --mutable=no merge
+(! touch "$fake_root$hierarchy/should-be-read-only")
+extension_verify_after_merge "$fake_root" "$hierarchy" -e -h
+
+SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" SYSTEMD_SYSEXT_MUTABLE_MODE=yes systemd-sysext --root="$fake_root" unmerge
+extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
+
+
+: "Extension data in /var/lib/extensions.mutable/…, R/O hierarchy, ephemeral mutability, mutable merged"
+fake_root="$FAKE_ROOTS_DIR/ephemeral"
+hierarchy=/usr
+extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
+
+prepare_root "$fake_root" "$hierarchy"
+prepare_extension_image "$fake_root" "$hierarchy"
+prepare_extension_mutable_dir "$extension_data_dir"
+prepare_read_only_hierarchy "$fake_root" "$hierarchy"
+(! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
+
+SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" --mutable=ephemeral merge
+touch "$fake_root$hierarchy/now-is-mutable"
+extension_verify_after_merge "$fake_root" "$hierarchy" -e -h
+test ! -f "$extension_data_dir/now-is-mutable"
+
+SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" unmerge
+extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
+test ! -f "$extension_data_dir/now-is-mutable"
+test ! -f "$fake_root$hierarchy/now-is-mutable"
+
+
+: "Extension data in /var/lib/extensions.mutable/…, R/O hierarchy, ephemeral mutability through env var, mutable merged"
+fake_root="$FAKE_ROOTS_DIR/ephemeral-env-var"
+hierarchy=/usr
+extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
+
+prepare_root "$fake_root" "$hierarchy"
+prepare_extension_image "$fake_root" "$hierarchy"
+prepare_extension_mutable_dir "$extension_data_dir"
+prepare_read_only_hierarchy "$fake_root" "$hierarchy"
+(! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
+
+SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" SYSTEMD_SYSEXT_MUTABLE_MODE=ephemeral systemd-sysext --root="$fake_root" merge
+touch "$fake_root$hierarchy/now-is-mutable"
+extension_verify_after_merge "$fake_root" "$hierarchy" -e -h
+test ! -f "$extension_data_dir/now-is-mutable"
+
+SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" SYSTEMD_SYSEXT_MUTABLE_MODE=ephemeral systemd-sysext --root="$fake_root" unmerge
+extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
+test ! -f "$extension_data_dir/now-is-mutable"
+test ! -f "$fake_root$hierarchy/now-is-mutable"
+
+
+: "Extension data in /var/lib/extensions.mutable/…, R/O hierarchy, ephemeral import mutability, mutable merged"
+fake_root="$FAKE_ROOTS_DIR/ephemeral-import"
+hierarchy=/usr
+extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
+
+prepare_root "$fake_root" "$hierarchy"
+prepare_extension_image "$fake_root" "$hierarchy"
+prepare_extension_mutable_dir "$extension_data_dir"
+prepare_read_only_hierarchy "$fake_root" "$hierarchy"
+(! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
+
+# run systemd-sysext
+SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" --mutable=ephemeral-import merge
+touch "$fake_root$hierarchy/now-is-mutable"
+extension_verify_after_merge "$fake_root" "$hierarchy" -e -h -u
+test ! -f "$extension_data_dir/now-is-mutable"
+
+SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" unmerge
+extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
+test ! -f "$extension_data_dir/now-is-mutable"
+test ! -f "$fake_root$hierarchy/now-is-mutable"
+
+
+: "Extension data in /var/lib/extensions.mutable/…, R/O hierarchy, ephemeral import mutability through env var, mutable merged"
+fake_root="$FAKE_ROOTS_DIR/ephemeral-import-env-var"
+hierarchy=/usr
+extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
+
+prepare_root "$fake_root" "$hierarchy"
+prepare_extension_image "$fake_root" "$hierarchy"
+prepare_extension_mutable_dir "$extension_data_dir"
+prepare_read_only_hierarchy "$fake_root" "$hierarchy"
+(! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
+
+SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" SYSTEMD_SYSEXT_MUTABLE_MODE=ephemeral-import systemd-sysext --root="$fake_root" merge
+touch "$fake_root$hierarchy/now-is-mutable"
+extension_verify_after_merge "$fake_root" "$hierarchy" -e -h -u
+test ! -f "$extension_data_dir/now-is-mutable"
+
+SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" SYSTEMD_SYSEXT_MUTABLE_MODE=ephemeral-import systemd-sysext --root="$fake_root" unmerge
+extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
+test ! -f "$extension_data_dir/now-is-mutable"
+test ! -f "$fake_root$hierarchy/now-is-mutable"
+
+
+: "Extension data pointing to mutable hierarchy, ephemeral import mutability, expected fail"
+fake_root="$FAKE_ROOTS_DIR/ephemeral-import-self"
+hierarchy=/usr
+extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
+extension_real_dir="$fake_root$hierarchy"
+
+prepare_root "$fake_root" "$hierarchy"
+prepare_extension_image "$fake_root" "$hierarchy"
+prepare_extension_mutable_dir "$extension_real_dir"
+ln -sfTr "$extension_real_dir" "$extension_data_dir"
+prepare_hierarchy "$fake_root" "$hierarchy"
+touch "$fake_root$hierarchy/should-succeed-on-read-only-fs"
+
+(! SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" --mutable=ephemeral-import merge)
+
+
+: "Extension data pointing to mutable hierarchy,  import mutability, expected fail"
+fake_root="$FAKE_ROOTS_DIR/import-self"
+hierarchy=/usr
+extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
+extension_real_dir="$fake_root$hierarchy"
+
+prepare_root "$fake_root" "$hierarchy"
+prepare_extension_image "$fake_root" "$hierarchy"
+prepare_extension_mutable_dir "$extension_real_dir"
+ln -sfTr "$extension_real_dir" "$extension_data_dir"
+prepare_hierarchy "$fake_root" "$hierarchy"
+touch "$fake_root$hierarchy/should-succeed-on-read-only-fs"
+
+(! SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" --mutable=import merge)
+
+exit 0
index 7618e92c9fa4ffa2cdae096256187f688fae037c..a8d5a0c3fa52cdf3720d7f668709d1119402a8ba 100755 (executable)
@@ -8,13 +8,20 @@ set -eux
 
 systemd-analyze log-level debug
 
-run_with_cred_compare() {
+run_with_cred_compare() (
     local cred="${1:?}"
     local exp="${2?}"
+    local log_file
     shift 2
 
-    diff <(systemd-run -p SetCredential="$cred" --wait --pipe -- systemd-creds "$@") <(echo -ne "$exp")
-}
+    log_file="$(mktemp)"
+    # shellcheck disable=SC2064
+    trap "rm -f '$log_file'" RETURN
+
+    set -o pipefail
+    systemd-run -p SetCredential="$cred" --wait --pipe -- systemd-creds "$@" | tee "$log_file"
+    diff "$log_file" <(echo -ne "$exp")
+)
 
 # Sanity checks
 #
@@ -200,6 +207,10 @@ elif [ -d /sys/firmware/qemu_fw_cfg/by_name ]; then
     [ "$(cat /tmp/sourcedfromcredential)" = "tmpfilessecret" ]
     [ "$(cat /etc/motd.d/50-provision.conf)" = "hello" ]
     [ "$(cat /etc/issue.d/50-provision.conf)" = "welcome" ]
+
+    # Verify that adding a unit and drop-in via credentials worked
+    systemctl start my-service
+    test -f /tmp/unit-dropin
 else
     echo "qemu_fw_cfg support missing in kernel. Sniff!"
     expected_credential=""
index 3e549dd7dbee1e8837f1d7969ee0b23246557bc9..e5d930175f7da1fc9a79b11946ea5f7dbf154ec8 100755 (executable)
@@ -16,7 +16,7 @@ test "$(cat /sys/fs/cgroup/init.scope/memory.high)" != "max"
 [[ "$(get_cgroup_hierarchy)" == "unified" ]] || echo "no cgroupsv2" >>/skipped
 [[ -x /usr/lib/systemd/systemd-oomd ]] || echo "no oomd" >>/skipped
 if [[ -s /skipped ]]; then
-    exit 0
+    exit 77
 fi
 
 rm -rf /run/systemd/system/testsuite-55-testbloat.service.d
index c64b2039f300f1f5346a063e37b1467c698bd2af..bebe7b727b62070d74e7864b46e7c6f78c1aa2d3 100755 (executable)
@@ -9,7 +9,7 @@ set -o pipefail
 
 if ! command -v systemd-repart >/dev/null; then
     echo "no systemd-repart" >/skipped
-    exit 0
+    exit 77
 fi
 
 # shellcheck source=test/units/test-control.sh
index 61564dc1664e1800f8a3278cc051350219ace053..0e044037b85ade90b9b754fa0aaf18a0ecacda0d 100755 (executable)
@@ -104,6 +104,14 @@ sleep 10
 
 systemctl daemon-reload
 
+# Same test for reexec, but we wait here
+timeout 15 bash -c 'while systemctl daemon-reexec; do true; done'
+
+# Rate limit should reset after 9s
+sleep 10
+
+systemctl daemon-reexec
+
 # Let's now test the notify-reload logic
 
 cat >/run/notify-reload-test.sh <<EOF
index 0dbedc4c868408e0343535eaf17f1c34b5c1c7e4..84c92247b81ddb9d71f2e45faad500364f5a8db6 100755 (executable)
@@ -44,12 +44,12 @@ MINOR_REQUIRED=7
 
 if [[ "$KERNEL_MAJOR" -lt $MAJOR_REQUIRED || ("$KERNEL_MAJOR" -eq $MAJOR_REQUIRED && "$KERNEL_MINOR" -lt $MINOR_REQUIRED) ]]; then
     echo "kernel is not 5.7+" >>/skipped
-    exit 0
+    exit 77
 fi
 
 if systemctl --version | grep -q -F -- "-BPF_FRAMEWORK"; then
     echo "bpf-framework is disabled" >>/skipped
-    exit 0
+    exit 77
 fi
 
 trap teardown EXIT
index 1123a1d33b1f22fc3e531595f7c92d5f92350fdb..120b5f3671b73e82117b087eadfaaba9ae201775 100755 (executable)
@@ -239,16 +239,27 @@ testcase_nvme_subsystem() {
 }
 
 testcase_virtio_scsi_identically_named_partitions() {
-    local num
+    local num_part num_disk i j
+    local alphabet='abcdefghijklmnopqrstuvwxyz'
 
     if [[ -v ASAN_OPTIONS || "$(systemd-detect-virt -v)" == "qemu" ]]; then
-        num=$((4 * 4))
+        num_part=4
+        num_disk=4
     else
-        num=$((16 * 8))
+        num_part=8
+        num_disk=16
     fi
 
+    for ((i = 0; i < num_disk; i++)); do
+        sfdisk "/dev/sd${alphabet:$i:1}" <<EOF
+label: gpt
+
+$(for ((j = 1; j <= num_part; j++)); do echo 'name="Hello world", size=2M'; done)
+EOF
+    done
+
     lsblk --noheadings -a -o NAME,PARTLABEL
-    [[ "$(lsblk --noheadings -a -o NAME,PARTLABEL | grep -c "Hello world")" -eq "$num" ]]
+    [[ "$(lsblk --noheadings -a -o NAME,PARTLABEL | grep -c "Hello world")" -eq "$((num_part * num_disk))" ]]
 }
 
 testcase_multipath_basic_failover() {
@@ -270,6 +281,16 @@ blacklist_exceptions {
 blacklist {
 }
 EOF
+
+    sfdisk /dev/sda <<EOF
+label: gpt
+
+name="first_partition", size=5M
+uuid="deadbeef-dead-dead-beef-000000000000", name="failover_part", size=5M
+EOF
+    udevadm settle
+    mkfs.ext4 -U "deadbeef-dead-dead-beef-111111111111" -L "failover_vol" "/dev/sda2"
+
     modprobe -v dm_multipath
     systemctl start multipathd.service
     systemctl status multipathd.service
@@ -950,6 +971,16 @@ testcase_long_sysfs_path() {
     stat /sys/block/vda
     readlink -f /sys/block/vda/dev
 
+    dev="/dev/vda"
+    sfdisk "${dev:?}" <<EOF
+label: gpt
+
+name="test_swap", size=32M
+uuid="deadbeef-dead-dead-beef-000000000000", name="test_part", size=5M
+EOF
+    udevadm settle
+    mkswap -U "deadbeef-dead-dead-beef-111111111111" -L "swap_vol" "${dev}1"
+    mkfs.ext4 -U "deadbeef-dead-dead-beef-222222222222" -L "data_vol" "${dev}2"
     udevadm wait --settle --timeout=30 "${expected_symlinks[@]}"
 
     # Try to mount the data partition manually (using its label)
index 953f2a16bf284e7815feffdc9a8b304e88aecf38..5e658e04856e287753eb52b91300b247db07a92d 100755 (executable)
@@ -6,111 +6,112 @@ set -eux
 set -o pipefail
 
 SYSUPDATE=/lib/systemd/systemd-sysupdate
-SECTOR_SIZES="512 4096"
-BACKING_FILE=/var/tmp/72-joined.raw
-export SYSTEMD_ESP_PATH=/var/tmp/72-esp
-export SYSTEMD_XBOOTLDR_PATH=/var/tmp/72-xbootldr
+SECTOR_SIZES=(512 4096)
+WORKDIR="$(mktemp -d /var/tmp/test-72-XXXXXX)"
+BACKING_FILE="$WORKDIR/joined.raw"
+export SYSTEMD_ESP_PATH="$WORKDIR/esp"
+export SYSTEMD_XBOOTLDR_PATH="$WORKDIR/xbootldr"
 export SYSTEMD_PAGER=cat
 export SYSTEMD_LOG_LEVEL=debug
 
-if ! test -x "$SYSUPDATE"; then
+if [[ ! -x "$SYSUPDATE" ]]; then
     echo "no systemd-sysupdate" >/skipped
-    exit 0
+    exit 77
 fi
 
 # Loopback devices may not be supported. They are used because sfdisk cannot
 # change the sector size of a file, and we want to test both 512 and 4096 byte
 # sectors. If loopback devices are not supported, we can only test one sector
 # size, and the underlying device is likely to have a sector size of 512 bytes.
-if ! losetup --find >/dev/null 2>&1; then
+if [[ ! -e /dev/loop-control ]]; then
     echo "No loopback device support"
-    SECTOR_SIZES="512"
+    SECTOR_SIZES=(512)
 fi
 
-trap cleanup ERR
-cleanup() {
-    set +o pipefail
-    blockdev="$( losetup --list --output NAME,BACK-FILE | grep $BACKING_FILE | cut -d' ' -f1)"
-    [ -n "$blockdev" ] && losetup --detach "$blockdev"
-    rm -f "$BACKING_FILE"
-    rm -rf /var/tmp/72-{dirs,defs,source,xbootldr,esp}
-    rm -f /testok
+at_exit() {
+    set +e
+
+    losetup -n --output NAME --associated "$BACKING_FILE" | while read -r loop_dev; do
+        losetup --detach "$loop_dev"
+    done
+
+    rm -rf "$WORKDIR"
 }
 
+trap at_exit EXIT
+
 new_version() {
-    # Inputs:
-    # $1: sector size
-    # $2: version
+    local sector_size="${1:?}"
+    local version="${2:?}"
 
     # Create a pair of random partition payloads, and compress one
-    dd if=/dev/urandom of="/var/tmp/72-source/part1-$2.raw" bs="$1" count=2048
-    dd if=/dev/urandom of="/var/tmp/72-source/part2-$2.raw" bs="$1" count=2048
-    gzip -k -f "/var/tmp/72-source/part2-$2.raw"
+    dd if=/dev/urandom of="$WORKDIR/source/part1-$version.raw" bs="$sector_size" count=2048
+    dd if=/dev/urandom of="$WORKDIR/source/part2-$version.raw" bs="$sector_size" count=2048
+    gzip -k -f "$WORKDIR/source/part2-$version.raw"
 
     # Create a random "UKI" payload
-    echo $RANDOM >"/var/tmp/72-source/uki-$2.efi"
+    echo $RANDOM >"$WORKDIR/source/uki-$version.efi"
 
     # Create a random extra payload
-    echo $RANDOM >"/var/tmp/72-source/uki-extra-$2.efi"
+    echo $RANDOM >"$WORKDIR/source/uki-extra-$version.efi"
 
     # Create tarball of a directory
-    mkdir -p "/var/tmp/72-source/dir-$2"
-    echo $RANDOM >"/var/tmp/72-source/dir-$2/foo.txt"
-    echo $RANDOM >"/var/tmp/72-source/dir-$2/bar.txt"
-    tar --numeric-owner -C "/var/tmp/72-source/dir-$2/" -czf "/var/tmp/72-source/dir-$2.tar.gz" .
+    mkdir -p "$WORKDIR/source/dir-$version"
+    echo $RANDOM >"$WORKDIR/source/dir-$version/foo.txt"
+    echo $RANDOM >"$WORKDIR/source/dir-$version/bar.txt"
+    tar --numeric-owner -C "$WORKDIR/source/dir-$version/" -czf "$WORKDIR/source/dir-$version.tar.gz" .
 
-    ( cd /var/tmp/72-source/ && sha256sum uki* part* dir-*.tar.gz >SHA256SUMS )
+    (cd "$WORKDIR/source" && sha256sum uki* part* dir-*.tar.gz >SHA256SUMS)
 }
 
 update_now() {
     # Update to newest version. First there should be an update ready, then we
     # do the update, and then there should not be any ready anymore
 
-    "$SYSUPDATE" --definitions=/var/tmp/72-defs --verify=no check-new
-    "$SYSUPDATE" --definitions=/var/tmp/72-defs --verify=no update
-    ( ! "$SYSUPDATE" --definitions=/var/tmp/72-defs --verify=no check-new )
+    "$SYSUPDATE" --definitions="$WORKDIR/defs" --verify=no check-new
+    "$SYSUPDATE" --definitions="$WORKDIR/defs" --verify=no update
+    (! "$SYSUPDATE" --definitions="$WORKDIR/defs" --verify=no check-new)
 }
 
 verify_version() {
-    # Inputs:
-    # $1: block device
-    # $2: sector size
-    # $3: version
-    # $4: partition number of part1
-    # $5: partition number of part2
+    local block_device="${1:?}"
+    local sector_size="${2:?}"
+    local version="${3:?}"
+    local part1_number="${4:?}"
+    local part2_number="${5:?}"
+    local gpt_reserved_sectors part1_offset part2_offset
 
-    gpt_reserved_sectors=$(( 1024 * 1024 / $2 ))
-    part1_offset=$(( ( $4 - 1 ) * 2048 + gpt_reserved_sectors ))
-    part2_offset=$(( ( $5 - 1 ) * 2048 + gpt_reserved_sectors ))
+    gpt_reserved_sectors=$((1024 * 1024 / sector_size))
+    part1_offset=$(((part1_number - 1) * 2048 + gpt_reserved_sectors))
+    part2_offset=$(((part2_number - 1) * 2048 + gpt_reserved_sectors))
 
     # Check the partitions
-    dd if="$1" bs="$2" skip="$part1_offset" count=2048 | cmp "/var/tmp/72-source/part1-$3.raw"
-    dd if="$1" bs="$2" skip="$part2_offset" count=2048 | cmp "/var/tmp/72-source/part2-$3.raw"
+    dd if="$block_device" bs="$sector_size" skip="$part1_offset" count=2048 | cmp "$WORKDIR/source/part1-$version.raw"
+    dd if="$block_device" bs="$sector_size" skip="$part2_offset" count=2048 | cmp "$WORKDIR/source/part2-$version.raw"
 
     # Check the UKI
-    cmp "/var/tmp/72-source/uki-$3.efi" "/var/tmp/72-xbootldr/EFI/Linux/uki_$3+3-0.efi"
-    test -z "$(ls -A /var/tmp/72-esp/EFI/Linux)"
+    cmp "$WORKDIR/source/uki-$version.efi" "$WORKDIR/xbootldr/EFI/Linux/uki_$version+3-0.efi"
+    test -z "$(ls -A "$WORKDIR/esp/EFI/Linux")"
 
     # Check the extra efi
-    cmp "/var/tmp/72-source/uki-extra-$3.efi" "/var/tmp/72-xbootldr/EFI/Linux/uki_$3.efi.extra.d/extra.addon.efi"
+    cmp "$WORKDIR/source/uki-extra-$version.efi" "$WORKDIR/xbootldr/EFI/Linux/uki_$version.efi.extra.d/extra.addon.efi"
 
     # Check the directories
-    cmp "/var/tmp/72-source/dir-$3/foo.txt" /var/tmp/72-dirs/current/foo.txt
-    cmp "/var/tmp/72-source/dir-$3/bar.txt" /var/tmp/72-dirs/current/bar.txt
+    cmp "$WORKDIR/source/dir-$version/foo.txt" "$WORKDIR/dirs/current/foo.txt"
+    cmp "$WORKDIR/source/dir-$version/bar.txt" "$WORKDIR/dirs/current/bar.txt"
 }
 
-for sector_size in $SECTOR_SIZES ; do
+for sector_size in "${SECTOR_SIZES[@]}"; do
     # Disk size of:
     # - 1MB for GPT
     # - 4 partitions of 2048 sectors each
     # - 1MB for backup GPT
-    disk_size=$(( sector_size * 2048 * 4 + 1024 * 1024 * 2 ))
+    disk_size=$((sector_size * 2048 * 4 + 1024 * 1024 * 2))
     rm -f "$BACKING_FILE"
     truncate -s "$disk_size" "$BACKING_FILE"
 
-    if losetup --find >/dev/null 2>&1; then
-        # shellcheck disable=SC2086
-        blockdev="$(losetup --find --show --sector-size $sector_size $BACKING_FILE)"
+    if [[ -e /dev/loop-control ]]; then
+        blockdev="$(losetup --find --show --sector-size "$sector_size" "$BACKING_FILE")"
     else
         blockdev="$BACKING_FILE"
     fi
@@ -126,16 +127,15 @@ size=2048, type=2c7357ed-ebd2-46d9-aec1-23d437ec2bf5, name=_empty
 size=2048, type=2c7357ed-ebd2-46d9-aec1-23d437ec2bf5, name=_empty
 EOF
 
-    rm -rf /var/tmp/72-dirs
-    mkdir -p /var/tmp/72-dirs
+    for d in "dirs" "defs"; do
+        rm -rf "${WORKDIR:?}/$d"
+        mkdir -p "$WORKDIR/$d"
+    done
 
-    rm -rf /var/tmp/72-defs
-    mkdir -p /var/tmp/72-defs
-
-    cat >/var/tmp/72-defs/01-first.conf <<EOF
+    cat >"$WORKDIR/defs/01-first.conf" <<EOF
 [Source]
 Type=regular-file
-Path=/var/tmp/72-source
+Path=$WORKDIR/source
 MatchPattern=part1-@v.raw
 
 [Target]
@@ -145,10 +145,10 @@ MatchPattern=part1-@v
 MatchPartitionType=root-x86-64
 EOF
 
-    cat >/var/tmp/72-defs/02-second.conf <<EOF
+    cat >"$WORKDIR/defs/02-second.conf" <<EOF
 [Source]
 Type=regular-file
-Path=/var/tmp/72-source
+Path=$WORKDIR/source
 MatchPattern=part2-@v.raw.gz
 
 [Target]
@@ -158,24 +158,24 @@ MatchPattern=part2-@v
 MatchPartitionType=root-x86-64-verity
 EOF
 
-    cat >/var/tmp/72-defs/03-third.conf <<EOF
+    cat >"$WORKDIR/defs/03-third.conf" <<EOF
 [Source]
 Type=directory
-Path=/var/tmp/72-source
+Path=$WORKDIR/source
 MatchPattern=dir-@v
 
 [Target]
 Type=directory
-Path=/var/tmp/72-dirs
-CurrentSymlink=/var/tmp/72-dirs/current
+Path=$WORKDIR/dirs
+CurrentSymlink=$WORKDIR/dirs/current
 MatchPattern=dir-@v
 InstancesMax=3
 EOF
 
-    cat >/var/tmp/72-defs/04-fourth.conf <<EOF
+    cat >"$WORKDIR/defs/04-fourth.conf" <<EOF
 [Source]
 Type=regular-file
-Path=/var/tmp/72-source
+Path=$WORKDIR/source
 MatchPattern=uki-@v.efi
 
 [Target]
@@ -191,10 +191,10 @@ TriesDone=0
 InstancesMax=2
 EOF
 
-    cat >/var/tmp/72-defs/05-fifth.conf <<EOF
+    cat >"$WORKDIR/defs/05-fifth.conf" <<EOF
 [Source]
 Type=regular-file
-Path=/var/tmp/72-source
+Path=$WORKDIR/source
 MatchPattern=uki-extra-@v.efi
 
 [Target]
@@ -206,11 +206,8 @@ Mode=0444
 InstancesMax=2
 EOF
 
-    rm -rf /var/tmp/72-esp /var/tmp/72-xbootldr
-    mkdir -p /var/tmp/72-esp/EFI/Linux /var/tmp/72-xbootldr/EFI/Linux
-
-    rm -rf /var/tmp/72-source
-    mkdir -p /var/tmp/72-source
+    rm -rf "${WORKDIR:?}"/{esp,xbootldr,source}
+    mkdir -p "$WORKDIR"/{source,esp/EFI/Linux,xbootldr/EFI/Linux}
 
     # Install initial version and verify
     new_version "$sector_size" v1
@@ -226,9 +223,9 @@ EOF
     new_version "$sector_size" v3
     update_now
     verify_version "$blockdev" "$sector_size" v3 1 3
-    test ! -f "/var/tmp/72-xbootldr/EFI/Linux/uki_v1+3-0.efi"
-    test ! -f "/var/tmp/72-xbootldr/EFI/Linux/uki_v1.efi.extra.d/extra.addon.efi"
-    test ! -d "/var/tmp/72-xbootldr/EFI/Linux/uki_v1.efi.extra.d"
+    test ! -f "$WORKDIR/xbootldr/EFI/Linux/uki_v1+3-0.efi"
+    test ! -f "$WORKDIR/xbootldr/EFI/Linux/uki_v1.efi.extra.d/extra.addon.efi"
+    test ! -d "$WORKDIR/xbootldr/EFI/Linux/uki_v1.efi.extra.d"
 
     # Create fourth version, and update through a file:// URL. This should be
     # almost as good as testing HTTP, but is simpler for us to set up. file:// is
@@ -238,10 +235,10 @@ EOF
     # see above)
     new_version "$sector_size" v4
 
-    cat >/var/tmp/72-defs/02-second.conf <<EOF
+    cat >"$WORKDIR/defs/02-second.conf" <<EOF
 [Source]
 Type=url-file
-Path=file:///var/tmp/72-source
+Path=file://$WORKDIR/source
 MatchPattern=part2-@v.raw.gz
 
 [Target]
@@ -251,16 +248,16 @@ MatchPattern=part2-@v
 MatchPartitionType=root-x86-64-verity
 EOF
 
-    cat >/var/tmp/72-defs/03-third.conf <<EOF
+    cat >"$WORKDIR/defs/03-third.conf" <<EOF
 [Source]
 Type=url-tar
-Path=file:///var/tmp/72-source
+Path=file://$WORKDIR/source
 MatchPattern=dir-@v.tar.gz
 
 [Target]
 Type=directory
-Path=/var/tmp/72-dirs
-CurrentSymlink=/var/tmp/72-dirs/current
+Path=$WORKDIR/dirs
+CurrentSymlink=$WORKDIR/dirs/current
 MatchPattern=dir-@v
 InstancesMax=3
 EOF
@@ -269,10 +266,8 @@ EOF
     verify_version "$blockdev" "$sector_size" v4 2 4
 
     # Cleanup
-    [ -b "$blockdev" ] && losetup --detach "$blockdev"
+    [[ -b "$blockdev" ]] && losetup --detach "$blockdev"
     rm "$BACKING_FILE"
 done
 
-rm -r /var/tmp/72-{dirs,defs,source,xbootldr,esp}
-
 touch /testok
index 6552643ee9671089e91124b5bbefa39bf2d66ea4..f48607c0db2ce9efcde17b9373ba51e26a537fad 100755 (executable)
@@ -77,24 +77,26 @@ rm -fv /run/systemd/coredump.conf.d/99-external.conf
 # Wait a bit for the coredumps to get processed
 timeout 30 bash -c "while [[ \$(coredumpctl list -q --no-legend $CORE_TEST_BIN | wc -l) -lt 4 ]]; do sleep 1; done"
 
-# Make sure we can forward crashes back to containers
-CONTAINER="testsuite-74-container"
-
-mkdir -p "/var/lib/machines/$CONTAINER"
-mkdir -p "/run/systemd/system/systemd-nspawn@$CONTAINER.service.d"
-# Bind-mounting /etc into the container kinda defeats the purpose of --volatile=,
-# but we need the ASan-related overrides scattered across /etc
-cat > "/run/systemd/system/systemd-nspawn@$CONTAINER.service.d/override.conf" << EOF
+if cgroupfs_supports_user_xattrs; then
+    # Make sure we can forward crashes back to containers
+    CONTAINER="testsuite-74-container"
+
+    mkdir -p "/var/lib/machines/$CONTAINER"
+    mkdir -p "/run/systemd/system/systemd-nspawn@$CONTAINER.service.d"
+    # Bind-mounting /etc into the container kinda defeats the purpose of --volatile=,
+    # but we need the ASan-related overrides scattered across /etc
+    cat > "/run/systemd/system/systemd-nspawn@$CONTAINER.service.d/override.conf" <<EOF
 [Service]
 ExecStart=
 ExecStart=systemd-nspawn --quiet --link-journal=try-guest --keep-unit --machine=%i --boot \
                          --volatile=yes --directory=/ --bind-ro=/etc --inaccessible=/etc/machine-id
 EOF
-systemctl daemon-reload
+    systemctl daemon-reload
+
+    [[ "$(systemd-detect-virt)" == "qemu" ]] && TIMEOUT=120 || TIMEOUT=60
 
-if cgroupfs_supports_user_xattrs; then
     machinectl start "$CONTAINER"
-    timeout 60 bash -xec "until systemd-run -M '$CONTAINER' -q --wait --pipe true; do sleep .5; done"
+    timeout "$TIMEOUT" bash -xec "until systemd-run -M '$CONTAINER' -q --wait --pipe true; do sleep .5; done"
 
     [[ "$(systemd-run -M "$CONTAINER" -q --wait --pipe coredumpctl list -q --no-legend /usr/bin/sleep | wc -l)" -eq 0 ]]
     machinectl copy-to "$CONTAINER" "$MAKE_DUMP_SCRIPT"
@@ -102,6 +104,8 @@ if cgroupfs_supports_user_xattrs; then
     systemd-run -M "$CONTAINER" -q --wait --pipe "$MAKE_DUMP_SCRIPT" "/usr/bin/sleep" "SIGTRAP"
     # Wait a bit for the coredumps to get processed
     timeout 30 bash -c "while [[ \$(systemd-run -M $CONTAINER -q --wait --pipe coredumpctl list -q --no-legend /usr/bin/sleep | wc -l) -lt 2 ]]; do sleep 1; done"
+
+    rm -rf "/var/lib/machines/$CONTAINER"
 fi
 
 coredumpctl
index e7ccea1ede6d2a99096f9f4030b1e935deff8cc0..5b5b0a13a398303886562b9b7898ce75b58b63fc 100755 (executable)
@@ -5,13 +5,21 @@ set -eux
 set -o pipefail
 
 at_exit() {
+    rm -f /run/credstore/network.conf.50-testme
     rm -f /run/credstore/network.network.50-testme
+    rm -f /run/systemd/networkd.conf.d/50-testme.conf
+    rm -f /run/systemd/network/50-testme.network
     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.conf.50-testme <<EOF
+[Network]
+SpeedMeter=yes
+EOF
+
 cat > /run/credstore/network.network.50-testme <<EOF
 [Match]
 Property=IDONTEXIST
@@ -19,9 +27,11 @@ EOF
 
 systemctl edit systemd-network-generator.service --stdin --drop-in=50-testme.conf <<EOF
 [Service]
+LoadCredential=network.conf.50-testme
 LoadCredential=network.network.50-testme
 EOF
 
 systemctl restart systemd-network-generator
 
-test -f /run/systemd/network/50-testme.network
+diff /run/credstore/network.conf.50-testme /run/systemd/networkd.conf.d/50-testme.conf
+diff /run/credstore/network.network.50-testme /run/systemd/network/50-testme.network
index 6cd5267b721abe94da7d78d1c12573aa83a15a71..d05de17bde059916e242e2514529a12e74d01773 100755 (executable)
@@ -15,6 +15,8 @@ at_exit() {
             {/usr/lib,/etc}/systemd/network/"$LINK_NAME" "/etc/systemd/network/${NETWORK_NAME}.d" \
             "new" "+4"
     fi
+
+    rm -f /run/systemd/networkd.conf.d/10-hoge.conf
 }
 
 trap at_exit EXIT
@@ -110,3 +112,12 @@ ip_link="$(ip link show test2)"
 if systemctl --quiet is-active systemd-udevd; then
     assert_in 'alias test_alias' "$ip_link"
 fi
+
+mkdir -p /run/systemd/networkd.conf.d
+cat >/run/systemd/networkd.conf.d/10-hoge.conf <<EOF
+# TEST DROP-IN FILE
+[Network]
+SpeedMeter=yes
+EOF
+
+assert_in '# TEST DROP-IN FILE' "$(networkctl cat)"
index 275bf296b8d9fb17bfbd81fc9a4f87f106640c0d..fc29e642fb0ad2625e68398da1761dd07146e520 100755 (executable)
@@ -200,6 +200,7 @@ cat >/run/systemd/network/10-dns0.network <<EOF
 Name=dns0
 
 [Network]
+IPv6AcceptRA=no
 Address=10.0.0.1/24
 Address=fd00:dead:beef:cafe::1/64
 DNSSEC=allow-downgrade
@@ -216,6 +217,7 @@ cat >/run/systemd/network/10-dns1.network <<EOF
 Name=dns1
 
 [Network]
+IPv6AcceptRA=no
 Address=10.99.0.1/24
 DNSSEC=no
 EOF
@@ -717,7 +719,9 @@ if command -v nft >/dev/null; then
     sleep 2
     drop_dns_outbound_traffic
     set +e
-    run dig stale1.unsigned.test -t A
+    # Make sure we give sd-resolved enough time to timeout (5-10s) before giving up
+    # See: https://github.com/systemd/systemd/issues/31639#issuecomment-2009152617
+    run dig +tries=1 +timeout=15 stale1.unsigned.test -t A
     set -eux
     grep -qE "no servers could be reached" "$RUN_OUT"
     nft flush ruleset
@@ -736,7 +740,8 @@ if command -v nft >/dev/null; then
     grep -qE "NOERROR" "$RUN_OUT"
     sleep 2
     drop_dns_outbound_traffic
-    run dig stale1.unsigned.test -t A
+    # Make sure we give sd-resolved enough time to timeout (5-10s) and serve the stale data (see above)
+    run dig +tries=1 +timeout=15 stale1.unsigned.test -t A
     grep -qE "NOERROR" "$RUN_OUT"
     grep -qE "10.0.0.112" "$RUN_OUT"
 
diff --git a/test/units/testsuite-77-client.sh b/test/units/testsuite-77-client.sh
deleted file mode 100755 (executable)
index 0d9487a..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# shellcheck source=test/units/util.sh
-. "$(dirname "$0")"/util.sh
-
-export SYSTEMD_LOG_LEVEL=debug
-
-assert_eq "$LISTEN_FDS" "1"
-assert_eq "$LISTEN_FDNAMES" "socket"
-read -r -u 3 text
-assert_eq "$text" "Socket"
diff --git a/test/units/testsuite-77-run.sh b/test/units/testsuite-77-run.sh
deleted file mode 100755 (executable)
index fadd34d..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# shellcheck source=test/units/util.sh
-. "$(dirname "$0")"/util.sh
-
-export SYSTEMD_LOG_LEVEL=debug
-
-assert_eq "$LISTEN_FDS" "1"
-assert_eq "$LISTEN_FDNAMES" "new-file"
-read -r -u 3 text
-assert_eq "$text" "New"
diff --git a/test/units/testsuite-77.service b/test/units/testsuite-77.service
deleted file mode 100644 (file)
index 6ed8add..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=TEST-77-OPENFILE
-
-[Service]
-OpenFile=/test-77-open.dat:open:read-only
-OpenFile=/test-77-file.dat
-ExecStartPre=rm -f /failed /testok
-ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
-Type=oneshot
diff --git a/test/units/testsuite-77.sh b/test/units/testsuite-77.sh
deleted file mode 100755 (executable)
index 2b85a8c..0000000
+++ /dev/null
@@ -1,38 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# shellcheck source=test/units/util.sh
-. "$(dirname "$0")"/util.sh
-
-export SYSTEMD_LOG_LEVEL=debug
-
-assert_eq "$LISTEN_FDS" "2"
-assert_eq "$LISTEN_FDNAMES" "open:test-77-file.dat"
-read -r -u 3 text
-assert_eq "$text" "Open"
-read -r -u 4 text
-assert_eq "$text" "File"
-
-# Test for socket
-systemctl start testsuite-77-server.socket
-systemd-run -p OpenFile=/tmp/test.sock:socket:read-only \
-            --wait \
-            --pipe \
-            /usr/lib/systemd/tests/testdata/units/testsuite-77-client.sh
-
-# Tests for D-Bus
-diff <(systemctl show -p OpenFile testsuite-77) - <<EOF
-OpenFile=/test-77-open.dat:open:read-only
-OpenFile=/test-77-file.dat
-EOF
-echo "New" >/test-77-new-file.dat
-systemd-run --wait -p OpenFile=/test-77-new-file.dat:new-file:read-only "$(dirname "$0")"/testsuite-77-run.sh
-
-assert_rc 202 systemd-run --wait -p OpenFile=/test-77-new-file.dat:new-file:read-only -p OpenFile=/test-77-mssing-file.dat:missing-file:read-only "$(dirname "$0")"/testsuite-77-run.sh
-
-assert_rc 0 systemd-run --wait -p OpenFile=/test-77-new-file.dat:new-file:read-only -p OpenFile=/test-77-mssing-file.dat:missing-file:read-only,graceful "$(dirname "$0")"/testsuite-77-run.sh
-
-# End
-touch /testok
index 224cd75c7fead009a23de405f186dd1b90d8eade..74361455980e5656872c4af0536db80ac5dcce21 100755 (executable)
@@ -27,6 +27,8 @@ if [ -f /run/testsuite82.touch3 ]; then
     echo "This is the fourth boot!"
     systemd-notify --status="Fourth Boot"
 
+    test "$(busctl -j get-property org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager SoftRebootsCount | jq -r '.data')" -eq 3
+
     rm /run/testsuite82.touch3
     mount
     rmdir /original-root /run/nextroot
@@ -52,6 +54,8 @@ elif [ -f /run/testsuite82.touch2 ]; then
     echo "This is the third boot!"
     systemd-notify --status="Third Boot"
 
+    test "$(busctl -j get-property org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager SoftRebootsCount | jq -r '.data')" -eq 2
+
     rm /run/testsuite82.touch2
 
     # Check that the fdstore entry still exists
@@ -95,6 +99,8 @@ elif [ -f /run/testsuite82.touch ]; then
     echo "This is the second boot!"
     systemd-notify --status="Second Boot"
 
+    test "$(busctl -j get-property org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager SoftRebootsCount | jq -r '.data')" -eq 1
+
     # Clean up what we created earlier
     rm /run/testsuite82.touch
 
@@ -140,6 +146,11 @@ elif [ -f /run/testsuite82.touch ]; then
     # Restart the unit that is not supposed to survive
     systemd-run --collect --service-type=exec --unit=testsuite-82-nosurvive.service sleep infinity
 
+    # Now ensure there are no naming clashes and a bunch of transient units all succeed
+    for _ in $(seq 1 25); do
+        systemd-run --wait true
+    done
+
     # Now issue the soft reboot. We should be right back soon. Given /run/nextroot exists, we should
     # automatically do a softreboot instead of normal reboot.
     touch /run/testsuite82.touch2
@@ -151,6 +162,8 @@ else
     # This is the first boot
     systemd-notify --status="First Boot"
 
+    test "$(busctl -j get-property org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager SoftRebootsCount | jq -r '.data')" -eq 0
+
     # Let's upload an fd to the fdstore, so that we can verify fdstore passing works correctly
     T="/dev/shm/fdstore.$RANDOM"
     echo "wuffwuff" >"$T"
@@ -219,6 +232,14 @@ EOF
         systemd-inhibit --what=shutdown --who=test --why=test --mode=delay \
             sleep infinity
 
+    # Enqueue a bunch of failing units to try and trigger the transient name clash that happens due to D-Bus
+    # being restarted and the "unique" bus IDs not being unique across restarts
+    for _ in $(seq 1 25); do
+        # Use --wait to ensure we connect to the system bus instead of the private bus (otherwise a UUID is
+        # used instead of the bus ID)
+        systemd-run --wait false || true
+    done
+
     # Now issue the soft reboot. We should be right back soon.
     touch /run/testsuite82.touch
     systemctl --no-block --check-inhibitors=yes soft-reboot
index 033379ec7a446c10d07b64895fbcb91bdfc9fd34..6d1a6a3e3198e504d44b62d98d7ae28da8e3fed4 100644 (file)
@@ -8,3 +8,6 @@
 # See tmpfiles.d(5) for details
 
 L {{SSHCONFDIR}}/20-systemd-ssh-proxy.conf - - - - {{LIBEXECDIR}}/ssh_config.d/20-systemd-ssh-proxy.conf
+{% if CREATE_SSHDPRIVSEPDIR %}
+d {{SSHDPRIVSEPDIR}} 0755
+{% endif %}
index c4d4b3e1ec0336cbcc306970261244d60221abfc..d4a87df3555f7927b9624de65381d8299449a6ed 100644 (file)
@@ -3,7 +3,12 @@
 
 import os
 import sys
-import lxml.etree as tree
+
+try:
+    import lxml.etree as tree
+except ImportError as e:
+    print(str(e), file=sys.stderr)
+    sys.exit(77)
 
 _parser = tree.XMLParser(resolve_entities=False)
 tree.set_default_parser(_parser)
index 6371d493310270d0aa7594d04eb39faaf3817c88..2cbcf6ca44641c905cb2380739526b279a61ce01 100755 (executable)
@@ -3,13 +3,7 @@
 set -eu
 
 tag="$(git describe --abbrev=0 --match 'v[0-9][0-9][0-9]')"
-(
-    # authors
-    git log --pretty=tformat:%aN -s "${tag}.."
-    # Co-authors (drop empty line and mail addresses)
-    git log --pretty='tformat:%(trailers:key=Co-authored-by,valueonly)' -s "${tag}.." | sed -e '/^[[:space:]]*$/ d' | sed -e 's/ <.*@.*>$//'
-) |
-    grep -v noreply@weblate.org |
-    sed 's/ / /g; s/--/-/g; s/.*/\0,/' |
-    sort -u | tr '\n' ' ' | sed -e "s/^/Contributions from: /g" -e "s/,\s*$/\n/g" | fold -w 72 -s |
+git shortlog -s --group=author --group=trailer:Co-authored-by "${tag}.." |
+    sed -e 's/^[[:space:]]*[0-9]*[[:space:]]*//; /Weblate/ d; /dependabot\[bot\]/ d; s/ / /g; s/--/-/g; s/.*/\0,/' |
+    tr '\n' ' ' | sed -e "s/^/Contributions from: /g" -e "s/,\s*$/\n/g" | fold -w 72 -s |
     sed -e "s/^/        /g" -e "s/\s*$//g"
similarity index 50%
rename from mkosi.images/base/mkosi.conf.d/10-debian-ubuntu/initrd/mkosi.postinst
rename to tools/git-post-rewrite-hook.sh
index 640cdb1e32ad92dcf6a3e0359596353b4629d67d..78feb9dbbaec9b3ab963632077b66cb20dfae4a8 100755 (executable)
@@ -1,5 +1,4 @@
-#!/bin/bash
+#!/bin/sh
 # SPDX-License-Identifier: LGPL-2.1-or-later
-set -e
 
-mkosi-install systemd udev
+exec git submodule update
index 4b49ab9c45f9d48547b93d3827038809b0550bfe..a53f1790c17a7ae2d2a233a3d351abbc732d7762 100755 (executable)
@@ -10,10 +10,19 @@ if [ -e .git ]; then
     git config push.recurseSubmodules no
 fi
 
-if [ ! -f .git/hooks/pre-commit.sample ] || [ -f .git/hooks/pre-commit ]; then
-    exit 2 # not needed
+ret=2
+
+if [ -f .git/hooks/pre-commit.sample ] && [ ! -f .git/hooks/pre-commit ]; then
+    cp -p .git/hooks/pre-commit.sample .git/hooks/pre-commit
+    chmod +x .git/hooks/pre-commit
+    echo 'Activated pre-commit hook'
+    ret=0
+fi
+
+if [ ! -f .git/hooks/post-rewrite ]; then
+    cp -p tools/git-post-rewrite-hook.sh .git/hooks/post-rewrite
+    echo 'Activated post-rewrite hook'
+    ret=0
 fi
 
-cp -p .git/hooks/pre-commit.sample .git/hooks/pre-commit
-chmod +x .git/hooks/pre-commit
-echo 'Activated pre-commit hook'
+exit $ret
index 6fecbea0a1b522a04cddc54ef40d94756cf921ea..5b2410fe9321fb37747712420e63b4a0cf354bf8 100644 (file)
@@ -312,6 +312,11 @@ units = [
         { 'file' : 'systemd-growfs-root.service.in' },
         { 'file' : 'systemd-growfs@.service.in' },
         { 'file' : 'systemd-halt.service' },
+        {
+          'file' : 'systemd-hibernate-clear.service.in',
+          'conditions' : ['ENABLE_HIBERNATE', 'ENABLE_EFI'],
+          'symlinks' : ['sysinit.target.wants/'],
+        },
         {
           'file' : 'systemd-hibernate-resume.service.in',
           'conditions' : ['ENABLE_HIBERNATE'],
@@ -683,6 +688,7 @@ units = [
           'conditions' : ['ENABLE_TMPFILES'],
           'symlinks' : ['sysinit.target.wants/'],
         },
+        { 'file' : 'systemd-udev-load-credentials.service' },
         { 'file' : 'systemd-udev-settle.service' },
         {
           'file' : 'systemd-udev-trigger.service',
@@ -727,6 +733,22 @@ units = [
           'file' : 'systemd-userdbd.socket',
           'conditions' : ['ENABLE_USERDB'],
         },
+        {
+          'file' : 'systemd-mountfsd.service.in',
+          'conditions' : ['ENABLE_MOUNTFSD'],
+        },
+        {
+          'file' : 'systemd-mountfsd.socket',
+          'conditions' : ['ENABLE_MOUNTFSD'],
+        },
+        {
+          'file' : 'systemd-nsresourced.service.in',
+          'conditions' : ['ENABLE_NSRESOURCED'],
+        },
+        {
+          'file' : 'systemd-nsresourced.socket',
+          'conditions' : ['ENABLE_NSRESOURCED'],
+        },
         {
           'file' : 'systemd-vconsole-setup.service.in',
           'conditions' : ['ENABLE_VCONSOLE'],
index eaadd0e55446387aa36ec444c6f6df5481f20040..2e17cb9c8e8b9e19686855faafbdc68b64aeee20 100644 (file)
@@ -12,8 +12,6 @@ Description=Check if Any System Units Failed
 Documentation=man:systemd-boot-check-no-failures.service(8)
 After=default.target graphical.target multi-user.target
 Before=boot-complete.target
-Conflicts=shutdown.target
-Before=shutdown.target
 
 [Service]
 Type=oneshot
diff --git a/units/systemd-hibernate-clear.service.in b/units/systemd-hibernate-clear.service.in
new file mode 100644 (file)
index 0000000..2e8587e
--- /dev/null
@@ -0,0 +1,24 @@
+#  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=Clear Stale Hibernate Storage Info
+Documentation=man:systemd-hibernate-clear.service(8)
+
+ConditionPathExists=/sys/firmware/efi/efivars/HibernateLocation-8cf2644b-4b0b-428f-9387-6d876050dc67
+ConditionPathExists=!/etc/initrd-release
+
+DefaultDependencies=no
+Before=sysinit.target shutdown.target
+Conflicts=shutdown.target
+
+[Service]
+Type=oneshot
+RemainAfterExit=yes
+ExecStart={{LIBEXECDIR}}/systemd-hibernate-resume --clear
diff --git a/units/systemd-mountfsd.service.in b/units/systemd-mountfsd.service.in
new file mode 100644 (file)
index 0000000..20a9b42
--- /dev/null
@@ -0,0 +1,46 @@
+#  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=DDI File System Mounter
+Documentation=man:systemd-mountfsd.service(8)
+Requires=systemd-mountfsd.socket
+After=systemd-mountfsd.socket
+Conflicts=shutdown.target
+Before=sysinit.target shutdown.target
+DefaultDependencies=no
+
+[Service]
+#CapabilityBoundingSet=CAP_DAC_READ_SEARCH CAP_SYS_RESOURCE CAP_BPF CAP_PERFMON CAP_SETGID CAP_SETUID
+ExecStart={{LIBEXECDIR}}/systemd-mountfsd
+IPAddressDeny=any
+LimitNOFILE={{HIGH_RLIMIT_NOFILE}}
+LockPersonality=yes
+MemoryDenyWriteExecute=yes
+NoNewPrivileges=yes
+ProtectProc=invisible
+ProtectControlGroups=yes
+ProtectHome=yes
+ProtectHostname=yes
+ProtectKernelLogs=yes
+ProtectKernelModules=yes
+ProtectSystem=strict
+RestrictAddressFamilies=AF_UNIX AF_NETLINK AF_INET AF_INET6
+RestrictRealtime=yes
+RestrictSUIDSGID=yes
+SystemCallArchitectures=native
+SystemCallErrorNumber=EPERM
+SystemCallFilter=@system-service @mount
+Type=notify
+NotifyAccess=all
+FileDescriptorStoreMax=4096
+{{SERVICE_WATCHDOG}}
+
+[Install]
+Also=systemd-mountfsd.socket
diff --git a/units/systemd-mountfsd.socket b/units/systemd-mountfsd.socket
new file mode 100644 (file)
index 0000000..cd88003
--- /dev/null
@@ -0,0 +1,22 @@
+#  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=DDI File System Mounter Socket
+Documentation=man:systemd-mountfsd.service(8)
+DefaultDependencies=no
+Conflicts=shutdown.target
+Before=sockets.target shutdown.target
+
+[Socket]
+ListenStream=/run/systemd/io.systemd.MountFileSystem
+SocketMode=0666
+
+[Install]
+WantedBy=sockets.target
index c151d362855b82324a425f7eef586b2cd1c75454..5db293654782eccdd5263f6639a0dcac60f3d8a0 100644 (file)
@@ -15,7 +15,6 @@ DefaultDependencies=no
 After=systemd-remount-fs.service systemd-networkd.service
 BindsTo=systemd-networkd.service
 Conflicts=shutdown.target
-RequiresMountsFor=/var/lib/systemd/network
 ConditionPathExists=!/etc/initrd-release
 
 [Service]
diff --git a/units/systemd-nsresourced.service.in b/units/systemd-nsresourced.service.in
new file mode 100644 (file)
index 0000000..3c92705
--- /dev/null
@@ -0,0 +1,47 @@
+#  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=Namespace Resource Manager
+Documentation=man:systemd-nsresourced.service(8)
+Requires=systemd-nsresourced.socket
+After=systemd-nsresourced.socket
+Conflicts=shutdown.target
+Before=sysinit.target shutdown.target
+DefaultDependencies=no
+
+[Service]
+CapabilityBoundingSet=CAP_DAC_READ_SEARCH CAP_SYS_RESOURCE CAP_BPF CAP_PERFMON CAP_SETGID CAP_SETUID CAP_SYS_ADMIN CAP_CHOWN CAP_FOWNER
+ExecStart={{LIBEXECDIR}}/systemd-nsresourced
+IPAddressDeny=any
+LimitNOFILE={{HIGH_RLIMIT_NOFILE}}
+LockPersonality=yes
+MemoryDenyWriteExecute=yes
+NoNewPrivileges=yes
+PrivateDevices=yes
+ProtectProc=invisible
+ProtectControlGroups=yes
+ProtectHome=yes
+ProtectHostname=yes
+ProtectKernelLogs=yes
+ProtectKernelModules=yes
+ProtectSystem=strict
+RestrictAddressFamilies=AF_UNIX AF_NETLINK
+RestrictRealtime=yes
+RestrictSUIDSGID=yes
+SystemCallArchitectures=native
+SystemCallErrorNumber=EPERM
+SystemCallFilter=@system-service bpf perf_event_open open_by_handle_at
+Type=notify
+NotifyAccess=all
+FileDescriptorStoreMax=4096
+{{SERVICE_WATCHDOG}}
+
+[Install]
+Also=systemd-nsresourced.socket
diff --git a/units/systemd-nsresourced.socket b/units/systemd-nsresourced.socket
new file mode 100644 (file)
index 0000000..2e3c8e9
--- /dev/null
@@ -0,0 +1,23 @@
+#  SPDX-License-Identifier: LGPL-2.1-or-later
+#
+#  This file is part of systemd.
+#
+#  systemd is free software; you can redistribute it and/or modify it
+#  under the terms of the GNU Lesser General Public License as published by
+#  the Free Software Foundation; either version 2.1 of the License, or
+#  (at your option) any later version.
+
+[Unit]
+Description=Namespace Resource Manager Socket
+Documentation=man:systemd-nsresourced.service(8)
+DefaultDependencies=no
+Conflicts=shutdown.target
+Before=sockets.target shutdown.target
+
+[Socket]
+ListenStream=/run/systemd/io.systemd.NamespaceResource
+Symlinks=/run/systemd/userdb/io.systemd.NamespaceResource
+SocketMode=0666
+
+[Install]
+WantedBy=sockets.target
index 5c9e6fb8b1ff3084b65a41a134bebf671deaa7d5..1f7e2a612a71f948015dafe5ffc9fcc6300c46ea 100644 (file)
@@ -21,7 +21,7 @@ ConditionDirectoryNotEmpty=|/sysusr/usr/local/lib/repart.d
 
 DefaultDependencies=no
 Wants=modprobe@loop.service modprobe@dm_mod.service
-After=initrd-usr-fs.target modprobe@loop.service modprobe@dm_mod.service
+After=initrd-usr-fs.target modprobe@loop.service modprobe@dm_mod.service systemd-tpm2-setup-early.service
 Before=initrd-root-fs.target
 Conflicts=shutdown.target initrd-switch-root.target
 Before=shutdown.target initrd-switch-root.target
diff --git a/units/systemd-udev-load-credentials.service b/units/systemd-udev-load-credentials.service
new file mode 100644 (file)
index 0000000..70f69dc
--- /dev/null
@@ -0,0 +1,29 @@
+#  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=Load udev Rules from Credentials
+Documentation=man:udevadm(8)
+Documentation=man:udev(7)
+Documentation=man:systemd.system-credentials(7)
+
+DefaultDependencies=no
+Before=systemd-udevd.service
+Conflicts=shutdown.target initrd-switch-root.target
+Before=shutdown.target initrd-switch-root.target
+
+[Service]
+Type=oneshot
+RemainAfterExit=yes
+ExecStart=udevadm control --load-credentials
+ImportCredential=udev.conf.*
+ImportCredential=udev.rules.*
+
+[Install]
+WantedBy=sysinit.target
index b59fdef9b01f71572b37b64a0ccd2e9f41ba206a..f4a44820880daaad11f98e1139e66c805860c250 100644 (file)
@@ -13,6 +13,7 @@ Documentation=man:systemd-udevd.service(8) man:udev(7)
 DefaultDependencies=no
 After=systemd-sysusers.service systemd-hwdb-update.service
 Before=sysinit.target
+Wants=systemd-udev-load-credentials.service
 ConditionPathIsReadWrite=/sys
 
 [Service]